#include <iostream>

#include "App.hh"

#ifdef _WIN32
    #pragma comment(linker, "/subsystem:windows")
    #define VK_USE_PLATFORM_WIN32_KHR
    #define PLATFORM_SURFACE_EXTENSION_NAME VK_KHR_WIN32_SURFACE_EXTENSION_NAME
#endif


//TODO (Eero): https://github.com/theandrew168/sdl2-opengl-demo/blob/master/src/main.c
namespace App
{    
    App::App() : m_ReturnCode(0), m_WindowWidth(800), m_WindowHeight(600), m_Quit(false), m_VSync(false)
    {
        float vertices[] = {
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            0.0f, 0.5f, 0.0f
        };

    //         printf("OpenGL Vendor:   %s\n", glGetString(GL_VENDOR));
    // printf("OpenGL Renderer: %s\n", glGetString(GL_RENDERER));
    // printf("OpenGL Version:  %s\n", glGetString(GL_VERSION));
    // printf("GLSL Version:    %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
        // Vertex Shader Part
        unsigned int VBO;
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "}\0";

        unsigned int vertexShader;
        vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);

        int  success;
        char infoLog[512];
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
        }

        // Fragment Shader Part
        unsigned int fragmentShader;
        fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        
        
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if(!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::FRAGMET::COMPILATION_FAILED\n" << infoLog << std::endl;
        }

        unsigned int shaderProgram;
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);

        glUseProgram(shaderProgram);

        // Oh yeah, and don't forget to delete the shader objects once we've linked them into the program object; we no longer need them anymore: 
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);  

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0); 

        

        // // 0. copy our vertices array in a buffer for OpenGL to use
        // glBindBuffer(GL_ARRAY_BUFFER, VBO);
        // glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        // // 1. then set the vertex attributes pointers
        // glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        // glEnableVertexAttribArray(0);  
        // // 2. use our shader program when we want to render an object
        // glUseProgram(shaderProgram);
        // // 3. now draw the object 
        // someOpenGLFunctionThatDrawsOurTriangle();  

        std::cout << "Eternity Engine" << std::endl;
        m_Logger = std::make_shared<Logger>();
        m_Controller = std::make_shared<Controller>();

        //Use OpenGL 3.1 core
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
        
        if(SDL_Init(SDL_INIT_VIDEO) < 0)
        {
            std::cout << "SDL could not be initialized!" << std::endl
                    << "SDL_Error: " << SDL_GetError() << std::endl;
            m_ReturnCode=-1;
            return;
        }
        
        // SDL_Vulkan_LoadLibrary(nullptr);
        m_Window = SDL_CreateWindow("App",
                                    SDL_WINDOWPOS_UNDEFINED,
                                    SDL_WINDOWPOS_UNDEFINED,
                                    m_WindowWidth, m_WindowHeight,
                                    SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
        if(!m_Window)
        {
            std::cout << "Window could not be created!" << std::endl
                    << "SDL_Error: " << SDL_GetError() << std::endl;
            m_ReturnCode=-1;
            return;
        }
        
        m_glContext = SDL_GL_CreateContext(m_Window);
        if( !m_glContext )
        {
            // Display error message
            std::cout << "OpenGL context could not be created!" << std::endl
                    << "SDL_Error: " << SDL_GetError() << std::endl;
            m_ReturnCode=-1;
            return;
        }
        else
        {
            // Event loop exit flag
            m_Quit = false;
            Uint32 command = 0;
            // Event loop
            while(!m_Quit)
            {
                SDL_Event e;

                // Wait indefinitely for the next available event
                if(m_VSync)
                    SDL_PollEvent(&e); // Use this for vsync
                else
                    SDL_WaitEvent(&e);

                // User requests quit
                if(e.type == SDL_QUIT)
                {
                    m_Quit = true;
                }
                
                command = m_Controller->HandleEvent(&e);
                handleCommand(command);

                // Set background color as cornflower blue
                glClearColor(0.39f, 0.58f, 0.93f, 1.f);
                // Clear color buffer
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                glUseProgram(shaderProgram);
                glBindVertexArray(VAO);
                // someOpenGLFunctionThatDrawsOurTriangle();
                // Update window with OpenGL rendering
                SDL_GL_SwapWindow(m_Window);
            }
        }

        
        
    }
    
    App::~App()
    {
        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &VBO);
        glDeleteProgram(shaderProgram);
        SDL_DestroyWindow(m_Window);
        SDL_Quit();
        m_Logger.reset();
        m_Controller.reset();
    }

    int App::getReturnCode() const 
    {
        return m_ReturnCode;
    }

    void App::handleCommand(const Uint32 command)
    {
        switch (command)
        {
        case -1:
            m_Quit = true;
            break;
        
        default:
            break;
        }
    }
    
} // namespace App
