// src/Renderer.cpp

#include "Renderer.h"
#include <glad.h>
#include <iostream>
#include <chrono>

Renderer::Renderer() = default;

void Renderer::Initialize() {
    // Nothing specific for now; ensure GLAD is loaded before this call
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
}

void Renderer::Shutdown() {
    // Cleanup if needed
}

void Renderer::BeginFrame() {
    stats_.draw_calls = 0;
    stats_.vertices_rendered = 0;
    stats_.triangles_rendered = 0;
    frame_start_time_ = GetTime();
}

void Renderer::EndFrame() {
    float frame_end = GetTime();
    stats_.frame_time = frame_end - frame_start_time_;
}

void Renderer::Clear(const glm::vec4& color) {
    glClearColor(color.r, color.g, color.b, color.a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void Renderer::SetViewport(int x, int y, int width, int height) {
    viewport_size_ = { width, height };
    glViewport(x, y, width, height);
}

void Renderer::SetCullFace(CullFace c) {
    if (c == CullFace::None) glDisable(GL_CULL_FACE);
    else {
        glEnable(GL_CULL_FACE);
        glCullFace(static_cast<GLenum>(c));
    }
    current_cull_face_ = c;
}

void Renderer::SetBlendMode(BlendMode mode) {
    if (mode == BlendMode::None) {
        glDisable(GL_BLEND);
    } else {
        glEnable(GL_BLEND);
        switch (mode) {
            case BlendMode::Alpha:     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break;
            case BlendMode::Additive:  glBlendFunc(GL_SRC_ALPHA, GL_ONE); break;
            case BlendMode::Multiply:  glBlendFunc(GL_DST_COLOR, GL_ZERO); break;
            default: break;
        }
    }
    current_blend_mode_ = mode;
}

void Renderer::SetDepthTest(DepthTest dt) {
    if (dt == DepthTest::None) glDisable(GL_DEPTH_TEST);
    else {
        glEnable(GL_DEPTH_TEST);
        GLenum func = GL_LESS;
        switch (dt) {
            case DepthTest::Less:        func = GL_LESS; break;
            case DepthTest::LessEqual:   func = GL_LEQUAL; break;
            case DepthTest::Greater:     func = GL_GREATER; break;
            case DepthTest::GreaterEqual:func = GL_GEQUAL; break;
            case DepthTest::Equal:       func = GL_EQUAL; break;
            case DepthTest::NotEqual:    func = GL_NOTEQUAL; break;
            case DepthTest::Always:      func = GL_ALWAYS; break;
            case DepthTest::Never:       func = GL_NEVER; break;
            default: break;
        }
        glDepthFunc(func);
    }
    current_depth_test_ = dt;
}

void Renderer::SetWireframe(bool enable) {
    glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
    wireframe_enabled_ = enable;
}

void Renderer::PrintRenderInfo() const {
    std::cout << "OpenGL Version: " << GetOpenGLVersion() << "\n";
    std::cout << "Viewport: " << viewport_size_.x << "x" << viewport_size_.y << "\n";
    std::cout << "Draw Calls: " << stats_.draw_calls << "\n";
    std::cout << "Vertices: " << stats_.vertices_rendered << "\n";
    std::cout << "Triangles: " << stats_.triangles_rendered << "\n";
}

void Renderer::ResetStats() {
    stats_ = RenderStats{};
}

std::string Renderer::GetOpenGLVersion() {
    return reinterpret_cast<const char*>(glGetString(GL_VERSION));
}

std::string Renderer::GetGPUInfo() {
    return reinterpret_cast<const char*>(glGetString(GL_RENDERER));
}

bool Renderer::CheckOpenGLError(const std::string& op) {
    GLenum err;
    bool hasError = false;
    while ((err = glGetError()) != GL_NO_ERROR) {
        std::cerr << "OpenGL Error";
        if (!op.empty()) std::cerr << " in " << op;
        std::cerr << ": " << err << "\n";
        hasError = true;
    }
    return !hasError;
}

float Renderer::GetTime() const {
    using clock = std::chrono::high_resolution_clock;
    return std::chrono::duration<float>(clock::now().time_since_epoch()).count();
}
