-- EmoteMenu.lua
-- Category-based UI with buttons for each emote

local ADDON_NAME = ...

-- Fallback data if EmoteMenuDB is not loaded for any reason
local DEFAULT_DATA = {
    categories = {
        { name = "General", emotes = { {cmd="wave",label="Wave"}, {cmd="cheer",label="Cheer"}, {cmd="clap",label="Clap"}, {cmd="dance",label="Dance"}, {cmd="laugh",label="Laugh"}, {cmd="salute",label="Salute"}, {cmd="shrug",label="Shrug"} } },
        { name = "Friendly", emotes = { {cmd="hug",label="Hug"}, {cmd="kiss",label="Kiss"}, {cmd="pat",label="Pat"}, {cmd="comfort",label="Comfort"}, {cmd="welcome",label="Welcome"} } },
        { name = "Expression", emotes = { {cmd="angry",label="Angry"}, {cmd="cry",label="Cry"}, {cmd="frown",label="Frown"}, {cmd="gasp",label="Gasp"}, {cmd="grin",label="Grin"}, {cmd="yawn",label="Yawn"} } },
    }
}

local DB = EmoteMenuDB or DEFAULT_DATA

-- Utility to run an emote command safely
local function RunEmote(cmd)
    if not cmd or cmd == "" then return end
    if DoEmote then
        DoEmote(cmd)
    else
        RunMacroText("/" .. cmd)
    end
end

-- Simple UI factory helpers
local ICONS_PATH = "Interface/AddOns/EmoteMenu/Icons/" -- safe literal path
local function TrySetIcon(texture, cmd)
    if not texture or not cmd then return false end
    cmd = cmd:lower()
    local ok = texture:SetTexture(ICONS_PATH .. cmd .. ".blp")
    if not ok then ok = texture:SetTexture(ICONS_PATH .. cmd .. ".tga") end
    return ok and true or false
end
local function CreateBackdrop(frame)
    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface/Tooltips/UI-Tooltip-Background",
            edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
            tile = true, tileSize = 16, edgeSize = 16,
            insets = { left = 4, right = 4, top = 4, bottom = 4 }
        })
        frame:SetBackdropColor(0, 0, 0, 0.9)
    end
end

local function SetDrag(frame)
    frame:EnableMouse(true)
    frame:SetMovable(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        self._moved = true
    end)
end

-- Forward declaration for Toggle so other helpers can reference it
local Toggle

-- Small launcher button above ChatFrameMenuButton
local function CreateLauncherButton()
    if _G.EmoteMenuLauncherButton then return end
    local anchor = _G.ChatFrameMenuButton
    local btn = CreateFrame("Button", "EmoteMenuLauncherButton", UIParent)
    btn:SetSize(24, 24)
    if anchor then
        btn:SetPoint("BOTTOM", anchor, "TOP", 0, 6)
        btn:SetFrameStrata("HIGH")
    else
        -- Fallback near ChatFrame1 if menu button is not found
        if _G.ChatFrame1 then
            btn:SetPoint("BOTTOMLEFT", _G.ChatFrame1, "TOPLEFT", 0, 6)
        else
            btn:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 10, 140)
        end
    end
    btn.icon = btn:CreateTexture(nil, "ARTWORK")
    btn.icon:SetAllPoints(true)
    if not TrySetIcon(btn.icon, "wave") then
        btn.icon:SetTexture("Interface/Icons/INV_Misc_QuestionMark")
        btn.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
    end
    btn:SetHighlightTexture("Interface/Buttons/ButtonHilight-Square", "ADD")
    btn:RegisterForClicks("LeftButtonUp")
    btn:SetScript("OnClick", function()
        if Toggle then Toggle() end
    end)
    btn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_TOP")
        GameTooltip:SetText("Emote Menu")
        GameTooltip:Show()
    end)
    btn:SetScript("OnLeave", function()
        if GameTooltip:IsShown() then GameTooltip:Hide() end
    end)
    btn:Show()
end

