// http://stackoverflow.com/questions/6005076/building-glew-on-windows-with-mingw

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <stdexcept>

GLuint LoadTexture(char *filename,int *textw,int *texth) {
        SDL_Surface *surface;
        GLuint textureid;
        int mode;
        surface = IMG_Load(filename);
        // Or if you don't use SDL_image you can use SDL_LoadBMP here instead:
        // surface = SDL_LoadBMP(filename);

        // could not load filename
        if (!surface) {
                return 0;
        }

        // work out what format to tell glTexImage2D to use...
        if (surface->format->BytesPerPixel == 3) { // RGB 24bit
                mode = GL_RGB;

        } else if (surface->format->BytesPerPixel == 4) { // RGBA 32bit
                mode = GL_RGBA;
        } else {
                SDL_FreeSurface(surface);
                return 0;
        }

        *textw=surface->w;
        *texth=surface->h;
        // create one texture name
        glGenTextures(1, &textureid);
        // tell opengl to use the generated texture name
        glBindTexture(GL_TEXTURE_2D, textureid);
        // this reads from the sdl surface and puts it into an opengl texture
        glTexImage2D(GL_TEXTURE_2D, 0, mode, surface->w, surface->h, 0, mode, GL_UNSIGNED_BYTE, surface->pixels);
        // these affect how this texture is drawn later on...glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        // clean up
        SDL_FreeSurface(surface);
        return textureid;

}

void DrawTexture(int x, int y, GLuint textureid,int textw,int texth) {
        //printf("x: %d\ty: %d\tw: %d\th: %d\n", x, y, textw, texth);
        //int textw,texth;
        // tell opengl to use the generated texture name
        glBindTexture(GL_TEXTURE_2D, textureid);
        glEnable(GL_TEXTURE_2D);
        // make a rectangle
        glBegin(GL_QUADS);
            glTexCoord2d(0.0,0.0); glVertex2f(-1, -1);
            glTexCoord2d(1.0,0.0); glVertex2f(1, -1);
            glTexCoord2d(1.0,1.0); glVertex2f(1, 1);
            glTexCoord2d(0.0,1.0); glVertex2f(-1, 1);
        glEnd();
        glDisable(GL_TEXTURE_2D);
}

GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){

	// Create the shaders
	GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
	GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

	// Read the Vertex Shader code from the file
	std::string VertexShaderCode;
	std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
	if(VertexShaderStream.is_open()){
		std::string Line = "";
		while(getline(VertexShaderStream, Line))
			VertexShaderCode += "\n" + Line;
		VertexShaderStream.close();
	}else{
		printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertex_file_path);
		getchar();
		return 0;
	}

	// Read the Fragment Shader code from the file
	std::string FragmentShaderCode;
	std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
	if(FragmentShaderStream.is_open()){
		std::string Line = "";
		while(getline(FragmentShaderStream, Line))
			FragmentShaderCode += "\n" + Line;
		FragmentShaderStream.close();
	}

	GLint Result = GL_FALSE;
	int InfoLogLength;


	// Compile Vertex Shader
	printf("Compiling shader : %s\n", vertex_file_path);
	char const * VertexSourcePointer = VertexShaderCode.c_str();
	glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
	glCompileShader(VertexShaderID);

	// Check Vertex Shader
	glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
	glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
	if ( InfoLogLength > 0 ){
		std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
		glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
		printf("%s\n", &VertexShaderErrorMessage[0]);
	}



	// Compile Fragment Shader
	printf("Compiling shader : %s\n", fragment_file_path);
	char const * FragmentSourcePointer = FragmentShaderCode.c_str();
	glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
	glCompileShader(FragmentShaderID);

	// Check Fragment Shader
	glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
	glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
	if ( InfoLogLength > 0 ){
		std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
		glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
		printf("%s\n", &FragmentShaderErrorMessage[0]);
	}



	// Link the program
	printf("Linking program\n");
	GLuint ProgramID = glCreateProgram();
	glAttachShader(ProgramID, VertexShaderID);
	glAttachShader(ProgramID, FragmentShaderID);
	glLinkProgram(ProgramID);

	// Check the program
	glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
	glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
	if ( InfoLogLength > 0 ){
		std::vector<char> ProgramErrorMessage(InfoLogLength+1);
		glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
		printf("%s\n", &ProgramErrorMessage[0]);
	}


	glDetachShader(ProgramID, VertexShaderID);
	glDetachShader(ProgramID, FragmentShaderID);

	glDeleteShader(VertexShaderID);
	glDeleteShader(FragmentShaderID);

	return ProgramID;
}



int main(int argc, char* args[]) {
    GLFWwindow* window;
    GLuint tex_jpe;
    int textw, texth;
    int timer = 0;


    /* 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);
    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)
    {
        std::cout << "OpenGL Error: " << error << std::endl;
    }
    GLenum glewinit = glewInit();
    if (glewinit != GLEW_OK) {
        std::cout << "Glew not okay! " << glewinit;
        return 1;
    }

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


    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);
    // 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);







    tex_jpe=LoadTexture("../img/tests/test.png",&textw,&texth);

    GLuint programID = LoadShaders("trimap.vertexshader", "trimap.fragmentshader");
    GLint textureLocation = glGetUniformLocation(programID, "myTextureSampler");
    GLint widthLocation = glGetUniformLocation(programID, "pixWidth");
    GLint heigthLocation = glGetUniformLocation(programID, "pixHeight");
    printf("Shader program ID: %d\n", programID);
    printf("textureLocation: %d\n", textureLocation);
    printf("widthLocation: %d\n", widthLocation);
    printf("heigthLocation: %d\n", heigthLocation);
    printf("Width: %f\n", 1.0f/textw);
    printf("Height: %f\n", 1.0f/texth);



    // INIT END
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window)) {
        timer += 1;
        if (timer==800)
            timer = 0;
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(programID);
        glUniform1f(widthLocation, 1.0f/textw);
        glUniform1f(heigthLocation, 1.0f/texth);
        // 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;
}
