if QueueQueue then return end
QueueQueue = {
    ["name"] = "QueueQueue",
    ["version"] = 1.0,
    ["defaults"] = {
        ["indicator"] = {
            ["x"] = 410,
            ["y"] = 30,
        },
        ["sound"] = true,
    },
    ["queues"] = {},
    ["lastUpdate"] = 0,
}
local QueueQueue = QueueQueue

function QueueQueue.OnCampaignQueueJoined(event, campaignId, isGroup)
    QueueQueue.SyncOneCampaignQueue(campaignId, isGroup)
end

function QueueQueue.OnCampaignQueueLeft(event, campaignId, isGroup)
    QueueQueue.queues[campaignId] = nil
end

function QueueQueue.OnCampaignQueuePositionChanged(event, campaignId, isGroup, position)
    QueueQueue.queues[campaignId].position = position
end

function QueueQueue.OnCampaignQueueStateChanged(event, campaignId, isGroup, state)
    QueueQueue.queues[campaignId].state = state
    -- If entry just became available, play a very noticeable sound.
    if state == CAMPAIGN_QUEUE_REQUEST_STATE_CONFIRMING and QueueQueue.savedVariables.sound then
        PlaySound(SOUNDS.LEVEL_UP)
    end
end

function QueueQueue.SyncCampaignQueues()
    local queueCount = GetNumCampaignQueueEntries()
    QueueQueue.queues = {}
    for i=1,queueCount do
        QueueQueue.SyncOneCampaignQueue(GetCampaignQueueEntry(i))
    end
end

function QueueQueue.SyncOneCampaignQueue(campaignId, isGroup)
    QueueQueue.queues[campaignId] = {
        ["isGroup"] = isGroup,
        ["name"] = GetCampaignName(campaignId),
        ["position"] = GetCampaignQueuePosition(campaignId, isGroup),
        ["state"] = GetCampaignQueueState(campaignId, isGroup),
    }
end

function QueueQueue.FormatTime(seconds)
    local hours = math.floor(seconds / 3600)
    local minutes = math.floor((seconds % 3600) / 60)
    seconds = seconds % 60
    if hours > 0 then
        return string.format("%d:%02d:%02d", hours, minutes, seconds)
    else
        return string.format("%d:%02d", minutes, seconds)
    end
end

function QueueQueue.UpdateIndicator()
    local campaignId, queue = next(QueueQueue.queues)
    if queue then
        -- Display the campaign title
        QueueQueueIndicatorTitle:SetText(queue.name .. (queue.isGroup and " [Group]" or " [Solo]"))

        -- Display color-coded position indicator
        if queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_CONFIRMING then
            QueueQueueIndicatorPosition:SetText("|c00FF00Ready!")
        elseif queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_PENDING_ACCEPT then
            QueueQueueIndicatorPosition:SetText("|c00FF00Entering")
        elseif queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_PENDING_JOIN then
            QueueQueueIndicatorPosition:SetText("|cEEEEEEQueueing")
        elseif queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_PENDING_LEAVE then
            QueueQueueIndicatorPosition:SetText("|cEEEEEELeaving")
        elseif queue.position > 25 then
            QueueQueueIndicatorPosition:SetText("|cEEEEEE" .. queue.position)
        elseif queue.position > 10 then
            QueueQueueIndicatorPosition:SetText("|cFFFF00" .. queue.position)
        elseif queue.position > 0 then
            QueueQueueIndicatorPosition:SetText("|cFF8800" .. queue.position)
        else
            QueueQueueIndicatorPosition:SetText("?")
        end

        -- Display time spent in queue, if waiting...
        if queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_WAITING then
            local seconds = GetSecondsInCampaignQueue(campaignId, queue.isGroup)
            QueueQueueIndicatorTime:SetText("|cCCCCCCQueued - " .. QueueQueue.FormatTime(seconds))
        -- ...or time left to join, if available.
        elseif queue.state == CAMPAIGN_QUEUE_REQUEST_STATE_CONFIRMING then
            local seconds = GetCampaignQueueRemainingConfirmationSeconds(campaignId, queue.isGroup)
            -- Play discrete sound each second for the last 10 seconds of acceptance period.
            if seconds <= 10 and seconds > 0 and QueueQueue.savedVariables.sound then
                PlaySound(SOUNDS.GENERAL_ALERT_ERROR)
            end
            QueueQueueIndicatorTime:SetText(QueueQueue.FormatTime(seconds))
        else
            QueueQueueIndicatorTime:SetText("")
        end
    else
        QueueQueueIndicatorTitle:SetText("")
        QueueQueueIndicatorPosition:SetText("")
        QueueQueueIndicatorTime:SetText("")
    end
end

function QueueQueue.OnUpdate()
    local now = GetSecondsSinceMidnight()
    if now ~= QueueQueue.lastUpdate then
        QueueQueue.lastUpdate = now
        QueueQueue.UpdateIndicator()
    end
end

function QueueQueue.OnIndicatorMoveStart()
    QueueQueueIndicatorBackdrop:SetAlpha(0.3)
end

function QueueQueue.OnIndicatorMoveStop()
    QueueQueueIndicatorBackdrop:SetAlpha(0.0)
    QueueQueue.savedVariables.indicator.x = QueueQueueIndicator:GetLeft()
    QueueQueue.savedVariables.indicator.y = QueueQueueIndicator:GetTop()
end

function QueueQueue.RestoreIndicatorPosition()
    local indicator = QueueQueue.savedVariables.indicator
    QueueQueueIndicator:ClearAnchors()
	QueueQueueIndicator:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, indicator.x, indicator.y)
end

function QueueQueue.RegisterEvents()
    EVENT_MANAGER:RegisterForEvent(QueueQueue.name, EVENT_CAMPAIGN_QUEUE_JOINED, QueueQueue.OnCampaignQueueJoined)
    EVENT_MANAGER:RegisterForEvent(QueueQueue.name, EVENT_CAMPAIGN_QUEUE_LEFT, QueueQueue.OnCampaignQueueLeft)
    EVENT_MANAGER:RegisterForEvent(QueueQueue.name, EVENT_CAMPAIGN_QUEUE_POSITION_CHANGED, QueueQueue.OnCampaignQueuePositionChanged)
    EVENT_MANAGER:RegisterForEvent(QueueQueue.name, EVENT_CAMPAIGN_QUEUE_STATE_CHANGED, QueueQueue.OnCampaignQueueStateChanged)
end

function QueueQueue.Initialize()
    QueueQueue.savedVariables = ZO_SavedVars:NewAccountWide("QueueQueueSavedVariables", 1, nil, QueueQueue.defaults)
    QueueQueue.RestoreIndicatorPosition()
    QueueQueue.SyncCampaignQueues()
    QueueQueue.RegisterEvents()
end

function QueueQueue.OnAddOnLoaded(event, addonName)
    if addonName == QueueQueue.name then
        QueueQueue.Initialize()
    end
end

EVENT_MANAGER:RegisterForEvent(QueueQueue.name, EVENT_ADD_ON_LOADED, QueueQueue.OnAddOnLoaded)