local SHOW_ALL_INDEX = 0
-- Quick toggle to show/hide the category sidebar. Set to true to show, false to hide.
local SHOW_CATEGORY_SIDEBAR = false
-- Base sizes
local FRAME_WIDTH_WITH_SIDEBAR = 600
local FRAME_WIDTH_NO_SIDEBAR = 430 -- narrower when sidebar hidden
local FRAME_HEIGHT = 620
local FAVORITES_INDEX = -1
local selectedCategory = SHOW_ALL_INDEX -- default to All (grouped)
local categoryButtons = {}
local emoteButtons = {}
local sectionHeaders = {}
local favorites = {}
local favoritesSet = {}
local otherCategoryIndex = nil

local function LoadFavorites()
    EmoteMenuSaved = EmoteMenuSaved or { favorites = {} }
    favorites = EmoteMenuSaved.favorites or {}
    favoritesSet = {}
    for _, cmd in ipairs(favorites) do favoritesSet[cmd] = true end
end

local function BuildOtherCategory()
    if not EmoteMenuAll or not EmoteMenuAll.emotes then return end
    -- Build a set of cmds already present in DB categories
    local present = {}
    for _, cat in ipairs(DB.categories or {}) do
        for _, e in ipairs(cat.emotes or {}) do
            present[e.cmd] = true
        end
    end
    local others = {}
    for _, e in ipairs(EmoteMenuAll.emotes) do
        if e.cmd and not present[e.cmd] then table.insert(others, e) end
    end
    -- Append or update the 'Other' category
    if otherCategoryIndex and DB.categories[otherCategoryIndex] then
        DB.categories[otherCategoryIndex].name = "Other"
        DB.categories[otherCategoryIndex].emotes = others
    else
        table.insert(DB.categories, { name = "Other", emotes = others })
        otherCategoryIndex = #DB.categories
    end
end

local function SaveFavorites()
    if not EmoteMenuSaved then EmoteMenuSaved = {} end
    -- dedupe and persist in stable order
    local seen = {}
    local out = {}
    for _, cmd in ipairs(favorites) do
        if not seen[cmd] then table.insert(out, cmd); seen[cmd] = true end
    end
    EmoteMenuSaved.favorites = out
    favorites = out
    favoritesSet = {}
    for _, cmd in ipairs(out) do favoritesSet[cmd] = true end
end

-- Main frame and sub-frames
local mainFrame
local rightPanelRef
local categoryScroll, categoryContent
local emoteScroll, emoteContent

local function RenderCategories()
    -- clear
    for _, b in ipairs(categoryButtons) do b:Hide() end
    wipe(categoryButtons)

    local y = -8
    -- 'All' option first
    do
        local btn = CreateFrame("Button", nil, categoryContent, "UIPanelButtonTemplate")
        btn:SetSize(100, 24)
        btn:SetPoint("TOPLEFT", 8, y)
        btn:SetText("All")
        btn:SetScript("OnClick", function()
            selectedCategory = SHOW_ALL_INDEX
            RenderCategories()
            mainFrame.RenderEmotes()
        end)
        if selectedCategory == SHOW_ALL_INDEX then btn:Disable() else btn:Enable() end
        table.insert(categoryButtons, btn)
        y = y - 28
    end

    -- Favorites category (visible always, but may be empty)
    do
        local btn = CreateFrame("Button", nil, categoryContent, "UIPanelButtonTemplate")
        btn:SetSize(100, 24)
        btn:SetPoint("TOPLEFT", 8, y)
        btn:SetText("Favorites")
        btn:SetScript("OnClick", function()
            selectedCategory = FAVORITES_INDEX
            RenderCategories()
            mainFrame.RenderEmotes()
        end)
        if selectedCategory == FAVORITES_INDEX then btn:Disable() else btn:Enable() end
        table.insert(categoryButtons, btn)
        y = y - 28
    end

    for i, cat in ipairs(DB.categories or {}) do
        local btn = CreateFrame("Button", nil, categoryContent, "UIPanelButtonTemplate")
        btn:SetSize(100, 24)
        btn:SetPoint("TOPLEFT", 8, y)
        btn:SetText(cat.name or ("Category %d"):format(i))
        btn:SetScript("OnClick", function()
            selectedCategory = i
            RenderCategories()
            -- refresh emote grid
            mainFrame.RenderEmotes()
        end)
        if i == selectedCategory then
            btn:Disable()
        else
            btn:Enable()
        end
        table.insert(categoryButtons, btn)
        y = y - 28
    end

    -- No scroll: fixed panel, no height change needed
