#include "GL/gl3w.h"
#include "GL/gl.h"
#include "OpenGLRenderingBackend.hpp"
#include "GLFW/glfw3.h"
#include "WindowGlfw.hpp"
#include "Debug.hpp"
#include "imgui/imgui.h"
#include "extern/imgui_impl_glfw.h"
#include "extern/imgui_impl_opengl3.h"
#include "PathHelper.hpp"
#include "elements/Texture.hpp"
#include "TextureHelperOpenGL.hpp"
#include "interfaces/IRenderer.hpp"
#include "BehaviourScripts/Camera.hpp"
#include "RenderTextureCreatorOpenGL.hpp"
#include "CameraHelperOpenGL.hpp"

TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync)
: OpenGLRenderingBackend(_backgroundColor, _vsync, 0, false){ }

TSE::GLFW::OpenGLRenderingBackend::OpenGLRenderingBackend(Color _backgroundColor, bool _vsync, int _samples, bool _useseImGui)
{
    backgroundColor = _backgroundColor;
    vsync = _vsync;
    samples = _samples;
    useseImGui = _useseImGui;
}

TSE::GLFW::OpenGLRenderingBackend::~OpenGLRenderingBackend()
{
    if(useseImGui)
    {
        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplGlfw_Shutdown();
        ImGui::DestroyContext();
    }
}

void TSE::GLFW::OpenGLRenderingBackend::InitPreWindow()
{
    IRenderTexture::factory = new RenderTextureCreatorOpenGL();
    Texture::helper = new TextureHelperOpenGL();
    Camera::helper = new CameraHelperOpenGL();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TSE_OPENGL_VERSION_MAJOR);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, TSE_OPENGL_VERSION_MINOR);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_SAMPLES, samples);
}

bool TSE::GLFW::OpenGLRenderingBackend::InitPostWindow()
{
    WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
    glfwMakeContextCurrent(wnd->window);
    if(gl3wInit())
    {
        Debug::Log("Failed to initialize gl3w.");
        return false;
    }
    if(!gl3wIsSupported(TSE_OPENGL_VERSION_MAJOR, TSE_OPENGL_VERSION_MINOR))
    {
        Debug::Log("gl3w dose not support the selected version of OpenGL.");
        return false;
    }

    if(vsync) glfwSwapInterval(1);
    else glfwSwapInterval(0);

    Debug::Log("OpenGL:" + std::string((const char*)glGetString(GL_VERSION)));
    Debug::Log("GLSL:" + std::string((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)));

    glEnable(GL_BLEND);
    glClearDepth(0.0);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_GEQUAL);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    glFrontFace(GL_CW);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glClearColor(backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a);
    glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    return true;
}

std::string imguiIniPath;

bool TSE::GLFW::OpenGLRenderingBackend::InitEnd()
{
    if(useseImGui)
    {
        GetAppDataPath(imguiIniPath);
        imguiIniPath += "/UI.cfg";
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGuiIO& io = ImGui::GetIO(); (void)io;
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
        io.IniFilename = imguiIniPath.c_str();
        ImGui::StyleColorsDark();

        WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
        ImGui_ImplGlfw_InitForOpenGL(wnd->window, true);
        ImGui_ImplOpenGL3_Init("#version 130");
        
        Debug::Log("ImGui:" + std::string(ImGui::GetVersion()));
    }
    return true;
}

void TSE::GLFW::OpenGLRenderingBackend::onResize(int width, int height)
{
    glViewport(0,0,width, height);
}

void TSE::GLFW::OpenGLRenderingBackend::onUpdate() const
{
    int error = glGetError();
    if(error != GL_NO_ERROR)
    {
        Debug::Log("OpenGL Error: " + std::to_string(error));
    }

    if(useseImGui)
    {
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        
        if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
        {
            GLFWwindow* backup_current_context = glfwGetCurrentContext();
            ImGui::UpdatePlatformWindows();
            ImGui::RenderPlatformWindowsDefault();
            glfwMakeContextCurrent(backup_current_context);
        }

    }

    WindowGlfw* wnd = static_cast<WindowGlfw*>(window);
    glfwSwapBuffers(wnd->window);
}

void TSE::GLFW::OpenGLRenderingBackend::onClear() const
{
    for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
    {
        IRenderer::camerasToRenderWith[i]->Bind();
        IRenderer::camerasToRenderWith[i]->UpdateRenderTarget();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        IRenderer::camerasToRenderWith[i]->Unbind();
    }

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    if(useseImGui)
    {
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
    }
}

void TSE::GLFW::OpenGLRenderingBackend::onClearDepthBuffer() const
{
    for (int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
    {
        IRenderer::camerasToRenderWith[i]->Bind();
        glClear(GL_DEPTH_BUFFER_BIT);
        IRenderer::camerasToRenderWith[i]->Unbind();
    }
    
    glClear(GL_DEPTH_BUFFER_BIT);
}
