#include "Renderer.h"
#include "../../Backend/Misc/Fonts/Fonts.h"
#include "../../Backend/Misc/Fonts/FontManager.h"
#include "../../Backend/Misc/Textures/Textures.h"

std::unique_ptr<Render::CDraw> Render::Draw = std::make_unique<Render::CDraw>();

using namespace Render;
using namespace GamesenseMenuFramework;

void CDraw::CreateObjects()
{
    // Register fonts through FontManager (DPI-scaled)
    float s = m_CurrentDpiScale;
    auto& fm = FontManager::GetInstance();

    // Standard UI fonts: Verdana 12, Tahombd 12, SmallFont 8
    fm.RegisterFont("Verdana",   "C:\\Windows\\Fonts\\Verdana.ttf",   (int)(12 * s));
    fm.RegisterFont("Tahombd",   "C:\\Windows\\Fonts\\Verdanab.ttf",  (int)(12 * s));
    fm.RegisterFont("SmallFont", "C:\\Windows\\Fonts\\verdana.ttf",   (int)(8 * s));

    // Chinese fallback fonts (searched in order on first CJK text)
    fm.RegisterFont("CJK",       "C:\\Windows\\Fonts\\simhei.ttf",    (int)(12 * s));

    // Tab icon fonts from memory
    fm.RegisterFontFromMemory("TabIcons",      FontsData::TabIcons,      5192,  (int)(47 * s));

    // Load background texture from memory (16x16, tile mode)
    RLImage bgImage = RLLoadImageFromMemory(".png", TexturesData::BgTexture, 424852);
    m_BgTexture = RLLoadTextureFromImage(bgImage);
    RLUnloadImage(bgImage);
    RLSetTextureWrap(m_BgTexture, RL_E_TEXTURE_WRAP_REPEAT);
}

void CDraw::SetDpiScale(float dpiScale)
{
    if (dpiScale == m_CurrentDpiScale)
        return;

    m_CurrentDpiScale = dpiScale;
    FontManager::GetInstance().SetDpiScale(dpiScale);
}

void CDraw::ReleaseObjects()
{
    if (!Initialized)
        return;

    FontManager::GetInstance().UnloadAll();
    RLUnloadTexture(m_BgTexture);
}

void CDraw::Init()
{
    if (!Initialized) {
        CreateObjects();
        Initialized = true;
    }
}

void CDraw::Reset()
{
    Screen.Width = (float)RLGetScreenWidth();
    Screen.Height = (float)RLGetScreenHeight();
    Screen.x_center = Screen.Width / 2;
    Screen.y_center = Screen.Height / 2;

    // Usable render area (excludes taskbar): get work area via Win32 API
    RECT workArea = { 0 };
    SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
    Screen.RenderWidth = (float)(workArea.right - workArea.left);
    Screen.RenderHeight = (float)(workArea.bottom - workArea.top);

    // Set up correct alpha blending for DWM transparent window:
    // - RGB: standard alpha blending (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    // - Alpha: preserve source alpha for opaque pixels (GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
    // This ensures opaque menu elements write alpha=255 so DWM renders them solid
    rlSetBlendMode(RL_BLEND_CUSTOM_SEPARATE);
    rlSetBlendFactorsSeparate(RL_SRC_ALPHA, RL_ONE_MINUS_SRC_ALPHA,
                              RL_ONE, RL_ONE_MINUS_SRC_ALPHA,
                              RL_FUNC_ADD, RL_FUNC_ADD);
}

void CDraw::Line(Vec2 Pos, Vec2 Pos2, RLColor Color)
{
    RLDrawLine((int)round(Pos.x), (int)round(Pos.y), (int)round(Pos2.x), (int)round(Pos2.y), Color);
}

void CDraw::Rect(Vec2 Pos, Vec2 Size, float LineWidth, RLColor Color, bool Antialias)
{
    if (LineWidth == 0 || LineWidth == 1)
    {
        RLDrawRectangleLines((int)Pos.x, (int)Pos.y, (int)Size.x, (int)Size.y, Color);
    }
    else
    {
        RLDrawRectangleLinesEx(RLRectangle{ Pos.x, Pos.y, Size.x, Size.y }, LineWidth, Color);
    }
}

void CDraw::FilledRect(Vec2 Pos, Vec2 Size, RLColor Color, bool Antialias)
{
    (void)Antialias;  // raylib handles antialiasing internally via MSAA
    RLDrawRectangle((int)Pos.x, (int)Pos.y, (int)Size.x, (int)Size.y, Color);
}