end

local function GetCategoryEmotes()
    if selectedCategory == FAVORITES_INDEX then
        -- Build list from favoritesSet using DB order for stable ordering
        local list = {}
        for _, cat in ipairs(DB.categories or {}) do
            for _, e in ipairs(cat.emotes or {}) do
                if favoritesSet[e.cmd] then table.insert(list, e) end
            end
        end
        return list
    end
    local cat = (DB.categories and DB.categories[selectedCategory]) or (DEFAULT_DATA.categories and DEFAULT_DATA.categories[1])
    if not cat then return {} end
    return cat.emotes or {}
end

local function CreateOrGetEmoteButton(index)
    local btn = emoteButtons[index]
    if not btn then
    btn = CreateFrame("Button", nil, emoteContent, "BackdropTemplate")
    btn:SetFrameStrata("DIALOG")
        btn:SetSize(100, 28)
        CreateBackdrop(btn)
    btn.icon = btn:CreateTexture(nil, "ARTWORK")
        btn.icon:SetSize(32, 32)
        btn.icon:SetPoint("LEFT", 4, 0)
    btn.label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        btn.label:SetPoint("LEFT", btn.icon, "RIGHT", 6, 0)
        btn.label:SetPoint("RIGHT", -6, 0)
        btn.label:SetJustifyH("LEFT")
        btn:SetScript("OnEnter", function(self)
            if not self.tooltipText then return end
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(self.tooltipText)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function()
            if GameTooltip:IsOwned(btn) then GameTooltip:Hide() end
        end)
        emoteButtons[index] = btn
    end
    btn:Show()
    return btn
end

