#include "RHI/OpenglDrv/GLDrvAPI.h"

#include "RHI/OpenglDrv/gl.h"

#include "Core/Logger.h"

#include "RHI/RHI.h"
#include "RHI/OpenglDrv/GLRHICommond.h"

bool GLDrvAPI::InitEnv(const DriverSettings& setting) {
    auto glfwErrorCallback = [](int error, const char* description) {
        ERRORLOG("code={}, description={}", error, description);
        };

    glfwSetErrorCallback(glfwErrorCallback);

    if (GLFW_TRUE != glfwInit()) {
        return false; //exit(EXIT_FAILURE);
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, setting.contextMajorVersion);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, setting.contextMinorVersion);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_SAMPLES, setting.samples);

    if (setting.debugProfile) {
        glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
    }

    return true;
}

bool GLDrvAPI::InitRenderEnv(const DriverSettings& setting) {
    // Initialize GLEW to setup the OpenGL Function pointers
    //glfwMakeContextCurrent(reinterpret_cast<GLFWwindow*>(setting.windowHandle));
    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        const GLubyte* errorString = glewGetErrorString(err);
        ERRORLOG("glewInit={1}, err={2}", err, reinterpret_cast<const char*>(errorString));
        return false;
    }
    //glfwSwapInterval(1);

    //if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    //    ERRORLOG("Failed to initialize GLAD");
    //    return false;
    //}
    
    glfwSwapInterval(1);
    return true;
}

void GLDrvAPI::UpdateRenderSize(const DriverSettings& setting) {
    int width = 0, height = 0;
    //glfwGetFramebufferSize(reinterpret_cast<GLFWwindow*>(setting.windowHandle), &width, &height);
    glViewport(0, 0, width, height);

    glViewport(0, 0, width, height);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Screen::set_width_height(width, height);
}

void GLDrvAPI::UninitEnv() {
    glfwTerminate();
}

void GLDrvAPI::PollEvents() {
    glfwPollEvents();
}

RHICommond* GLDrvAPI::CreateVAOCommand(
    class Mesh* mesh,
    uint32 vaoHandle,
    uint32 vboHandle,
    uint32 eboHandle
) {
    auto endCommand = new GLVAOCreateCommand(mesh, vaoHandle, vboHandle, eboHandle);
    return endCommand;
}

RHICommond* GLDrvAPI::DrawArrayCommand(uint32 vaoHandle, uint32 vaoStart, uint32 vaoCount) {
    auto endCommand = new GLDrawArrayCommand(vaoHandle, vaoStart, vaoCount);
    return endCommand;
}

RHICommond* GLDrvAPI::DrawElementsCommand(uint32 vaoHandle, uint32 eboHandle, uint32 eboCount) {
    auto endCommand = new GLDrawElementsCommand(vaoHandle, eboHandle, eboCount);
    return endCommand;
}

RHICommond* GLDrvAPI::DeleteVAOCommand(uint32 vaoHandle, uint32 vboHandle, uint32 eboHandle) {
    auto endCommand = new GLVAODeleteCommand(vaoHandle, vboHandle, eboHandle);
    return endCommand;
}

RHICommond* GLDrvAPI::CreateShaderCommand(uint32 shaderHandle, uint32 type, const std::string& shader) {
    auto endCommand = new GLShaderCreateCommand(shaderHandle, type, shader);
    return endCommand;
}

RHICommond* GLDrvAPI::DeleteShaderCommand(uint32 shaderHandle) {
    auto endCommand = new GLShaderDeleteCommand(shaderHandle);
    return endCommand;
}

RHICommond* GLDrvAPI::CreateProgramCommand(uint32 programHandle, const std::vector<uint32>& shaders) {
    auto endCommand = new GLProgramCreateCommand(programHandle, shaders);
    return endCommand;
}

RHICommond* GLDrvAPI::UseProgramCommand(uint32 programHandle, bool use) {
    auto endCommand = new GLProgramUseCommand(programHandle, use);
    return endCommand;
}

RHICommond* GLDrvAPI::DeleteProgramCommand(uint32 programHandle) {
    auto endCommand = new GLProgramDeleteCommand(programHandle);
    return endCommand;
}


RHICommond* GLDrvAPI::CreateStartCommand() {
    auto endCommand = new GLStartCommand();
    return endCommand;
}

RHICommond* GLDrvAPI::CreateEndCommand() {
    auto endCommand = new GLEndCommand();
    return endCommand;
}