-- Ticker.lua
-- Revised version to update timers using a looping period (1097 seconds)
-- When a timer’s remaining time would drop to zero, non-leader clients wrap the value 
-- using modulo arithmetic. This ensures that a looping timer (1097 seconds) never remains dead.
-- When not in a group, the addon shows the top six local timers. In a group,
-- non-leaders use synchronization from the leader via structured data.
if not TimerFrame or not TimerScrollFrame then return end

----------------------------
-- Utility and Formatting --
----------------------------
local function FormatTime(seconds)
    local mins = math.floor(seconds / 60)
    local secs = math.floor(seconds % 60)
    return string.format("%02d:%02d", mins, secs)
end

local zoneColors = {
    ["Azj-Kahet"]         = "|cffA330C9",
    ["Siren Isle"]        = "|cffC79C6E",
    ["Isle of Dorn"]      = "|cff40C7EB",
    ["The Ringing Deeps"] = "|cffC41F3B",
    ["Hallowfall"]        = "|cff00FF96",
    ["Undermine"]         = "|cffffa500",
}

----------------------------
-- Create Scrolling Ticker  --
----------------------------
TickerFrame = CreateFrame("Frame", "TickerFrame", TimerFrame)
local TickerText = TickerFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
TickerFrame:SetHeight(20)
TickerFrame:SetPoint("BOTTOM", TimerFrame, "BOTTOM", 0, 5)
TickerFrame:SetPoint("LEFT", TimerFrame, "LEFT", 5, 0)
TickerFrame:SetPoint("RIGHT", TimerFrame, "RIGHT", -5, 0)
TickerFrame:SetClipsChildren(true)
TickerFrame:SetFrameStrata("MEDIUM")
TickerFrame:SetFrameLevel(TimerFrame:GetFrameLevel() + 5)

TickerText:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE")
TickerText:SetTextColor(1, 1, 0, 1)
TickerText:SetJustifyH("LEFT")
TickerText:SetPoint("LEFT", TickerFrame, "RIGHT", 0, 0)

-----------------------------------------------
-- Scrolling and Ticker Text Refresh          --
-----------------------------------------------
local scrollTickerStarted = false
local scrollSpeed = 50
local tickerOffset = 0

-- For non-group members (or non-leaders in a group), store the structured timer data and sync timestamp.
local syncedTimerData = {}  -- Each entry: { zone, shardID, remaining }
local syncTimestamp = 0

-- Leader (or solo) display: Build a human-readable ticker string using local data.
local function GetTickerText()
    local displayText = ""
    local count = 0

    local scrollChild = TimerScrollFrame:GetScrollChild()
    if not scrollChild then
        return "TimerScrollFrame missing scroll child"
    end

    local timerFrames = { scrollChild:GetChildren() }
    if #timerFrames == 0 then
        return "No timer frames found"
    end

    table.sort(timerFrames, function(a, b)
        local aTop = a:GetTop() or 0
        local bTop = b:GetTop() or 0
        return aTop > bTop
    end)

    for i, timer in ipairs(timerFrames) do
        if timer:IsShown() then
            count = count + 1
            local zoneName = timer.zoneName or "Unknown"
            local shardID = timer.shardID or "?"
            local timeRemaining = timer.remainingSeconds or timer.duration or 0
            local colorCode = zoneColors[zoneName] or "|cffffffff"
            displayText = displayText .. string.format("%s%s|r (%s): %s   ", colorCode, zoneName, shardID, FormatTime(timeRemaining))
            if count >= 6 then break end
        end
    end

    if displayText == "" then displayText = "No active timers" end
    return displayText
end

-- Leader: Prepare a structured data string from the top six timers.
-- Format: "zone~shard~remaining;zone~shard~remaining;..."
local function GetTickerData()
    local dataEntries = {}
    local count = 0

    local scrollChild = TimerScrollFrame:GetScrollChild()
    if not scrollChild then
        return ""
    end

    local timerFrames = { scrollChild:GetChildren() }
    if #timerFrames == 0 then
        return ""
    end

    table.sort(timerFrames, function(a, b)
        local aTop = a:GetTop() or 0
        local bTop = b:GetTop() or 0
        return aTop > bTop
    end)

    for i, timer in ipairs(timerFrames) do
        if timer:IsShown() then
            count = count + 1
            local zoneName = timer.zoneName or "Unknown"
            local shardID = timer.shardID or "?"
            local timeRemaining = timer.remainingSeconds or timer.duration or 0
            table.insert(dataEntries, string.format("%s~%s~%d", zoneName, shardID, timeRemaining))
            if count >= 6 then break end
        end
    end

    return table.concat(dataEntries, ";")
end

-- Function to scroll the ticker text.
local function ScrollTickerText(self, elapsed)
    if not scrollTickerStarted then return end

    local frameWidth = TickerFrame:GetWidth()
    local textWidth = TickerText:GetStringWidth()

    tickerOffset = tickerOffset - (scrollSpeed * elapsed)
    if tickerOffset + textWidth < 0 then
        tickerOffset = frameWidth
    end

    TickerText:ClearAllPoints()
    TickerText:SetPoint("LEFT", TickerFrame, "LEFT", tickerOffset, 0)
end

TickerFrame:SetScript("OnUpdate", ScrollTickerText)