local function mainFrame_RenderEmotes()
    -- clear/hide existing
    for _, b in ipairs(emoteButtons) do b:Hide() end
    for _, h in ipairs(sectionHeaders) do h:Hide() end

        local spacingX, spacingY = 4, 4
        local startX, startY = 8, -8
        local btnW, btnH = 38, 38
        local panelWidth = rightPanelRef and rightPanelRef:GetWidth() or (SHOW_CATEGORY_SIDEBAR and 600 or 430) - 20
        local cols = math.max(3, math.floor((panelWidth - startX) / (btnW + spacingX)))

    local row, col = 0, 0
    local btnIndex = 0

    local function placeHeader(text)
        local idx
        for i, h in ipairs(sectionHeaders) do if not h:IsShown() then idx = i; break end end
        local hdr = idx and sectionHeaders[idx] or emoteContent:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
        if not idx then table.insert(sectionHeaders, hdr) end
        hdr:Show()
        hdr:ClearAllPoints()
        hdr:SetPoint("TOPLEFT", startX, startY - row * (btnH + spacingY) + 2)
        hdr:SetText(text)
        -- Use a fractional row advance so the gap to buttons is smaller
        local headerGapRows = 0.35 -- tweak for tighter/looser header spacing
        row = row + headerGapRows
        col = 0
    end

    local function placeButton(info)
        btnIndex = btnIndex + 1
        local btn = CreateOrGetEmoteButton(btnIndex)
        btn:SetSize(btnW, btnH)
        local x = startX + col * (btnW + spacingX)
        local y = startY - row * (btnH + spacingY)
        btn:ClearAllPoints()
        btn:SetPoint("TOPLEFT", x, y)
        local labelText = info.label or info.cmd
        btn.tooltipText = labelText
        local hasIcon = TrySetIcon(btn.icon, info.cmd)
        if not hasIcon then
            btn.icon:SetTexture(nil)
            -- fallback: center text if no icon
            btn.label:Show()
            btn.label:SetText(labelText)
            btn.label:ClearAllPoints()
            btn.label:SetPoint("CENTER")
        else
            -- icon-only mode; hide the label and keep tooltip
            btn.label:SetText("")
            btn.label:Hide()
        end
        -- Favorite state visuals: gold border if favorite
        if favoritesSet[info.cmd] then
            if btn.SetBackdropBorderColor then btn:SetBackdropBorderColor(1, 0.84, 0, 1) end -- gold
        else
            if btn.SetBackdropBorderColor then btn:SetBackdropBorderColor(0.6, 0.6, 0.6, 1) end -- silver
        end
        btn:SetScript("OnClick", function()
            RunEmote(info.cmd)
        end)
        btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
        btn:SetScript("OnMouseUp", function(self, mouseButton)
            if mouseButton == "RightButton" then
                -- toggle favorite
                if favoritesSet[info.cmd] then
                    favoritesSet[info.cmd] = nil
                    -- remove from favorites list
                    for i = #favorites, 1, -1 do if favorites[i] == info.cmd then table.remove(favorites, i) end end
                else
                    favoritesSet[info.cmd] = true
                    table.insert(favorites, info.cmd)
                end
                SaveFavorites()
                -- re-render to update borders and Favorites category
                mainFrame.RenderEmotes()
                RenderCategories()
            end
        end)
        col = col + 1
        if col >= cols then
            col = 0
            row = row + 1
        end
    end

    if selectedCategory == SHOW_ALL_INDEX then
        -- Grouped view: Favorites first (if any), then categories
        if #favorites > 0 then
            placeHeader("Favorites")
            local added = {}
            for _, cat in ipairs(DB.categories or {}) do
                for _, info in ipairs(cat.emotes or {}) do
                    if favoritesSet[info.cmd] and not added[info.cmd] then
                        placeButton(info)
                        added[info.cmd] = true
                    end
                end
            end
            if col > 0 then col = 0; row = row + 1 end
        end

        for i, cat in ipairs(DB.categories or {}) do
            if cat.emotes and #cat.emotes > 0 then
                placeHeader(cat.name or ("Category %d"):format(i))
                for _, info in ipairs(cat.emotes) do
                    -- Avoid duplicating favorites in the All view
                    if not favoritesSet[info.cmd] then
                        placeButton(info)
                    end
                end
                if col > 0 then -- move to next line between sections
                    col = 0
                    row = row + 1
                end
            end
        end
    elseif selectedCategory == FAVORITES_INDEX then
        -- Favorites view
        if #favorites == 0 then
            placeHeader("Favorites")
            btnIndex = btnIndex + 1
            local fs = emoteContent:CreateFontString(nil, "OVERLAY", "GameFontDisable")
            fs:SetPoint("TOPLEFT", startX, startY - row * (btnH + spacingY))
            fs:SetText("No favorites yet. Right-click an emote to add it.")
            table.insert(sectionHeaders, fs) -- reuse hide handling
            row = row + 1
        else
            for _, cat in ipairs(DB.categories or {}) do
                for _, info in ipairs(cat.emotes or {}) do
                    if favoritesSet[info.cmd] then placeButton(info) end
                end
            end
        end
    else
        -- Single category grid
        local emotes = GetCategoryEmotes()
        for _, info in ipairs(emotes) do
            placeButton(info)
        end
    end

    local totalRows = math.ceil(row + (col > 0 and 1 or 0))
    local contentW = math.max(1, (cols * btnW) + ((cols - 1) * spacingX) + 16)
    local contentH = math.max(1, totalRows * (btnH + spacingY) + 16)
    emoteContent:SetSize(contentW, contentH)
end

