#include "GameCore.h"
#include "GameEngine.h"

#include "../sleek/loader/texture_loader.h"

using namespace sleek;
using namespace device;
#define number 100
namespace interne
{
    void associate(gui::frame *i) noexcept;

    GameEngine::GameEngine(GameCore *mom) noexcept
        : screen(mom->getDevice()), core(mom)
    {
        smgr = new sleek::scene3d::engine(screen, mom->getDriver());
        rot = -10000;

        {
            // load some texture
            crate = mom->getLoader()->loadTexture("texture/item/crate.png");
            bundul = mom->getLoader()->loadTexture("texture/item/add.bmp");
            bundul->flipVertical();

            // create hardware link
            crate->createIdentifier(mom->getContext());
            bundul->setIdentifier(mom->getContext()->createTexture(bundul.get(), false));

            // update hardware link (send data to gpy)
            crate->getIdentifier()->update();
            bundul->getIdentifier()->update();
        }

        mat = new driver::material();
        mat->setMode(driver::rmd_polygon);
        mat->setShadeModel(driver::rsd_flat);
        mat->setFaceCulling(driver::rfc_off);
        mat->setMaterialRender(driver::rmt_solid | driver::rmt_lighting);
        mat->setTexture(0, crate->getIdentifier().get());
        mat->setTexture(1, bundul->getIdentifier().get());
        mat->setPointSize(2.f);

        // shader creation sample
        {
            auto shade = core->getContext()->createShader();

            // only need fragment shader
            shade->attacheShader(driver::shd_frag);

            // used to get information from material (like texture binding) by calback
            shade->setLinkToMaterial(mat);

            shade->setFragmentShader(
                "uniform sampler2D T0;\n"
                "uniform sampler2D T1;\n"
                "uniform float alpha;\n"
                "uniform float density;\n"
                "void main (void)\n"
                "{\n"
                "     vec4 texel0, texel1;\n"
                "     //----------------------------------------\n"
                "     texel0 = texture2D(T0, gl_TexCoord[0].st)*density;\n"
                "     texel1 = texture2D(T1, gl_TexCoord[1].st)*(1.0 - density);\n"
                "     //----------------------------------------\n"
                "     gl_FragColor = (texel0+texel1);\n"
                "     gl_FragColor *= gl_Color;\n"
                "     gl_FragColor.a = alpha;\n"
                "}\n",
                "main"
            );
            shade->setCallback([](driver::shader *i) noexcept
            {
                i->setTexture("T0", i->getLinkFromMaterial()->getTexture(0), 0);
                i->setTexture("T1", i->getLinkFromMaterial()->getTexture(1), 1);
                i->setVariable("alpha", 0.75f);
                i->setVariable("density", 0.25f);
            });
            shade->compileShader();

            // link
            mat->setShader(shade);
        }

        driver::Geometry geom;
        tmp = geom.createCube(math::vector3df(15), math::pixel(0,0,255,255));

        scene3d::Node *grid = new scene3d::real::Grid(smgr);
        smgr->addSceneNode(grid);

        for(int x = -100; x<150; x += 50)
        {
            for(int y = -300; y<350; y += 50)
            {
                for(int z = -100;z<150; z += 50)
                {
                    scene3d::Node *cube = new scene3d::real::Natif(smgr);
                        cube->setPosition(math::vector3df(x,y,z));
                        cube->setMaterial(mat);
                        cube->setMesh(tmp);
                    smgr->addSceneNode(cube);
                }
            }
        }
        static_crate = smgr->getNumberNode();

        smgr->getCamera()->setRotation(math::vector3df(0, 1, 0));
        smgr->getCamera()->setTarget(math::vector3df(0, 0, 0));

        light_test();
    }

    GameEngine::~GameEngine() noexcept
    {
        smgr->clear();
    }

    bool GameEngine::manage(sleek::device::input *a) noexcept
    {
        if(a->type == EVENT_KEY_UP)
        {
            //! engine test
            if(a->key[KEY_ESCAPE]) screen->setFullScreen(!screen->getInfo().fullscreen);
//            if(a->key[KEY_F1]) texture::WriteTextureToBmp(screen->createScreenshot(),"texture/screenshot.bmp");
            //! thread support, disable and enable
//            if(a->key[KEY_KEY_A]) smgr->enableThread(engine::etf_all);
//            if(a->key[KEY_KEY_Z]) smgr->disableThread(engine::etf_all);
            //! wireframe view, disable and enable
//            if(a->key[KEY_KEY_W]) glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
//            if(a->key[KEY_KEY_X]) glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            //! vertex buffer object, disable and enable
            if(a->key[KEY_KEY_Q])
            {
                core->getContext()->createVBO(tmp, driver::VBO_VERTEX_BUFFER);
                tmp->getIdentifier(driver::VBO_VERTEX_BUFFER)->update();
                core->getContext()->createVBO(tmp, driver::VBO_INDEX_BUFFER);
                tmp->getIdentifier(driver::VBO_INDEX_BUFFER)->update();
            }
            if(a->key[KEY_KEY_S])
            {
                for(int i = 0; i<driver::VBO_COUNT; ++i)
                    tmp->setIdentifier(driver::VBO_BUFFER_TYPE(i), nullptr);
            }
            //! visible physique draw, enable or disable
//            if(a->key[KEY_KEY_D]) smgr->enableDebugDraw(!smgr->isDebugDraw());
            //! push or delete "crate" from world
            if(a->key[KEY_SPACE]) push_crate(1);
            if(a->key[KEY_RETURN]) delete_crate(1);
            return true;
        }
    }

    void GameEngine::push_crate(int n) noexcept
    {
        for(int i = 0; i<n; ++i)
        {
            float scale = 0.05f;
            scene3d::Node *tmp = new scene3d::billboard::Billboard(smgr, bundul.get());
                tmp->setPosition(math::vector3df(rand()%300-150,rand()%150+50, 0));
                tmp->setScale(math::vector3df(scale));
                tmp->setId(3);
            smgr->addSceneNode(tmp);
        }
    }

    void GameEngine::delete_crate(int n) noexcept
    {
        for(int i = 0; i<smgr->getNumberNode(); ++i)
        {
            if(smgr->getNode(i)->getId() == 3)
                smgr->removeNode(smgr->getNode(i));
        }
    }

    void GameEngine::render() noexcept
    {
        tm.update();
        rot = tm.getTimeMsec()/50.0f;
        smgr->getCamera()->setPosition(math::vector3df(sin(rot/100)*200, 250, cos(rot/100)*200));
        smgr->getCamera()->updateProjection();
        smgr->render();
    }
}