-- Initial setup: after a brief delay, show the proper ticker.
C_Timer.After(0.2, function()
    if UnitIsGroupLeader("player") or not IsInGroup() then
        TickerText:SetText(GetTickerText())
    else
        TickerText:SetText("Waiting for leader ticker...")
    end
    tickerOffset = TickerFrame:GetWidth()
    scrollTickerStarted = true
end)

-- Update the ticker display every 0.1 seconds.
-- For solo or leader clients, refresh the local ticker.
-- For non-leaders in a group, update using synced timer data with modulo arithmetic.
C_Timer.NewTicker(0.1, function()
    if scrollTickerStarted and TickerText then
        if UnitIsGroupLeader("player") or not IsInGroup() then
            TickerText:SetText(GetTickerText())
        else
            if #syncedTimerData > 0 then
                local elapsed = GetTime() - syncTimestamp
                local newDisplayText = ""
                for i, timer in ipairs(syncedTimerData) do
                    -- Use modulo 1097 so that if the timer "expires" it wraps around.
                    local updatedTime = (timer.remaining - elapsed) % 1097
                    local colorCode = zoneColors[timer.zone] or "|cffffffff"
                    newDisplayText = newDisplayText .. string.format("%s%s|r (%s): %s   ", colorCode, timer.zone, timer.shardID, FormatTime(updatedTime))
                end
                TickerText:SetText(newDisplayText)
            else
                TickerText:SetText("Waiting for leader ticker...")
            end
        end
    end
end)

-----------------------------------------------------------
-- Party/Raid Ticker Synchronization Mechanism         --
-- Using the Unique Prefix "RUFFTICKER"                  --
-----------------------------------------------------------
local TICKER_PREFIX = "RUFFTICKER"
C_ChatInfo.RegisterAddonMessagePrefix(TICKER_PREFIX)

local tickerEventFrame = CreateFrame("Frame")
tickerEventFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
tickerEventFrame:RegisterEvent("CHAT_MSG_ADDON")

tickerEventFrame:SetScript("OnEvent", function(self, event, arg1, arg2, arg3, ...)
    if event == "GROUP_ROSTER_UPDATE" then
        if IsInGroup() and not UnitIsGroupLeader("player") then
            local inRaid = IsInRaid()
            local inParty = IsInGroup()
            local channel = inRaid and "RAID" or (inParty and "PARTY" or nil)
            if channel then
                C_ChatInfo.SendAddonMessage(TICKER_PREFIX, "TICKER_REQUEST", channel)
            end
        end
    elseif event == "CHAT_MSG_ADDON" then
        local prefix, msg, channel, sender = arg1, arg2, arg3, ...
        if prefix ~= TICKER_PREFIX then return end

        if msg == "TICKER_REQUEST" then
            if UnitIsGroupLeader("player") then
                local tickerData = GetTickerData()
                C_ChatInfo.SendAddonMessage(TICKER_PREFIX, "TICKER_REPLY:" .. tickerData, channel)
            end
        elseif msg:sub(1,13) == "TICKER_REPLY:" then
            if IsInGroup() and not UnitIsGroupLeader("player") then
                local dataString = msg:sub(14)
                syncTimestamp = GetTime()  -- Record the time of sync.
                syncedTimerData = {}
                if dataString and dataString ~= "" then
                    -- Data string format: "zone~shard~remaining;zone~shard~remaining;..."
                    for entry in string.gmatch(dataString, "([^;]+)") do
                        local zone, shard, remaining = entry:match("([^~]+)~([^~]+)~(%d+)")
                        if zone and shard and remaining then
                            table.insert(syncedTimerData, {
                                zone = zone,
                                shardID = shard,
                                remaining = tonumber(remaining)
                            })
                        end
                    end
                end
            end
        end
    end
end)

-- For non-leaders in a group, periodically request the leader's ticker every 3 seconds.
if IsInGroup() and not UnitIsGroupLeader("player") then
    C_Timer.NewTicker(3, function()
        local inRaid = IsInRaid()
        local inParty = IsInGroup()
        local channel = inRaid and "RAID" or (inParty and "PARTY" or nil)
        if channel then
            C_ChatInfo.SendAddonMessage(TICKER_PREFIX, "TICKER_REQUEST", channel)
        end
    end)
end

-- Function to update the tooltip with general information
local function UpdateTickerTooltip()
    GameTooltip:SetOwner(TickerFrame, "ANCHOR_TOP")
    GameTooltip:ClearLines()

    if UnitIsGroupLeader("player") or not IsInGroup() then
        GameTooltip:AddLine("Top 6 Timers")
        GameTooltip:AddLine("Your Top 6 Timers Are Being Displayed For Everyone In The Group", 1, 1, 0) -- Yellow text
    else
        if #syncedTimerData > 0 then
            GameTooltip:AddLine("Top 6 Timers\n(From Leader):")
        else
            GameTooltip:AddLine("Waiting for leader ticker...", 1, 0, 0)
        end
    end

    GameTooltip:Show()
end

-- Add tooltip handlers to the TickerFrame
TickerFrame:SetScript("OnEnter", function()
    UpdateTickerTooltip()
end)

TickerFrame:SetScript("OnLeave", function()
    GameTooltip:Hide()
end)
