#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <SDL2/SDL.h>
#include <fstream>
#include <stdexcept>
#include <dirent.h>
#include "loaders.h"

int main(int argc, char* args[]) {
    GLFWwindow* window;
    GLuint texFG, texBG;
    int textw, texth;
    int textw2, texth2;


    /* Initialize the library */

    /* GLFW */
    if (!glfwInit()) {
        printf("GLFW Error");
        return 1;
    }
    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    glfwSetWindowSizeLimits(window, 640, 480, 640, 480);
    if (!window) {
        glfwTerminate();
        return 1;
    }
    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    /* GLEW */
    glewExperimental = GL_FALSE;
    GLenum error = glGetError();
    if (error != GL_NO_ERROR)
    {
        printf("OpenGL Error: %d",error);
    }
    GLenum glewinit = glewInit();
    if (glewinit != GLEW_OK) {
        printf("Glew not okay! %d",glewinit);
        return 1;
    }

    /* SDL */
    if (InitLoaders() < 0) {
        printf("SDL Error");
        return 1;
    }
    // INIT

    // An array of 3 vectors which represents 3 vertices
    static const GLfloat g_vertex_buffer_data[] = {
       -1.0f, -1.0f, 0.0f,  // (bottom - left )  TRIANGLE 1
        1.0f, -1.0f, 0.0f,  // (bottom - right)
       -1.0f,  1.0f, 0.0f,  // (top    - left )
        1.0f,  1.0f, 0.0f,  // (top    - right)  TRIANGLE 2
        1.0f, -1.0f, 0.0f,  // (bottom - right)
       -1.0f,  1.0f, 0.0f,  // (top    - left )
    };
    // One color for each vertex. They were generated randomly.
    static const GLfloat g_uv_buffer_data[] = {
        0.0f, 1.0f-0.0f,         // (bottom - left )  TRIANGLE 1
        1.0f, 1.0f-0.0f,         // (bottom - right)
        0.0f, 1.0f-1.0f,         // (top    - left )
        1.0f, 1.0f-1.0f,         // (top    - right)  TRIANGLE 2
        1.0f, 1.0f-0.0f,         // (bottom - right)
        0.0f, 1.0f-1.0f,         // (top    - left )
    };


    // Identify our vertex buffer
    GLuint vertexbuffer;
    glGenBuffers(1, &vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    // Identify our color buffer
    GLuint colorbuffer;
    glGenBuffers(1, &colorbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);

    // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
    GLuint FramebufferName;
    glGenFramebuffers(1, &FramebufferName);
    glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

    int lenTexFG = 10;
    GLint texFGs[lenTexFG];
    char buffer[50];


    texBG = LoadTexture("../img/backgrounds/sky.jpg",&textw2,&texth2);
    for (int i=0; i<lenTexFG; i++) {
        snprintf(buffer, sizeof(buffer), "../img/foregrounds/surfing/%d.jpg", i);
        texFGs[i] = LoadTexture(buffer,&textw,&texth);
    }
    texFG = LoadTexture("../img/foregrounds/surfing/0.jpg",&textw,&texth);
    glfwSetWindowSize(window, textw, texth);
    glViewport(0, 0, textw, texth);
    glfwSetWindowSizeLimits(window, textw, texth, textw, texth);



    // The texture we're going to render to
    GLuint renderedTexture;
    glGenTextures(1, &renderedTexture);
    glBindTexture(GL_TEXTURE_2D, renderedTexture);

    glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, textw, texth, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // Set "renderedTexture" as our colour attachement #2
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, renderedTexture, 0);
    // Set the list of draw buffers.
    GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT2};
    glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers

    // Always check that our framebuffer is ok
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        return false;


    GLuint programID = LoadShaders("trimap.vertexshader", "trimap.fragmentshader");
    GLuint programRefineID = LoadShaders("refinement.vertexshader", "refinement.fragmentshader");
    GLint textureLoc1 = glGetUniformLocation(programID, "myTextureSampler");
    GLint widthLoc = glGetUniformLocation(programID, "pixWidth");
    GLint heigthLoc = glGetUniformLocation(programID, "pixHeight");


    GLint textureLocTri = glGetUniformLocation(programRefineID, "trimap");
    GLint textureLocFore = glGetUniformLocation(programRefineID, "foreground");
    GLint textureLocBack = glGetUniformLocation(programRefineID, "background");

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texBG);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, renderedTexture);


    printf("Shader program ID: %d\n", programID);
    printf("textureLoc1: %d\n", textureLoc1);
    printf("textureLocTri: %d\n", textureLocTri);
    //printf("textureLocFore: %d\n", textureLocFore);
    printf("widthLoc: %d\n", widthLoc);
    printf("heigthLoc: %d\n", heigthLoc);
    printf("Width: %f\n", 1.0f/textw);
    printf("Height: %f\n", 1.0f/texth);


    int loopNumber = -1;
     double lastTime = glfwGetTime();
 int nbFrames = 0;

    // INIT END
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window)) {
        /* Render here */
        loopNumber++;
        if (loopNumber >= lenTexFG) {
            loopNumber = 0;
        }
        // Measure speed
     double currentTime = glfwGetTime();
     nbFrames++;
     if ( currentTime - lastTime >= 1.0 ){ // If last prinf() was more than 1 sec ago
         // printf and reset timer
         printf("%f ms/frame\n", 1000.0/double(nbFrames));
         nbFrames = 0;
         lastTime += 1.0;
     }

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texFGs[loopNumber]);

        // Render to our framebuffer
        glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
        //glBindFramebuffer(GL_FRAMEBUFFER, 0);
        //glViewport(0,0,textw,texth); // Render on the whole framebuffer, complete from the lower left corner to the upper right
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(programID);
        glUniform1f(widthLoc, 1.0f/textw);
        glUniform1f(heigthLoc, 1.0f/texth);
        glUniform1i(textureLoc1, 0);

        // 1rst attribute buffer : vertices
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glVertexAttribPointer(
           0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
           3,                  // size
           GL_FLOAT,           // type
           GL_FALSE,           // normalized?
           0,                  // stride
           (void*)0            // array buffer offset
        );
        // 2nd attribute buffer : colors
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
        glVertexAttribPointer(
            1,                 // attribute. No particular reason for 1, but must match the layout in the shader.
            2,                 // size
            GL_FLOAT,          // type
            GL_FALSE,          // normalized?
            0,                 // stride
            (void*)0           // array buffer offset
        );


        // Draw the triangle !
        glDrawArrays(GL_TRIANGLES, 0, 2*3); // 2*3 indices starting at 0 -> 2 triangles -> 1 square
        glDisableVertexAttribArray(0);


        // Render to the screen
        glUseProgram(0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0,0,textw, texth); // Render on the whole framebuffer, complete from the lower left corner to the upper right


        glUseProgram(programRefineID);
        glUniform1i(textureLocTri, 2);
        glUniform1i(textureLocBack, 1);
        glUniform1i(textureLocFore, 0);


        // 1rst attribute buffer : vertices
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glVertexAttribPointer(
           0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
           3,                  // size
           GL_FLOAT,           // type
           GL_FALSE,           // normalized?
           0,                  // stride
           (void*)0            // array buffer offset
        );
        // 2nd attribute buffer : colors
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
        glVertexAttribPointer(
            1,                 // attribute. No particular reason for 1, but must match the layout in the shader.
            2,                 // size
            GL_FLOAT,          // type
            GL_FALSE,          // normalized?
            0,                 // stride
            (void*)0           // array buffer offset
        );


        // Draw the triangle !
        glDrawArrays(GL_TRIANGLES, 0, 2*3); // 2*3 indices starting at 0 -> 2 triangles -> 1 square
        glDisableVertexAttribArray(0);



        // RENDER END
        /* Swap front and back buffers */
        glfwSwapBuffers(window);
        /* Poll for and process events */
        glfwPollEvents();
    }
    glfwTerminate();
    return 0;
}
