/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/
module;
module Crafter.Graphics:UIComponents_impl;
import :UIComponents;
import :UI;
import :Font;
import :FontAtlas;
import std;

using namespace Crafter;

namespace {
    // Push one QuadItem into buf if there's room. No-op if full.
    inline void PushQuad(UIBuffer& buf, const QuadItem& q) {
        if (buf.quads == nullptr || buf.quadCount == nullptr) return;
        if (*buf.quadCount >= buf.quadCap) return;
        buf.quads[(*buf.quadCount)++] = q;
    }

    inline std::array<float, 4> Pick(const std::array<float, 4>& a,
                                     const std::array<float, 4>& b, bool useB) {
        return useB ? b : a;
    }

    // Centered single-line text emit. Appends glyphs at the buffer's tail,
    // then offsets them so the run is horizontally centered around `centerX`.
    // Vertical baseline is placed at the centerY plus a coarse ascent estimate
    // (fontSize * 0.32). Returns the advance width that was written.
    float EmitCenteredLabel(UIBuffer& buf, std::string_view label,
                            Font& font, float fontSize,
                            float centerX, float centerY,
                            std::array<float, 4> color)
    {
        if (label.empty() || buf.atlas == nullptr || buf.renderer == nullptr) return 0.0f;
        if (buf.glyphs == nullptr || buf.glyphCount == nullptr) return 0.0f;

        std::uint32_t before = *buf.glyphCount;
        std::uint32_t cap    = (buf.glyphCap > before) ? (buf.glyphCap - before) : 0;
        if (cap == 0) return 0.0f;

        GlyphItem* writePos = buf.glyphs + before;
        float baseline      = centerY + fontSize * 0.32f;
        float advance       = 0.0f;
        // ShapeText folds the -advance/2 centering shift into its own pass.
        std::uint32_t n = buf.renderer->ShapeText(
            font, fontSize, centerX, baseline,
            label, color, writePos, cap, &advance, TextAlign::Center
        );
        *buf.glyphCount = before + n;
        return advance;
    }
}

// ─── DrawButton ─────────────────────────────────────────────────────────

void Crafter::DrawButton(UIBuffer& buf, Rect r, std::string_view label,
                         bool hovered, bool pressed,
                         Font& font, float fontSize,
                         const ButtonColors& c)
{
    auto bg = pressed ? c.bgPressed : (hovered ? c.bgHover : c.bg);
    PushQuad(buf, QuadItem{
        r.x, r.y, r.w, r.h,
        bg[0], bg[1], bg[2], bg[3],
        c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
        c.borderThickness, c.border[0], c.border[1], c.border[2],
    });

    EmitCenteredLabel(buf, label, font, fontSize,
                      r.x + r.w * 0.5f, r.y + r.h * 0.5f, c.text);
}

// ─── DrawCheckbox ───────────────────────────────────────────────────────

void Crafter::DrawCheckbox(UIBuffer& buf, Rect r, bool checked, bool hovered,
                           const CheckboxColors& c)
{
    auto bg = Pick(c.bg, c.bgHover, hovered);
    PushQuad(buf, QuadItem{
        r.x, r.y, r.w, r.h,
        bg[0], bg[1], bg[2], bg[3],
        c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
        c.borderThickness, c.border[0], c.border[1], c.border[2],
    });
    if (checked) {
        Rect inner = r.Inset(c.checkInset);
        if (inner.w > 0 && inner.h > 0) {
            float innerR = std::max(0.0f, c.cornerRadius - c.checkInset);
            PushQuad(buf, QuadItem{
                inner.x, inner.y, inner.w, inner.h,
                c.check[0], c.check[1], c.check[2], c.check[3],
                innerR, innerR, innerR, innerR,
                0, 0, 0, 0,
            });
        }
    }
}

// ─── DrawSlider ─────────────────────────────────────────────────────────

