-- VDS_Components_CanvasLoadouts
-- ============================================================================
-- loadoutChipWall -- 7-col x 3-row (21 cell) grid for the canvas-preview footer.
-- Renders up to 20 saved loadouts as small color-striped chips; cell 21 is a
-- dedicated "+" save-new tile (always present).
--
-- Layout: 28x28 chips, 4px gap, 220x88 total.
-- Each filled chip shows N stripes vertically (N = present dye count for the
-- loadout, 1-3). Stripes are dynamically sized so the chip always fills.
-- Empty cells (after the last loadout, before "+") render as muted dashed.
--
-- Interactions (this iteration):
--   left-click filled chip        -> apply loadout (CANVAS_APPLY_LOADOUT)
--   right-click filled chip       -> rename loadout (popup)
--   shift+right-click filled chip -> delete loadout (CANVAS_DELETE_LOADOUT)
--   left-click "+" tile           -> open save-new popup (StaticPopup_Show)
--   left-click empty cell         -> no-op (parity with "+")

VDS = VDS or {}
local Types = VDS.WidgetTypes

local CHIP_SIZE = 28
local CHIP_GAP  = 4
local COLS, ROWS = 11, 1
local CELL_COUNT = COLS * ROWS  -- 11
local SAVE_TILE_IDX = CELL_COUNT  -- cell 11 = "+"

local WHITE = "Interface\\Buttons\\WHITE8x8"

local function buildChipChrome(chip)
    if chip._laidOut then return end
    chip._laidOut = true

    chip:SetSize(CHIP_SIZE, CHIP_SIZE)
    chip:RegisterForClicks("LeftButtonUp", "RightButtonUp")

    -- Background fill (default = chip dark). For empty cells, we tint to
    -- a darker shade. For "+" tile, we tint gold.
    local bg = chip:CreateTexture(nil, "BACKGROUND", nil, 0)
    bg:SetTexture(WHITE)
    bg:SetVertexColor(0.10, 0.10, 0.12, 1)
    bg:SetAllPoints()
    chip._bg = bg

    -- Border (gold on hover; dark by default).
    local border = chip:CreateTexture(nil, "BORDER", nil, 1)
    border:SetTexture(WHITE)
    border:SetVertexColor(0.30, 0.30, 0.36, 1)
    border:SetPoint("TOPLEFT",     chip, "TOPLEFT",     -1, 1)
    border:SetPoint("BOTTOMRIGHT", chip, "BOTTOMRIGHT",  1, -1)
    chip._border = border

    -- 3 stripe textures (used by filled chips; hidden for empty / "+" cells).
    chip._stripes = {}
    for i = 1, 3 do
        local s = chip:CreateTexture(nil, "ARTWORK", nil, 1)
        s:SetTexture(WHITE)
        s:Hide()
        chip._stripes[i] = s
    end

    -- "+" glyph for the save tile -- Blizzard's uitools-icon-plus (white
    -- atlas; vertex-colored each frame by the cycle when hint is on).
    local plus = chip:CreateTexture(nil, "OVERLAY")
    plus:SetAtlas("uitools-icon-plus", false)
    plus:SetSize(14, 14)
    plus:SetPoint("CENTER", chip, "CENTER", 0, 0)
    plus:Hide()
    chip._plus = plus

    -- Four 1px edge strips for the save tile's hollow border. The solid
    -- _border texture is a fully-filled 30x30 square (visible only because the
    -- inner stripes cover it on filled chips) -- swapping bg off would expose
    -- that fill as a solid block. These strips draw a true outline instead.
    -- Only shown by paintSaveTile; cycled in lockstep with _plus.
    chip._edges = {}
    for i = 1, 4 do
        local e = chip:CreateTexture(nil, "OVERLAY", nil, -1)  -- above plus' default but vertex-colored same
        e:SetTexture(WHITE)
        e:Hide()
        chip._edges[i] = e
    end
    -- Anchor: 1=top, 2=right, 3=bottom, 4=left -- all 1px thick.
    chip._edges[1]:SetPoint("TOPLEFT",     chip, "TOPLEFT",  0,  0)
    chip._edges[1]:SetPoint("TOPRIGHT",    chip, "TOPRIGHT", 0,  0)
    chip._edges[1]:SetHeight(1)
    chip._edges[2]:SetPoint("TOPRIGHT",    chip, "TOPRIGHT",    0, 0)
    chip._edges[2]:SetPoint("BOTTOMRIGHT", chip, "BOTTOMRIGHT", 0, 0)
    chip._edges[2]:SetWidth(1)
    chip._edges[3]:SetPoint("BOTTOMLEFT",  chip, "BOTTOMLEFT",  0, 0)
    chip._edges[3]:SetPoint("BOTTOMRIGHT", chip, "BOTTOMRIGHT", 0, 0)
    chip._edges[3]:SetHeight(1)
    chip._edges[4]:SetPoint("TOPLEFT",     chip, "TOPLEFT",    0, 0)
    chip._edges[4]:SetPoint("BOTTOMLEFT",  chip, "BOTTOMLEFT", 0, 0)
    chip._edges[4]:SetWidth(1)

    -- OnEnter / OnLeave own the gold-hover border tint. Tooltip render
    -- belongs to TE (HookScript-composed below).
    chip:SetScript("OnEnter", function(self)
        if self._border then self._border:SetVertexColor(0.94, 0.75, 0.35, 1) end  -- gold hover
    end)
    chip:SetScript("OnLeave", function(self)
        -- Restore the per-variant base border color (set by paintFilled /
        -- paintEmpty / paintSaveTile and stamped on the chip as _baseBorder).
        if self._border and self._baseBorder then
            local c = self._baseBorder
            self._border:SetVertexColor(c[1], c[2], c[3], 1)
        end
    end)

    VDS.TooltipEngine:Attach(chip, VDS.TooltipRecipes.LoadoutChip)

    chip:SetScript("OnClick", function(self, button)
        if self._loadout and VDS.Controller_Canvas then
            if button == "RightButton" then
                if _G.IsShiftKeyDown() then
                    VDS.Controller_Canvas:OnLoadoutDelete(self._loadout.decorID, self._loadout.idx)
                else
                    VDS.Controller_Canvas:OnLoadoutRename(self._loadout)
                end
            else
                VDS.Controller_Canvas:OnLoadoutChipClick(self._loadout)
            end
        elseif self._isSaveTile and VDS.Controller_Canvas then
            VDS.Controller_Canvas:OnLoadoutSaveClick()
        end
    end)
