local _, ns = ...

------------------------------------------------------------
-- L'ura Memory-Game Rune Display
-- Raid leader / tanks / healers press macros that do
-- `/raid YYR<1-5>`; every YYH client parses CHAT_MSG_RAID(_LEADER)
-- and displays the matching raid-marker icon in a pentagon frame.
--
-- We use raid-chat (not addon comms) because Blizzard blocks many
-- addon actions in combat; chat macros remain reliable.
------------------------------------------------------------

local RUNE_COUNT = 5
local previewActive = false  -- forward declaration (assigned in SetPreview)

-- Bundled display icons — the only textures used by the pentagon display.
-- No per-user customization; the 5 symbols are part of the addon's visual
-- identity and consistent across every raider's screen.
local TEX = "Interface\\AddOns\\YippYappHelper\\Media\\Textures\\"
local DISPLAY_ICONS = {
    TEX .. "symbol_circle",
    TEX .. "symbol_diamond",
    TEX .. "symbol_triangle",
    TEX .. "symbol_t",
    TEX .. "symbol_x",
}

local function ResolveIcon(slotIdx)
    return DISPLAY_ICONS[slotIdx]
end

-- Pentagon layout (copied from NSRT's geometry so it reads naturally)
local X_OFFSET = { 60, 60, 0, -60, -60 }
local Y_OFFSET = { 45, -30, -75, -30, 45 }

local AUTO_HIDE_SECONDS = 12
local MACRO_PREFIX = "YYH_RUNE_"
local CHAT_TOKEN = "YYR"  -- `/raid YYR1` etc.

------------------------------------------------------------
-- SavedVariables init
------------------------------------------------------------
local function DB()
    YippYappHelperDB = YippYappHelperDB or {}
    YippYappHelperDB.luraRunes = YippYappHelperDB.luraRunes or {
        enabled = true,
        point = "CENTER", relPoint = "CENTER", x = -400, y = 220,
        locked = true,
    }
    local d = YippYappHelperDB.luraRunes
    -- Clean up legacy per-slot icon picker data from 2.0 betas.
    d.icons = nil
    d.iconsVersion = nil
    d.mythicMode = nil
    d.enabled = d.enabled ~= false  -- default on for fresh and migrated installs
    return d
end

------------------------------------------------------------
-- Display frame
------------------------------------------------------------
local display = CreateFrame("Frame", "YippYappLuraRunes", UIParent, "BackdropTemplate")
display:SetSize(220, 220)
display:SetFrameStrata("HIGH")
display:SetClampedToScreen(true)
display:Hide()

display:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    edgeSize = 10,
    insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
display:SetBackdropColor(0.05, 0.05, 0.05, 0.75)
display:SetBackdropBorderColor(0.2, 0.2, 0.2, 0.8)

local header = display:CreateFontString(nil, "OVERLAY", "GameFontNormal")
header:SetPoint("TOP", 0, -6)
header:SetText("L'ura Runes")

local slots = {}
for i = 1, RUNE_COUNT do
    local icon = display:CreateTexture(nil, "ARTWORK")
    icon:SetSize(48, 48)
    icon:SetPoint("CENTER", display, "CENTER", X_OFFSET[i], Y_OFFSET[i])
    icon:Hide()

    local number = display:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    number:SetPoint("CENTER", icon, "CENTER", 0, 32)
    number:SetText(tostring(i))
    number:Hide()

    slots[i] = { icon = icon, number = number, filled = false }
end

local function ApplyPosition()
    local db = DB()
    display:ClearAllPoints()
    display:SetPoint(db.point or "CENTER", UIParent, db.relPoint or "CENTER", db.x or 0, db.y or 0)
end

display:SetMovable(true)
display:RegisterForDrag("LeftButton")
display:SetScript("OnDragStart", function(self)
    if DB().locked then return end
    self:StartMoving()
end)
display:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local db = DB()
    local point, _, relPoint, x, y = self:GetPoint()
    db.point, db.relPoint, db.x, db.y = point, relPoint, x, y
end)

local unlockHint = display:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
unlockHint:SetPoint("BOTTOM", 0, 6)
unlockHint:SetText("|cffffff00drag to move|r")
unlockHint:Hide()