void Crafter::DrawSlider(UIBuffer& buf, Rect r, float t01, bool dragging,
                         const SliderColors& c)
{
    t01 = std::clamp(t01, 0.0f, 1.0f);

    // Track is a thin centered horizontal strip.
    float trackY = r.y + (r.h - c.trackHeight) * 0.5f;
    float trackR = c.trackHeight * 0.5f;
    float fillW  = r.w * t01;

    if (fillW > 0.0f) {
        PushQuad(buf, QuadItem{
            r.x, trackY, fillW, c.trackHeight,
            c.trackFilled[0], c.trackFilled[1], c.trackFilled[2], c.trackFilled[3],
            trackR, trackR, trackR, trackR,
            0, 0, 0, 0,
        });
    }
    if (fillW < r.w) {
        PushQuad(buf, QuadItem{
            r.x + fillW, trackY, r.w - fillW, c.trackHeight,
            c.track[0], c.track[1], c.track[2], c.track[3],
            trackR, trackR, trackR, trackR,
            0, 0, 0, 0,
        });
    }

    // Thumb: a quad with cornerRadius = thumbRadius (= a circle).
    auto thumbColor = Pick(c.thumb, c.thumbHover, dragging);
    float thumbCx = r.x + r.w * t01;
    float thumbCy = r.y + r.h * 0.5f;
    float d = c.thumbRadius * 2.0f;
    PushQuad(buf, QuadItem{
        thumbCx - c.thumbRadius, thumbCy - c.thumbRadius, d, d,
        thumbColor[0], thumbColor[1], thumbColor[2], thumbColor[3],
        c.thumbRadius, c.thumbRadius, c.thumbRadius, c.thumbRadius,
        0, 0, 0, 0,
    });
}

// ─── DrawProgressBar ────────────────────────────────────────────────────

void Crafter::DrawProgressBar(UIBuffer& buf, Rect r, float t01,
                              const ProgressColors& c)
{
    t01 = std::clamp(t01, 0.0f, 1.0f);

    PushQuad(buf, QuadItem{
        r.x, r.y, r.w, r.h,
        c.bg[0], c.bg[1], c.bg[2], c.bg[3],
        c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
        0, 0, 0, 0,
    });

    float fillW = r.w * t01;
    if (fillW > 0.0f) {
        PushQuad(buf, QuadItem{
            r.x, r.y, fillW, r.h,
            c.fill[0], c.fill[1], c.fill[2], c.fill[3],
            c.cornerRadius, c.cornerRadius, c.cornerRadius, c.cornerRadius,
            0, 0, 0, 0,
        });
    }
}

// ─── DrawText ───────────────────────────────────────────────────────────

float Crafter::DrawText(UIBuffer& buf, std::string_view text,
                        float x, float baselineY,
                        Font& font, float fontSize,
                        std::array<float, 4> color,
                        TextAlign align)
{
    if (text.empty() || buf.atlas == nullptr || buf.renderer == nullptr) return 0.0f;
    if (buf.glyphs == nullptr || buf.glyphCount == nullptr) return 0.0f;

    std::uint32_t before = *buf.glyphCount;
    std::uint32_t cap    = (buf.glyphCap > before) ? (buf.glyphCap - before) : 0;
    if (cap == 0) return 0.0f;

    GlyphItem* writePos = buf.glyphs + before;
    float advance = 0.0f;
    // Alignment is applied inside ShapeText (folded into its single pass).
    std::uint32_t n = buf.renderer->ShapeText(
        font, fontSize, x, baselineY,
        text, color, writePos, cap, &advance, align
    );
    *buf.glyphCount = before + n;
    return advance;
}

// ─── DrawImage ──────────────────────────────────────────────────────────

void Crafter::DrawImage(UIBuffer& buf, Rect r,
                        std::uint32_t textureSlot, std::uint32_t samplerSlot,
                        std::array<float, 4> tint,
                        std::array<float, 4> uv)
{
    if (buf.images == nullptr || buf.imageCount == nullptr) return;
    if (*buf.imageCount >= buf.imageCap) return;
    buf.images[(*buf.imageCount)++] = ImageItem{
        r.x, r.y, r.w, r.h,
        uv[0], uv[1], uv[2], uv[3],
        tint[0], tint[1], tint[2], tint[3],
        textureSlot, samplerSlot, 0, 0,
    };
}