local function CreateUI()
    if mainFrame then return end
    -- Event registration moved to top level

    -- Main window
    mainFrame = CreateFrame("Frame", "EmoteMenuMainFrame", UIParent, "BackdropTemplate")
    local frameWidth = SHOW_CATEGORY_SIDEBAR and FRAME_WIDTH_WITH_SIDEBAR or FRAME_WIDTH_NO_SIDEBAR
    mainFrame:SetSize(frameWidth, FRAME_HEIGHT)
    mainFrame:SetPoint("CENTER")
    mainFrame:SetFrameStrata("DIALOG")
    mainFrame:SetToplevel(true)
    CreateBackdrop(mainFrame)
    SetDrag(mainFrame)
    mainFrame:Hide()

    -- Allow closing with ESC
    table.insert(UISpecialFrames, "EmoteMenuMainFrame")

    local title = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    title:SetPoint("TOPLEFT", 14, -12)
    title:SetText("Emote Menu")

    local close = CreateFrame("Button", nil, mainFrame, "UIPanelCloseButton")
    close:SetPoint("TOPRIGHT", 0, 0)

    -- Left: categories
    local leftPanel = CreateFrame("Frame", nil, mainFrame, "BackdropTemplate")
    leftPanel:SetFrameStrata("DIALOG")
    leftPanel:SetPoint("TOPLEFT", 10, -40)
    leftPanel:SetPoint("BOTTOMLEFT", 10, 10)
    leftPanel:SetWidth(160)
    CreateBackdrop(leftPanel)

    -- Static category content (no scroll)
    categoryContent = CreateFrame("Frame", nil, leftPanel)
    categoryContent:SetPoint("TOPLEFT", 4, -4)
    categoryContent:SetPoint("BOTTOMRIGHT", -4, 4)

    -- Right: emotes grid (scroll)
    local rightPanel = CreateFrame("Frame", nil, mainFrame, "BackdropTemplate")
    rightPanel:SetFrameStrata("DIALOG")
    if SHOW_CATEGORY_SIDEBAR then
        rightPanel:SetPoint("TOPLEFT", leftPanel, "TOPRIGHT", 10, 0)
    else
        leftPanel:Hide()
        rightPanel:SetPoint("TOPLEFT", 10, -40)
    end
    rightPanel:SetPoint("BOTTOMRIGHT", -10, 10)
    CreateBackdrop(rightPanel)
    rightPanelRef = rightPanel

    emoteScroll = CreateFrame("ScrollFrame", nil, rightPanel, "UIPanelScrollFrameTemplate")
    emoteScroll:SetFrameStrata("DIALOG")
    emoteScroll:SetPoint("TOPLEFT", 4, -4)
    emoteScroll:SetPoint("BOTTOMRIGHT", -26, 4)

    emoteContent = CreateFrame("Frame", nil, emoteScroll)
    emoteContent:SetSize(1, 1)
    emoteScroll:SetScrollChild(emoteContent)

    -- expose render so category click can call it
    mainFrame.RenderEmotes = mainFrame_RenderEmotes

    -- initial render
    LoadFavorites()
    BuildOtherCategory()
    if SHOW_CATEGORY_SIDEBAR then
        RenderCategories()
    end
    mainFrame.RenderEmotes()
end

-- Public open/toggle
local function PositionMenuDefault()
    -- Position bottom-left of menu at top-right of the launcher button
    local anchor = _G.EmoteMenuLauncherButton or _G.ChatFrameMenuButton or _G.ChatFrame1
    mainFrame:ClearAllPoints()
    if anchor == _G.ChatFrame1 then
        mainFrame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 10, 12)
    else
        mainFrame:SetPoint("BOTTOMLEFT", anchor, "TOPRIGHT", 6, 6)
    end
end

Toggle = function()
    if not mainFrame then CreateUI() end
    -- Ensure launcher exists for anchoring
    CreateLauncherButton()
    if not mainFrame._moved then
        PositionMenuDefault()
    end
    if mainFrame:IsShown() then mainFrame:Hide() else mainFrame:Show() end
end

SLASH_EMOTEMENU1 = "/emotemenu"
SlashCmdList["EMOTEMENU"] = function()
    Toggle()
end

-- Top-level init to ensure launcher appears even if menu hasn't been opened yet
local EmoteMenuInit = CreateFrame("Frame")
EmoteMenuInit:RegisterEvent("PLAYER_LOGIN")
EmoteMenuInit:RegisterEvent("PLAYER_ENTERING_WORLD")
EmoteMenuInit:SetScript("OnEvent", function(_, evt)
    if evt == "PLAYER_LOGIN" then
        if C_Timer and C_Timer.After then C_Timer.After(0.5, CreateLauncherButton) else CreateLauncherButton() end
        print("EmoteMenu loaded. Type /emotemenu to open.")
    elseif evt == "PLAYER_ENTERING_WORLD" then
        if C_Timer and C_Timer.After then C_Timer.After(2, function()
            CreateLauncherButton()
        end) else
            CreateLauncherButton()
        end
    end
end)