end

-- Render a filled chip from a loadout entry.
local function paintFilled(chip, loadout)
    if chip._pulsing then chip:SetScript("OnUpdate", nil); chip._pulsing = false end
    chip._loadout    = loadout
    chip._isSaveTile = false
    chip._plus:Hide()
    chip._bg:Show()      -- save tile hid these; restore for non-save paints
    chip._border:Show()
    for i = 1, 4 do chip._edges[i]:Hide() end
    chip._bg:SetVertexColor(0.10, 0.10, 0.12, 1)
    chip._baseBorder = { 0.30, 0.30, 0.36 }
    chip._border:SetVertexColor(0.30, 0.30, 0.36, 1)

    -- Count present dyes (loadout.dyes[0..2]; nil where channel unused).
    local present = {}
    for i = 0, 2 do if loadout.dyes and loadout.dyes[i] then present[#present + 1] = loadout.dyes[i] end end
    local n = #present
    if n == 0 then
        for i = 1, 3 do chip._stripes[i]:Hide() end
        return
    end

    -- Inner area = chip_size - 2 (1px border each side). 3 stripes split that
    -- equally; 2 stripes split half-and-half; 1 stripe fills.
    local innerH = CHIP_SIZE - 2
    local stripeH = innerH / n
    for i = 1, 3 do
        local s = chip._stripes[i]
        if i <= n then
            local dyeID = present[i]
            local info  = VDS.DyeOwnershipObserver:ResolveInfo(dyeID)  -- boundary, ADR-001
            local c     = info and info.swatchRGB  -- exception(nullable): catalog lookup can miss pre-CATALOG_READY
            if c then s:SetVertexColor(c.r, c.g, c.b, 1) else s:SetVertexColor(0.5, 0.5, 0.5, 1) end
            s:ClearAllPoints()
            s:SetPoint("TOPLEFT",  chip, "TOPLEFT",   1, -1 - (i - 1) * stripeH)
            s:SetPoint("TOPRIGHT", chip, "TOPRIGHT", -1, -1 - (i - 1) * stripeH)
            s:SetHeight(stripeH)
            s:Show()
        else
            s:Hide()
        end
    end
end

-- Empty cell (slot reserved for a future loadout).
local function paintEmpty(chip)
    chip._loadout    = nil
    chip._isSaveTile = false
    chip._plus:Hide()
    chip._bg:Show()
    chip._border:Show()
    for i = 1, 4 do chip._edges[i]:Hide() end
    for i = 1, 3 do chip._stripes[i]:Hide() end
    chip._bg:SetVertexColor(0.08, 0.08, 0.10, 1)
    chip._baseBorder = { 0.18, 0.18, 0.22 }
    chip._border:SetVertexColor(0.18, 0.18, 0.22, 1)
end

-- Rainbow color-cycle for the "+" save tile when an unsaved combo exists.
-- Borrows VDS_Components_Custom's SPECTRUM_BAR_COLORS palette (used by the
-- titlebar) so the animation reads as "this addon's accent" rather than
-- chip-wall-specific gold. Both the border and the plus glyph cycle together;
-- segments interpolate continuously so the motion is smooth, not stepped.
local SPECTRUM = {
    { 1.00, 0.15, 0.15 },
    { 1.00, 0.50, 0.10 },
    { 1.00, 0.90, 0.15 },
    { 0.30, 0.85, 0.20 },
    { 0.15, 0.85, 0.75 },
    { 0.20, 0.45, 1.00 },
    { 0.55, 0.25, 0.90 },
    { 0.90, 0.35, 0.65 },
}
local CYCLE_PERIOD = 4.0  -- seconds for one full trip through the spectrum
local function startSaveTilePulse(chip)
    if chip._pulsing then return end
    chip._pulsing = true
    chip._pulseT  = 0
    chip:SetScript("OnUpdate", function(self, elapsed)
        self._pulseT = self._pulseT + elapsed
        local t   = (self._pulseT % CYCLE_PERIOD) / CYCLE_PERIOD
        local n   = #SPECTRUM
        local pos = t * n
        local i   = math.floor(pos) + 1
        local j   = (i % n) + 1
        local f   = pos - math.floor(pos)
        local a, b = SPECTRUM[i], SPECTRUM[j]
        local r = a[1] + (b[1] - a[1]) * f
        local g = a[2] + (b[2] - a[2]) * f
        local bl = a[3] + (b[3] - a[3]) * f
        for k = 1, 4 do self._edges[k]:SetVertexColor(r, g, bl, 1) end
        self._plus:SetVertexColor(r, g, bl, 1)
    end)