local function SetLocked(locked)
    DB().locked = locked and true or false
    display:EnableMouse(not locked)
    if locked then
        unlockHint:Hide()
    else
        unlockHint:Show()
        display:Show()
    end
end

------------------------------------------------------------
-- Slot logic (mirrors NSRT assignment so two pressers don't collide)
--  RAID_LEADER channel -> slot 1, then 4
--  RAID channel       -> slot 2, then 3, then 5
------------------------------------------------------------
local LEADER_ORDER = { 1, 4 }
local MEMBER_ORDER = { 2, 3, 5 }

-- Heroic mode: tracks how many runes have been pressed this round, so
-- the next press fills the next-visible slot left-to-right.
local nextHeroicSlot = 1

local function ClearSlots()
    for i = 1, RUNE_COUNT do
        slots[i].icon:Hide()
        slots[i].number:Hide()
        slots[i].filled = false
    end
    nextHeroicSlot = 1
end

local hideTimer
local function ScheduleHide()
    if hideTimer then hideTimer:Cancel() end
    hideTimer = C_Timer.NewTimer(AUTO_HIDE_SECONDS, function()
        ClearSlots()
        display:Hide()
    end)
end

local function FillSlot(pos, runeIdx)
    local s = slots[pos]
    s.icon:SetTexture(ResolveIcon(runeIdx))
    s.icon:Show()
    s.number:Show()
    s.filled = true
end

local function NextOpenSlot(order)
    for _, p in ipairs(order) do
        if not slots[p].filled then return p end
    end
    return order[#order]  -- fallback: overwrite last
end

local function DisplayRune(runeIdx, fromLeader)
    if runeIdx < 1 or runeIdx > RUNE_COUNT then return end
    display:Show()
    -- Slot = press order. 1st press fills slot 1, regardless of which rune
    -- was pressed. Works for both heroic (1 clicker) and mythic (multiple
    -- clickers) since the fight just asks for the observed sequence.
    local pos = nextHeroicSlot
    nextHeroicSlot = nextHeroicSlot + 1
    if nextHeroicSlot > RUNE_COUNT then nextHeroicSlot = 1 end
    FillSlot(pos, runeIdx)
    ScheduleHide()
end

------------------------------------------------------------
-- Chat listener
------------------------------------------------------------
local listener = CreateFrame("Frame")
local TOKEN_PATTERN = "^" .. CHAT_TOKEN .. "([1-5])$"

listener:SetScript("OnEvent", function(_, event, msg)
    if not msg then return end
    local n = msg:match(TOKEN_PATTERN)
    if not n then return end
    DisplayRune(tonumber(n), event == "CHAT_MSG_RAID_LEADER")
end)

local function SetEnabled(enabled)
    DB().enabled = enabled and true or false
    if enabled then
        -- Only raid leaders/assists can meaningfully drive the display —
        -- their /raid messages fire CHAT_MSG_RAID_LEADER. A regular
        -- raider's /raid goes to CHAT_MSG_RAID, which we deliberately
        -- ignore so random pressers can't spam the pentagon.
        listener:RegisterEvent("CHAT_MSG_RAID_LEADER")
    else
        listener:UnregisterAllEvents()
        ClearSlots()
        display:Hide()
    end
end

------------------------------------------------------------
-- Macro creator
------------------------------------------------------------
-- Numbered macro names. Action-bar icon is intentionally blank (question
-- mark) so the raid leader can manually pick the matching rune symbol
-- from the macro-icon browser after installing the bundled TGAs.
local MACRO_NAMES = { "YY_1", "YY_2", "YY_3", "YY_4", "YY_5" }
local MACRO_ICON_NAMES = { "symbol_circle", "symbol_diamond", "symbol_triangle", "symbol_t", "symbol_x" }
local BLANK_MACRO_ICON = "INV_Misc_QuestionMark"

local function CreateRuneMacros()
    if InCombatLockdown() then
        print("|cffff4444YippYapp:|r cannot create macros in combat.")
        return 0, 0
    end

    -- Remove legacy macros from previous naming schemes so the list
    -- doesn't accumulate stale entries.
    local LEGACY_NAMES = { "O", "D", "G", "T", "X" }
    for i = 1, RUNE_COUNT do
        for _, legacy in ipairs({ "YYH_RUNE_" .. i, LEGACY_NAMES[i] }) do
            if GetMacroInfo(legacy) then DeleteMacro(legacy) end
        end
    end

    local created, existed = 0, 0
    for i = 1, RUNE_COUNT do
        local name = MACRO_NAMES[i]
        local body = ("/raid %s%d"):format(CHAT_TOKEN, i)
        -- Icon intentionally left as the default question-mark. The raid
        -- leader manually picks the matching rune symbol from the icon
        -- browser after copying our bundled TGAs into Interface\ICONS\.
        if GetMacroInfo(name) then
            EditMacro(name, name, BLANK_MACRO_ICON, body)
            existed = existed + 1
        else
            local id = CreateMacro(name, BLANK_MACRO_ICON, body, false)
            if id then created = created + 1 end
        end
    end
    return created, existed
end

------------------------------------------------------------
-- Preview mode (show all 5 slots with markers)
------------------------------------------------------------
local function SetPreview(on)
    previewActive = on and true or false
    if on then
        ClearSlots()
        for i = 1, RUNE_COUNT do FillSlot(i, i) end
        display:Show()
        if hideTimer then hideTimer:Cancel() end
    else
        ClearSlots()
        display:Hide()
    end
end

------------------------------------------------------------
-- Public API + tab renderer
------------------------------------------------------------
------------------------------------------------------------
-- Simulation (runs the same path as a real CHAT_MSG)
------------------------------------------------------------
local simTimers = {}

local function CancelSim()
    for _, t in ipairs(simTimers) do
        if t and t.Cancel then t:Cancel() end
    end
    wipe(simTimers)
end

local function ClearDisplay()
    CancelSim()
    if hideTimer then hideTimer:Cancel(); hideTimer = nil end
    ClearSlots()
    display:Hide()
end

local function TriggerRune(runeIdx, fromLeader)
    if previewActive then SetPreview(false) end
    DisplayRune(runeIdx, fromLeader)
end

-- Fires a plausible Mythic sequence (lead covers 1 & 4, raid covers 2, 3, 5).
local function SimulateSequence()
    if previewActive then SetPreview(false) end
    ClearSlots()
    display:Show()
    CancelSim()
    local steps = {
        { 0.0, 1, true  },
        { 0.9, 2, false },
        { 1.8, 3, false },
        { 2.7, 4, true  },
        { 3.6, 5, false },
    }
    for _, s in ipairs(steps) do
        local delay, rune, lead = s[1], s[2], s[3]
        table.insert(simTimers, C_Timer.NewTimer(delay, function()
            DisplayRune(rune, lead)
        end))
    end
end

ns.LuraRunes = {
    IsEnabled   = function() return DB().enabled end,
    SetEnabled  = SetEnabled,
    CreateMacros = CreateRuneMacros,
    SetPreview  = SetPreview,
    IsPreview   = function() return previewActive end,
    IsLocked    = function() return DB().locked end,
    SetLocked   = SetLocked,
    TriggerRune = TriggerRune,
    SimulateSequence = SimulateSequence,
    ClearDisplay = ClearDisplay,
    Token       = CHAT_TOKEN,
    MacroPrefix = MACRO_PREFIX,
}

------------------------------------------------------------
-- Init on login
------------------------------------------------------------
local init = CreateFrame("Frame")
init:RegisterEvent("PLAYER_LOGIN")
init:SetScript("OnEvent", function(self)
    self:UnregisterAllEvents()
    local db = DB()
    ApplyPosition()
    display:EnableMouse(not db.locked)
    if db.enabled then SetEnabled(true) end
end)

------------------------------------------------------------
-- Tab UI builder (called from RaidUI.lua)
------------------------------------------------------------
local function MakeButton(parent, label, w, onClick)
    local b = CreateFrame("Button", nil, parent, "BackdropTemplate")
    b:SetSize(w or 160, 22)
    b:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 8,
        insets   = { left = 2, right = 2, top = 2, bottom = 2 },
    })
    b:SetBackdropColor(0.08, 0.08, 0.08, 0.9)
    b:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
    if ns.SmoothFrame then ns.SmoothFrame(b) end
    if ns.AddGlowHighlight then ns.AddGlowHighlight(b, 0.08) end

    local fs = b:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    fs:SetPoint("CENTER")
    fs:SetText(label)
    if ns.ApplyTextShadow then ns.ApplyTextShadow(fs) end
    b._label = fs

    b:SetScript("OnEnter", function(self) self:SetBackdropBorderColor(0.0, 0.8, 1.0, 0.6) end)
    b:SetScript("OnLeave", function(self) self:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6) end)
    b:SetScript("OnClick", onClick)
    return b