void CDraw::BorderedRect(Vec2 Pos, Vec2 Size, float BorderWidth, RLColor Color, RLColor BorderColor)
{
    FilledRect(Pos, Size, Color);
    Rect(Vec2(Pos.x - BorderWidth, Pos.y - BorderWidth), Vec2(Size.x + 2 * BorderWidth, Size.y + BorderWidth), BorderWidth, BorderColor);
}

void CDraw::Gradient(Vec2 Pos, Vec2 Size, RLColor LColor, RLColor ROtherColor, bool Vertical, bool Antialias)
{
    (void)Antialias;
    if (Vertical)
        RLDrawRectangleGradientV((int)Pos.x, (int)Pos.y, (int)Size.x, (int)Size.y, LColor, ROtherColor);
    else
        RLDrawRectangleGradientH((int)Pos.x, (int)Pos.y, (int)Size.x, (int)Size.y, LColor, ROtherColor);
}

void CDraw::Text(const char* text, float x_, float y_, int Orientation, RLFont Font, bool Bordered, RLColor Color, Vec2 TextClipSize)
{
    // Auto-detect CJK characters and switch to CJK font for rendering
    // (Verdana lacks CJK glyphs; simhei provides them)
    bool hasCJK = false;
    for (const char* p = text; *p; ) {
        unsigned char c = (unsigned char)*p;
        if (c >= 0x80) { hasCJK = true; break; }
        p++;
    }
    if (hasCJK) {
        Font = FontManager::GetInstance().GetFont("CJK");
        FontManager::GetInstance().EnsureText("CJK", text);
    }

    float fontSize = (float)Font.baseSize;
    if (fontSize <= 0) fontSize = 12.0f;
    
    RLVector2 textSize = RLMeasureTextEx(Font, text, fontSize, 1.0f);
    RLVector2 pos = { x_, y_ };
    RLVector2 origin = { 0, 0 };

    // Handle text alignment
    switch (Orientation)
    {
    case CENTER:
        origin.x = textSize.x / 2.0f;
        break;
    case RIGHT:
        origin.x = textSize.x;
        break;
    case LEFT:
    default:
        origin.x = 0;
        break;
    }

    // Handle clipping
    if (TextClipSize.x > 0 || TextClipSize.y > 0)
    {
        RLBeginScissorMode((int)x_, (int)y_, (int)TextClipSize.x, (int)TextClipSize.y);
    }

    if (Bordered)
    {
        RLColor borderCol = { 15, 15, 15, Color.a };
        // Draw 8-direction border (shadow effect)
        RLDrawTextEx(Font, text, { pos.x - 1, pos.y }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x + 1, pos.y }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x, pos.y - 1 }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x, pos.y + 1 }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x - 1, pos.y - 1 }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x + 1, pos.y + 1 }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x - 1, pos.y + 1 }, fontSize, 1.0f, borderCol);
        RLDrawTextEx(Font, text, { pos.x + 1, pos.y - 1 }, fontSize, 1.0f, borderCol);
    }

    RLDrawTextEx(Font, text, pos, fontSize, 1.0f, Color);

    if (TextClipSize.x > 0 || TextClipSize.y > 0)
    {
        RLEndScissorMode();
    }
}

void CDraw::Sprite(RLTexture2D Texture, Vec2 Pos, Vec2 Size, RLColor Color)
{
    // Source rect matches destination so texture tiles via WRAP_REPEAT
    RLRectangle src = { 0, 0, Size.x, Size.y };
    RLRectangle dst = { Pos.x, Pos.y, Size.x, Size.y };
    RLVector2 origin = { 0, 0 };
    RLDrawTexturePro(Texture, src, dst, origin, 0.0f, Color);
}

void CDraw::Triangle(Vec2 Top, Vec2 Left, Vec2 Right, RLColor Color, bool antialias)
{
    (void)antialias;
    RLDrawTriangle({ Top.x, Top.y }, { Left.x, Left.y }, { Right.x, Right.y }, Color);
}

RLTexture2D CDraw::GetBgTexture()
{
    return m_BgTexture;
}

Vec2 CDraw::GetTextSize(RLFont Font, const char* Text)
{
    // Auto-detect CJK and measure with CJK font for correct size
    bool hasCJK = false;
    for (const char* p = Text; *p; ) {
        if ((unsigned char)*p >= 0x80) { hasCJK = true; break; }
        p++;
    }
    if (hasCJK) {
        Font = FontManager::GetInstance().GetFont("CJK");
        FontManager::GetInstance().EnsureText("CJK", Text);
    }

    float fontSize = (float)Font.baseSize;
    if (fontSize <= 0) fontSize = 12.0f;
    RLVector2 size = RLMeasureTextEx(Font, Text, fontSize, 1.0f);
    return Vec2(size.x, size.y);
}