#include <ork/render/FrameBuffer.h>
#include <ork/scenegraph/SceneManager.h>
#include <ork/ui/GlutWindow.h>
#include <pmath.h>
#include <stbi/stb_image.h>
#include <iostream>
#include <fstream>
const unsigned char * loadfile(const std::string &file, int &size)
{
    std::ifstream fs(file.c_str(), std::ios::binary);
    fs.seekg(0, std::ios::end);
    size = fs.tellg();
    char * data = new char[size + 1];
    fs.seekg(0);
    fs.read(data, size);
    fs.close();
    data[size] = 0;
    return (unsigned char *)data;
}
struct P3_UV
{
    float _x, _y, _z;
    float _u, _v;
    P3_UV()
    {
    }
    P3_UV(float x, float y, float z, float u, float v)
        :_x(x),_y(y),_z(z),_u(u),_v(v)
    {
    }
};
class TestWindow : public ork::GlutWindow
{
public:
    TestWindow()
        :ork::GlutWindow(ork::Window::Parameters().name("ProlandTerrain").size(1024, 800))
        ,_dist(2.0)
    {
        _mesh = new ork::Mesh<P3_UV, unsigned int>(ork::TRIANGLES, ork::GPU_STATIC);
        _mesh->addAttributeType(0, 3, ork::A32F, false);
        _mesh->addAttributeType(1, 2, ork::A32F, true);
        _mesh->addVertex(P3_UV(-1, -1, 0, 0, 1));
        _mesh->addVertex(P3_UV(1, -1, 0, 1, 1));
        _mesh->addVertex(P3_UV(1, 1, 0, 1, 0));
        _mesh->addVertex(P3_UV(-1, 1, 0, 0, 0));
        _mesh->addIndice(0);
        _mesh->addIndice(1);
        _mesh->addIndice(2);
        _mesh->addIndice(0);
        _mesh->addIndice(2);
        _mesh->addIndice(3);
        int w;
        int h;
        int channels;
        int size;
        const unsigned char * data = loadfile("D:/1.jpg", size);
        const unsigned char * logo = stbi_load_from_memory(data, size, &w, &h, &channels, 0);
        ork::ptr<ork::Texture2D> texture2D = new ork::Texture2D(w, h, ork::RGB32F, ork::TextureFormat::RGB, ork::ORK_UNSIGNED_BYTE, ork::Texture::Parameters().mag(ork::NEAREST), ork::Buffer::Parameters(), ork::CPUBuffer(logo));
        ork::ptr<ork::Module> meshModule = new ork::Module(330, "\
                                                                uniform mat4 localToScreen; \n\
                                                                layout(location = 0) in vec3 vertex; \n\
                                                                layout(location = 1) in vec2 uv; \n\
                                                                out vec2 fuv; \n\
                                                                void main() { \n\
                                                                fuv = uv; \n\
                                                                gl_Position = localToScreen * vec4(vertex, 1.0); \n\
                                                                } \n\
                                                                ", "\
                                                                uniform sampler2D sampler; \n\
                                                                layout(location = 0) out vec4 data; \n\
                                                                in vec2 fuv; \n\
                                                                void main() { \n\
                                                                data = vec4(texture(sampler, fuv).rgb, 1); \n\
                                                                } \n\
                                                                ");
        _meshProgram = new ork::Program(meshModule);
        _localToScreen = _meshProgram->getUniformMatrix4f("localToScreen");
        _meshProgram->getUniformSampler("sampler")->set(texture2D);
        _frameBuffer = ork::FrameBuffer::getDefault();
        _frameBuffer->setClearColor(ork::vec4f(0.0, 0.0, 1.0, 1.0));
        _frameBuffer->setDepthTest(true, ork::LESS);
        
    }
    virtual void redisplay(double t, double dt)
    {
        _frameBuffer->clear(true, false, true);
        static float i = 0.0;
        ork::mat4f cameraToWorld = ork::mat4f::rotatey(i);
        i += 0.01;
        cameraToWorld = cameraToWorld * ork::mat4f::translate(ork::vec3f(0.0, 0.0, _dist));
        ork::mat4f worldToCamera = cameraToWorld.inverse();
        ork::vec4<int> vp = _frameBuffer->getViewport();
        float width = float(vp.z);
        float height = float(vp.w);
        ork::mat4f cameraToScreen = ork::mat4f::perspectiveProjection(degrees(45.0), width/height, 0.1, 100000.0);
        _localToScreen->setMatrix(cameraToScreen * worldToCamera);
        _frameBuffer->draw(_meshProgram, *_mesh);
        ork::GlutWindow::redisplay(t, dt);
    }
    virtual void reshape(int x, int y)
    {
        _frameBuffer->setViewport(ork::vec4i(0, 0, x, y));
        ork::GlutWindow::reshape(x, y);
        idle(false);
    }
public:
    static ork::static_ptr<ork::Window>            _app;
    ork::ptr<ork::FrameBuffer>                     _frameBuffer;
    ork::ptr<ork::Mesh<P3_UV, unsigned int> >      _mesh;
    ork::ptr<ork::Program>                         _meshProgram;
    ork::ptr<ork::UniformMatrix4f>                 _localToScreen;
    float                                          _dist;
};
ork::static_ptr<ork::Window> TestWindow::_app;
int main()
{
    atexit(ork::Object::exit);
    TestWindow::_app = new TestWindow;
    TestWindow::_app->start();
    return EXIT_SUCCESS;
}