#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <vector>
#include <memory>

#include "Components/TilemapComponent.h"
#include "Components/SpriteComponent.h"
#include "Components/SpriteComponent.h"
#include "Components/AnimationComponent.h"

#include "core/utils/EngineConfig.h"
#include "utils/Shader.h"
#include "core/utils/Profiler.h"
#include "core/types/all.h"
#include "core/utils/TextureAtlas.h"
#include "core/utils/Texture.h"

struct ColorCorrection
{
    float brightness = 1.0f;
    float saturation = 1.0f;
    float gamma = 1.0f;

    bool bloom = true;
    float threshold = 1.0f;
    float intensity = 1.2f;

    void Upload(Shader &shader) const
    {
        shader.SetFloat("uBrightness", brightness);
        shader.SetFloat("uSaturation", saturation);
        shader.SetFloat("uGamma", gamma);
    }
};

struct Light
{
    glm::vec2 screenPos;
    glm::vec3 color;
    float intensity;
    float radius;
};

struct QuadInstance
{
    core::types::Vec2 screenPos;
    core::types::Vec2 size;
    float rotation;
    core::types::Color color;
    float roundness = 0.0f;
};

class Renderer
{
public:
    static void Init();
    static void Resize(int w, int h);
    static void Begin();
    static void End();

    static void DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos);
    static void DrawTilemap(TilemapComponent* tilemap, const glm::vec2& pos, float zoom, const glm::vec2& cameraPos);


    static void AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius);
    static void ClearLights();

    static void DrawEditorGrid(const glm::vec2 &cameraPos, float zoom);
    static void DrawGizmoLine(const glm::vec2 &worldStart, const glm::vec2 &worldEnd, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
    static void DrawGizmoRect(const glm::vec2 &worldPos, const glm::vec2 &size, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
    static void DrawGizmoCircle(const glm::vec2 &worldCenter, float radius, int segments, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);

    static GLuint GetRenderTexture();
    static core::types::Vec2 GetSize();
    static int GetDrawCallCount();
    static int GetLightsCount();

    static void SetColorCorrection(std::unique_ptr<ColorCorrection> correction);
    static ColorCorrection *GetColorCorrection();
    static GLuint GetFinalTexture();

    static void UpdateClusterLights();
    static void FlushSprites();

    static void InitQuadBatch();
    static void DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &size, float rotation, const core::types::Color &color);
    static void BatchQuad(const core::types::Vec2 &worldPos,
                          const core::types::Vec2 &size,
                          float rotation,
                          const core::types::Color &color,
                          const core::types::Vec2 &cameraPos,
                          float zoom,
                          float isCircle = 0.0f);

    static void BatchCircle(const core::types::Vec2 &worldPos,
                            const core::types::Vec2 &size,
                            const core::types::Color &color,
                            const core::types::Vec2 &cameraPos,
                            float zoom);

    static void DrawTextureAtlas(const TextureAtlas *atlas, int index, const core::types::Vec2 &pos, float rotationDeg, float zoom);

    static void DrawAnimator(const AnimationComponent *anim, const core::types::Vec2 &worldPos, float zoom, const core::types::Vec2 &cameraPos);

    static void FlushQuads();

private:
    static std::vector<Light> s_Lights;

    static GLuint fbo, textureColorBuffer, rbo;
    static GLuint defaultNormalMap;
    static int width, height;
    static int s_DrawCalls;
    static int s_LightsCount;

    static GLuint shader, quadVAO, quadVBO;
    static void InitQuad();
    static GLuint LoadShader(const char *vertexSrc, const char *fragmentSrc);
    static std::unique_ptr<ColorCorrection> s_ColorCorrection;

    // Clustered Lighting
    static constexpr int CLUSTER_SIZE = 16;
    static constexpr int MAX_LIGHTS_PER_CLUSTER = 32;

    struct Cluster
    {
        std::vector<int> lightIndices;
    };

    static std::vector<Cluster> s_Clusters;
    static GLuint s_ClusterSSBO;
    static int s_ClusterCols;
    static int s_ClusterRows;

    static GLuint bloomFBO;
    static GLuint bloomTexture;

    static std::vector<QuadInstance> s_QuadBatch;
    static GLuint s_QuadVAO, s_QuadVBO, s_QuadInstanceVBO;
    static Shader s_UnlitQuadShader;
    static constexpr size_t MAX_QUADS = 10000;

    static void *s_QuadMappedPtr;
    static GLuint s_QuadPersistentFlags;
};