end
local function stopSaveTilePulse(chip)
    if not chip._pulsing then return end
    chip._pulsing = false
    chip:SetScript("OnUpdate", nil)
end

-- "+" save tile (always last visible cell when under cap).
-- True hollow tile: bg AND solid _border hidden; 4 edge strips draw the
-- outline. Resting state is a quiet muted white; hint=true (current dye combo
-- is unsaved) starts a rainbow cycle on both the edges and the plus glyph
-- synchronously.
local function paintSaveTile(chip, hint)
    chip._loadout    = nil
    chip._isSaveTile = true
    for i = 1, 3 do chip._stripes[i]:Hide() end
    chip._bg:Hide()
    chip._border:Hide()
    for i = 1, 4 do
        chip._edges[i]:SetVertexColor(0.55, 0.55, 0.60, 1)
        chip._edges[i]:Show()
    end
    chip._plus:SetVertexColor(0.85, 0.85, 0.85, 1)
    chip._plus:Show()
    if hint then startSaveTilePulse(chip) else stopSaveTilePulse(chip) end
end

-- Growing-strip dispatch: only chips 1..N (filled) + chip N+1 (save tile) render.
-- Empty slots are hidden entirely; visible chips re-anchor each paint so the
-- whole strip stays centered within the widget's declared width.
local function dispatchChipWall(widget, values)
    local loadouts = values.loadouts or {}
    local MAX = VDS.Constants.FLOATING_WIDGET.CANVAS_MAX_LOADOUTS_PER_DECOR
    local n = #loadouts
    if n > MAX then n = MAX end

    -- Save tile only shown when there's room for another loadout. At cap (n ==
    -- MAX) the "+" disappears so the chip wall doesn't lie about being able to
    -- add more.
    local saveTileVisible = n < MAX
    local visible    = n + (saveTileVisible and 1 or 0)
    local totalWidth = visible * CHIP_SIZE + math.max(0, visible - 1) * CHIP_GAP
    local frameW     = widget:GetWidth()
    local startX     = math.max(0, (frameW - totalWidth) / 2)

    for i = 1, CELL_COUNT do
        local chip = widget._chips[i]
        if not chip then break end
        if i <= visible then
            chip:ClearAllPoints()
            chip:SetPoint("TOPLEFT", widget, "TOPLEFT",
                          startX + (i - 1) * (CHIP_SIZE + CHIP_GAP), 0)
            if i <= n then
                paintFilled(chip, loadouts[i])
            else
                paintSaveTile(chip, values.hasUnsaved == true)
            end
            chip:Show()
        else
            chip:Hide()
        end
    end
end

Types:Register("loadoutChipWall", {
    build = function(parent, spec)
        local f = CreateFrame("Frame", nil, parent)
        f:SetSize(COLS * CHIP_SIZE + (COLS - 1) * CHIP_GAP,
                  ROWS * CHIP_SIZE + (ROWS - 1) * CHIP_GAP)

        f._chips = {}
        for i = 1, CELL_COUNT do
            local col = ((i - 1) % COLS)
            local row = math.floor((i - 1) / COLS)
            local chip = CreateFrame("Button", nil, f)
            buildChipChrome(chip)
            chip:SetPoint("TOPLEFT", f, "TOPLEFT",
                          col * (CHIP_SIZE + CHIP_GAP),
                         -row * (CHIP_SIZE + CHIP_GAP))
            f._chips[i] = chip
        end

        return f
    end,
    dispatch = { fields = { "loadouts", "hasUnsaved" }, push = dispatchChipWall },
    specFields = { "binding" },
})