end

local function MakeCheckbox(parent, label, getFn, setFn)
    local cb = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    cb.text = cb.text or _G[cb:GetName() and (cb:GetName().."Text") or ""]
    local fs = cb:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    fs:SetPoint("LEFT", cb, "RIGHT", 2, 0)
    fs:SetText(label)
    cb:SetChecked(getFn())
    cb:SetScript("OnClick", function(self) setFn(self:GetChecked()) end)
    cb._refresh = function() cb:SetChecked(getFn()) end
    return cb
end

------------------------------------------------------------
-- Tab layout helpers
------------------------------------------------------------
local function MakeSectionHeader(parent, text, y, accent)
    local fs = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    fs:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, y)
    fs:SetText(text)
    fs:SetTextColor(0.85, 0.85, 0.85)
    if ns.ApplyTextShadow then ns.ApplyTextShadow(fs) end

    local underline = parent:CreateTexture(nil, "ARTWORK")
    underline:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, y - 16)
    underline:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, y - 16)
    underline:SetHeight(1)
    local r, g, b = accent and accent[1] or 0.3, accent and accent[2] or 0.3, accent and accent[3] or 0.3
    underline:SetColorTexture(r, g, b, 0.6)

    return fs, underline
end

------------------------------------------------------------
-- Tab: full-width sections with 2-column inner layouts
------------------------------------------------------------
function ns.BuildLuraRunesTab(container)
    local PAD = 14

    --------------------------------------------------------
    -- Header block: title + description across full width
    --------------------------------------------------------
    local title = container:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", PAD, -14)
    title:SetText("L'ura Memory-Game Runes")

    local desc = container:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    desc:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -6)
    desc:SetPoint("RIGHT", container, "RIGHT", -PAD, 0)
    desc:SetJustifyH("LEFT")
    desc:SetSpacing(2)
    desc:SetText(
        "The boss shows a sequence of 5 runes. The clicker presses the macros "
        .. "(|cffffff00YY_1|r-|cffffff00YY_5|r) in that order. Each press sends "
        .. "|cffffff00YYR<n>|r in raid chat; every YippYapp viewer sees the icon "
        .. "appear in the next pentagon slot, right to left. "
        .. "|cffaaaaaaWorks for heroic (solo clicker) and mythic (any clicker).|r"
    )

    local y = -14 - title:GetStringHeight() - desc:GetStringHeight() - 18

    --------------------------------------------------------
    -- Section 1: Settings + Simulation (two columns side by side)
    --------------------------------------------------------
    MakeSectionHeader(container, "Settings & Simulation", y, { 0.4, 0.8, 1.0 })
    y = y - 24

    -- Left half: checkboxes
    local settingsX = PAD + 4
    local enableCB = MakeCheckbox(container, "Show rune display during raid",
        function() return DB().enabled end,
        function(v) SetEnabled(v) end)
    enableCB:SetPoint("TOPLEFT", settingsX, y)

    local previewCB = MakeCheckbox(container, "Preview display (show all 5)",
        function() return previewActive end,
        function(v) SetPreview(v) end)
    previewCB:SetPoint("TOPLEFT", settingsX, y - 26)

    local lockCB = MakeCheckbox(container, "Lock display position",
        function() return DB().locked end,
        function(v) SetLocked(v) end)
    lockCB:SetPoint("TOPLEFT", settingsX, y - 52)

    -- Right half: simulation buttons
    local simX = 300  -- right-half origin
    local simLabel = container:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    simLabel:SetPoint("TOPLEFT", simX, y - 4)
    simLabel:SetText("Simulation (no raid required):")

    local seqBtn = MakeButton(container, "Play sequence", 130, function() SimulateSequence() end)
    seqBtn:SetPoint("TOPLEFT", simX, y - 22)

    local clearBtn = MakeButton(container, "Clear", 70, function() ClearDisplay() end)
    clearBtn:SetPoint("LEFT", seqBtn, "RIGHT", 8, 0)

    local runeLabel = container:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    runeLabel:SetPoint("TOPLEFT", simX, y - 52)
    runeLabel:SetText("Fire individual rune:")

    local bx = simX
    for i = 1, RUNE_COUNT do
        local lead = (i == 1 or i == 4)
        local b = MakeButton(container, tostring(i), 38, function() TriggerRune(i, lead) end)
        b:SetPoint("TOPLEFT", bx, y - 70)
        bx = bx + 42
    end
    y = y - 108

    --------------------------------------------------------
    -- Section 2: Macros
    --------------------------------------------------------
    MakeSectionHeader(container, "Raid Leader Macros", y, { 0.9, 0.65, 0.2 })
    y = y - 24

    local macroBtn = MakeButton(container, "Create Macros", 150, function()
        local created, existed = CreateRuneMacros()
        print(("|cff00aaffYippYapp:|r %d macro(s) created, %d updated. Drag YY_1 - YY_5 to your bars."):format(
            created, existed))
    end)
    macroBtn:SetPoint("TOPLEFT", PAD + 4, y)

    local macroHint = container:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    macroHint:SetPoint("LEFT", macroBtn, "RIGHT", 10, 0)
    macroHint:SetText("Creates / updates 5 macros: |cffffff00YY_1|r - |cffffff00YY_5|r  (blank icon)")
    y = y - 36

    -- Two columns under the macros section:
    --   Left: install guide text
    --   Right: reference table (icon → macro number → filename)
    local guideHdr = container:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    guideHdr:SetPoint("TOPLEFT", PAD + 4, y)
    guideHdr:SetText("|cffffc840Add custom icons to the action bar:|r")

    local guide = container:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    guide:SetPoint("TOPLEFT", PAD + 4, y - 18)
    guide:SetWidth(280)
    guide:SetJustifyH("LEFT")
    guide:SetSpacing(3)
    guide:SetText(
        "|cffffff001.|r Copy the 5 |cffffff00symbol_*.tga|r files from:\n"
        .. "   |cffaaaaaaAddOns\\YippYappHelper\\Media\\Textures\\|r\n"
        .. "   into:  |cffaaaaaaInterface\\ICONS\\|r\n"
        .. "|cffffff002.|r Fully restart WoW (not /reload).\n"
        .. "|cffffff003.|r Edit each macro, click its icon, and open the\n"
        .. "   |cffffff00Items|r tab in the icon selector. Pick the symbol\n"
        .. "   that matches its macro number (see the list to the right)."
    )

    -- Reference column on the right
    local refX = 300
    local refHdr = container:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    refHdr:SetPoint("TOPLEFT", refX, y)
    refHdr:SetText("|cffffc840Pick this icon for each macro:|r")

    local REFERENCE = {
        { num = 1, icon = TEX .. "symbol_circle",   file = "symbol_circle" },
        { num = 2, icon = TEX .. "symbol_diamond",  file = "symbol_diamond" },
        { num = 3, icon = TEX .. "symbol_triangle", file = "symbol_triangle" },
        { num = 4, icon = TEX .. "symbol_t",        file = "symbol_t" },
        { num = 5, icon = TEX .. "symbol_x",        file = "symbol_x" },
    }
    local rowY = y - 20
    for _, r in ipairs(REFERENCE) do
        local numFs = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        numFs:SetPoint("TOPLEFT", refX, rowY)
        numFs:SetWidth(46)
        numFs:SetText("|cffffffffYY_" .. r.num .. "|r")

        local tex = container:CreateTexture(nil, "ARTWORK")
        tex:SetSize(20, 20)
        tex:SetPoint("TOPLEFT", refX + 48, rowY + 2)
        tex:SetTexture(r.icon)
        tex:SetTexCoord(0.08, 0.92, 0.08, 0.92)

        local fileFs = container:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        fileFs:SetPoint("TOPLEFT", refX + 76, rowY)
        fileFs:SetText("|cffaaaaaa" .. r.file .. "|r")

        rowY = rowY - 22
    end

    container:HookScript("OnShow", function()
        enableCB._refresh()
        previewCB._refresh()
        lockCB._refresh()
    end)
end
