-- Cadence: Queue.lua -- the upcoming reminders list.
--
-- Shows what is still ahead of you in the fight. Each entry falls off the moment
-- its cue lands, so the top row is always the next thing you have to do.

local ADDON, ns = ...

ns.Queue = {}
local Q = ns.Queue

local FONT = "Fonts\\FRIZQT__.TTF"
local ROW_H, TITLE_H, WIDTH = 20, 20, 240

local entries = {}      -- { key, reminder, fireAt }
local rows = {}
local previewing = false
local lastUpdate = 0

--------------------------------------------------------------------------------
-- Frame
--------------------------------------------------------------------------------

local function Settings_()
    return CadenceDB.settings.queue
end

local function CreateRow(parent, index)
    local r = CreateFrame("Frame", nil, parent)
    r:SetSize(WIDTH - 16, ROW_H)

    r.time = r:CreateFontString(nil, "OVERLAY")
    r.time:SetFont(FONT, 12, "OUTLINE")
    r.time:SetPoint("LEFT", 0, 0)
    r.time:SetWidth(42)
    r.time:SetJustifyH("RIGHT")

    r.icon = r:CreateTexture(nil, "ARTWORK")
    r.icon:SetSize(16, 16)
    r.icon:SetPoint("LEFT", r.time, "RIGHT", 6, 0)
    r.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)

    r.name = r:CreateFontString(nil, "OVERLAY")
    r.name:SetFont(FONT, 12, "OUTLINE")
    r.name:SetPoint("LEFT", r.icon, "RIGHT", 5, 0)
    r.name:SetPoint("RIGHT", 0, 0)
    r.name:SetJustifyH("LEFT")
    r.name:SetWordWrap(false)

    r:SetPoint("TOPLEFT", parent, "TOPLEFT", 8, -(TITLE_H + (index - 1) * ROW_H))
    return r
end

local function AcquireRow(index)
    local r = rows[index]
    if not r then
        r = CreateRow(Q.frame, index)
        rows[index] = r
    end
    return r
end

function Q:Init()
    local s = Settings_()

    local f = CreateFrame("Frame", "CadenceQueue", UIParent, "BackdropTemplate")
    f:SetSize(WIDTH, TITLE_H + ROW_H)
    f:SetPoint(s.anchor.point, UIParent, s.anchor.relPoint, s.anchor.x, s.anchor.y)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:SetClampedToScreen(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", function(self) self:StartMoving() end)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local point, _, relPoint, x, y = self:GetPoint()
        s.anchor.point, s.anchor.relPoint, s.anchor.x, s.anchor.y = point, relPoint, x, y
    end)

    if f.SetBackdrop then
        f:SetBackdrop({
            bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = true, tileSize = 16, edgeSize = 12,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        f:SetBackdropColor(ns.COLORS.black[1], ns.COLORS.black[2], ns.COLORS.black[3], 0.6)
        f:SetBackdropBorderColor(ns.COLORS.red[1], ns.COLORS.red[2], ns.COLORS.red[3], 1)
    end

    f.title = f:CreateFontString(nil, "OVERLAY")
    f.title:SetFont(FONT, 11, "")
    f.title:SetPoint("TOPLEFT", 8, -5)
    f.title:SetTextColor(unpack(ns.COLORS.gold))
    f.title:SetText("Upcoming")

    f:SetScript("OnUpdate", function(_, elapsed)
        lastUpdate = lastUpdate + elapsed
        if lastUpdate < 0.1 then return end
        lastUpdate = 0
        Q:Refresh()
    end)

    f:Hide()
    self.frame = f
    self:UpdateScale()
end

function Q:UpdateScale()
    if self.frame then self.frame:SetScale(Settings_().scale or 1) end
end

--------------------------------------------------------------------------------
-- Contents
--------------------------------------------------------------------------------

function Q:Add(reminder, fireAt)
    if not self.frame then return end
    if not Settings_().enabled then return end

    entries[#entries + 1] = {
        key = tostring(reminder.id) .. "@" .. tostring(fireAt),
        reminder = reminder,
        fireAt = fireAt,
    }
    table.sort(entries, function(a, b) return a.fireAt < b.fireAt end)

    self.frame:Show()
    self:Refresh()
end

function Q:Clear()
    wipe(entries)
    previewing = false
    if self.frame then self.frame:Hide() end
end

function Q:Stop() self:Clear() end

function Q:Count() return #entries end

-- Drops everything whose moment has passed, then redraws.
function Q:Refresh()
    if not self.frame then return end
    local now = GetTime()

    for i = #entries, 1, -1 do
        if entries[i].fireAt <= now then table.remove(entries, i) end
    end

    if #entries == 0 and not previewing then
        self.frame:Hide()
        return
    end

    local maxRows = Settings_().maxRows or 8
    local shown = math.min(#entries, maxRows)

    for i = 1, shown do
        local e = entries[i]
        local r = AcquireRow(i)
        local remaining = math.max(0, e.fireAt - now)

        r.time:SetText(ns.FormatTime(remaining))
        local icon = e.reminder.spellID and ns.GetSpellIcon(e.reminder.spellID)
        r.icon:SetTexture(icon or nil)
        r.icon:SetShown(icon ~= nil)
        if icon then
            r.name:SetPoint("LEFT", r.icon, "RIGHT", 5, 0)
        else
            r.name:SetPoint("LEFT", r.time, "RIGHT", 6, 0)
        end

        local label = e.reminder.label
        if not label or label == "" then label = ns.SpellLabel(e.reminder.spellID) or "Cooldown" end
        if e.reminder.note and e.reminder.note ~= "" then
            label = label .. "  " .. ns.Hex("boneDim") .. e.reminder.note .. "|r"
        end
        r.name:SetText(label)

        -- The next one is bright; everything after it fades back.
        if i == 1 then
            r.time:SetTextColor(unpack(ns.COLORS.goldLight))
            r.name:SetTextColor(unpack(ns.COLORS.bone))
            r:SetAlpha(1)
        else
            r.time:SetTextColor(unpack(ns.COLORS.boneDim))
            r.name:SetTextColor(unpack(ns.COLORS.boneDim))
            r:SetAlpha(math.max(0.45, 1 - (i - 1) * 0.12))
        end
        r:Show()
    end

    for i = shown + 1, #rows do rows[i]:Hide() end

    self.frame:SetHeight(TITLE_H + math.max(1, shown) * ROW_H + 6)

    local hidden = #entries - shown
    self.frame.title:SetText(hidden > 0
        and ("Upcoming  (+%d more)"):format(hidden)
        or "Upcoming")
end

--------------------------------------------------------------------------------
-- Preview, for placing the window outside a fight
--------------------------------------------------------------------------------

function Q:TogglePreview()
    if not self.frame then return end
    if previewing then
        self:Clear()
        ns.Print("Upcoming list hidden.")
        return
    end

    wipe(entries)
    previewing = true

    local sample = ns.GetSpecCooldowns()
    local now = GetTime()
    for i = 1, 4 do
        local cd = sample[i]
        entries[i] = {
            key = "preview" .. i,
            fireAt = now + 3600 + i * 60,   -- far enough out that it never prunes
            reminder = {
                id = "preview" .. i,
                spellID = cd and cd.spellID,
                label = cd and cd.name or ("Cooldown " .. i),
                note = i == 1 and "drag to move" or "",
            },
        }
    end

    self.frame:Show()
    self:Refresh()
    ns.Print("Drag the list where you want it, then run /cadence queue again.")
end

function Q:IsPreviewing() return previewing end
