//
// Created by sebastian on 29.07.26.
//

#include "Window.h"

#include <iostream>

#include "EngineConfig.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "utils/openglWrapper/GLStateCache.h"

namespace engine {
    static void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id,
                                      GLenum severity, GLsizei length,
                                      const GLchar* message, const void* userParam) {
        if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) return; // Spam raus
        std::cerr << "[GL DEBUG] " << message << "\n";
    }

    Window::Window(const EngineConfig& config) {
        if (!glfwInit()) throw std::runtime_error("GLFW init failed");

        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, config.debugContext ? GLFW_TRUE : GLFW_FALSE);

        if (config.msaa) {
            glfwWindowHint(GLFW_SAMPLES, config.msaaSamples);
        }

        GLFWmonitor* monitor = config.fullscreen ? glfwGetPrimaryMonitor() : nullptr;
        m_handle = glfwCreateWindow(config.windowWidth, config.windowHeight, config.windowTitle.c_str(), monitor, nullptr);
        if (!m_handle) {
            glfwTerminate();
            throw std::runtime_error("Window creation failed");
        }

        glfwMakeContextCurrent(m_handle);
        glfwSwapInterval(config.vsync ? 1 : 0); // VSync

        if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
            throw std::runtime_error("GLAD init failed");
        }

        GLStateCache::init();

        if (config.debugContext) {
            std::cout << "Debug Context should be enabled" << std::endl;
            glEnable(GL_DEBUG_OUTPUT);
            glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
            glDebugMessageCallback(DebugCallback, nullptr);
        }

        if (config.msaa) {
            glEnable(GL_MULTISAMPLE);
        }



        glViewport(0, 0, config.windowWidth, config.windowHeight);
        glfwSetWindowUserPointer(m_handle, &m_callbackTarget);
    }

    Window::~Window() {
        glfwDestroyWindow(m_handle);
        glfwTerminate();
    }

    bool Window::shouldClose() const { return glfwWindowShouldClose(m_handle); }
    void Window::swapBuffers() { glfwSwapBuffers(m_handle); }
    void Window::pollEvents() { glfwPollEvents(); }

    int Window::getWidth() const {
        int width, height;
        glfwGetFramebufferSize(m_handle, &width, &height);
        return width;
    }

    int Window::getHeight() const {
        int width, height;
        glfwGetFramebufferSize(m_handle, &width, &height);
        return height;
    }
}
