--Shared Trade Window Prototype
ZO_WorldMapQuests_Shared = ZO_InitializingObject:Subclass()

function ZO_WorldMapQuests_Shared:Initialize(control)
    self.control = control

    self.data = ZO_WorldMapQuestsData_Singleton_Initialize(control)

    control:RegisterForEvent(EVENT_LEVEL_UPDATE, function(eventCode, unitTag)
        if unitTag == "player" then
            self:RefreshHeaders()
        end
    end)

    local function LayoutList(forceLayout)
        if not self.control:IsHidden() or forceLayout then
            self:LayoutList()
        end
    end

    CALLBACK_MANAGER:RegisterCallback("OnWorldMapQuestsDataRefresh", LayoutList)
    CALLBACK_MANAGER:RegisterCallback("OnWorldMapChanged", function() self:RefreshNoQuestsLabel() end)
end

function ZO_WorldMapQuests_Shared:RefreshNoQuestsLabel()
    if #self.data.masterList > 0 or self.pendingQuestIndex ~= nil then
        self.noQuestsLabel:SetHidden(true)
    else
        self.noQuestsLabel:SetHidden(false)
        if ZO_WorldMapQuestsData_Singleton.ShouldMapShowQuestsInList() then
            self.noQuestsLabel:SetText(GetString(SI_WORLD_MAP_NO_QUESTS))
        else
            self.noQuestsLabel:SetText(GetString(SI_WORLD_MAP_DOESNT_SHOW_QUESTS_DISTANCE))
        end
    end
end

-- Singleton shared data
ZO_WorldMapQuestsData_Singleton = ZO_Object:Subclass()

function ZO_WorldMapQuestsData_Singleton:New(...)
    local object = ZO_Object.New(self)
    object:Initialize(...)
    return object
end

function ZO_WorldMapQuestsData_Singleton:Initialize(control)
    self.listDirty = false
    self.masterList = {}

    self.CompareQuests = function(a, b)
        local aCon = GetCon(a.level)
        local bCon = GetCon(b.level)
        if(aCon == bCon) then
            return a.name < b.name
        else
            return aCon < bCon
        end
    end

    WORLD_MAP_QUEST_BREADCRUMBS:RegisterCallback("QuestAvailable", function(...) self:OnQuestAvailable(...) end)
    WORLD_MAP_QUEST_BREADCRUMBS:RegisterCallback("QuestRemoved", function(...) self:OnQuestRemoved(...) end)
    EVENT_MANAGER:RegisterForUpdate("ZO_WorldMapQuestsData_Singleton", 100, function()
        if self.listDirty then
            self.listDirty = false
            self:LayoutList(true)
        end
    end)
end

function ZO_WorldMapQuestsData_Singleton.ShouldMapShowQuestsInList()
    return true
end

function ZO_WorldMapQuestsData_Singleton.IsQuestInCurrentMapNormalizedBounds(questSteps)
    for stepIndex, questConditions in pairs(questSteps) do
        for conditionIndex, conditionData in pairs(questConditions) do
            if conditionData.xLoc >= 0 and conditionData.xLoc <= 1 and conditionData.yLoc >= 0 and conditionData.yLoc <= 1 and conditionData.insideCurrentMapWorld then
                return true
            end
        end
    end

    return false
end

function ZO_WorldMapQuestsData_Singleton:OnQuestAvailable(questIndex)
    if not ZO_WorldMapQuestsData_Singleton.ShouldMapShowQuestsInList() then
        return
    end
        
    if self:GetQuestMasterListIndex(questIndex) then
        -- We already have this quest in the list.
        return
    end

    local shouldAddQuest
    local questSteps = WORLD_MAP_QUEST_BREADCRUMBS:GetSteps(questIndex)
    if questSteps then
        shouldAddQuest = ZO_WorldMapQuestsData_Singleton.IsQuestInCurrentMapNormalizedBounds(questSteps)
    else
        shouldAddQuest = IsJournalQuestInCurrentMapZone(questIndex)
    end

    if shouldAddQuest then
        local questType = GetJournalQuestType(questIndex)
        local name = GetJournalQuestName(questIndex)
        local level = GetJournalQuestLevel(questIndex)
        local displayType = GetJournalQuestZoneDisplayType(questIndex)
        table.insert(self.masterList, {
            questIndex = questIndex,
            name = name,
            level = level,
            questType = questType,
            displayType = displayType,
        })

        self.listDirty = true
    end       
end

function ZO_WorldMapQuestsData_Singleton:OnQuestRemoved(questIndex)
    local masterListIndex = self:GetQuestMasterListIndex(questIndex)
    if masterListIndex then
        table.remove(self.masterList, masterListIndex)
        self.listDirty = true
    end
end

function ZO_WorldMapQuestsData_Singleton:GetQuestMasterListIndex(questIndex)
    for i, questData in ipairs(self.masterList) do
        if questData.questIndex == questIndex then
            return i
        end
    end
    return nil
end

function ZO_WorldMapQuestsData_Singleton:LayoutList(forceLayout)
    self:Sort()
    CALLBACK_MANAGER:FireCallbacks("OnWorldMapQuestsDataRefresh", forceLayout)
end

function ZO_WorldMapQuestsData_Singleton:Sort()
    table.sort(self.masterList, self.CompareQuests)
end

function ZO_WorldMapQuestsData_Singleton_Initialize(control)
    if not WORLD_MAP_QUESTS_DATA then
        WORLD_MAP_QUESTS_DATA = ZO_WorldMapQuestsData_Singleton:New(control)
    end
    return WORLD_MAP_QUESTS_DATA
end

local AddConditionLine = function(self, labels, text)
    local conditionLabel = self.labelPool:AcquireObject()
    conditionLabel:SetWidth(0)
    zo_bulletFormat(conditionLabel, text)
    table.insert(labels, conditionLabel)
end

function ZO_WorldMapQuests_Shared_SetupQuestDetails(self, questIndex)
    local labels = {}
    local questName, bgText, stepText, stepType, stepOverrideText, completed, tracked = GetJournalQuestInfo(questIndex)

    if completed then
        AddConditionLine(self, labels, GetJournalQuestEnding(questIndex))
    else
        local tasks = {}
        QUEST_JOURNAL_MANAGER:BuildTextForTasks(stepOverrideText, questIndex, tasks)

        for i = 1, #tasks do
            AddConditionLine(self, labels, tasks[i].name)
        end
    end

    local width = 0
    for i = 1, #labels do
        local labelWidth = labels[i]:GetTextDimensions() 
        width = zo_max(width, labelWidth)
    end

    local MAX_WIDTH = 250
    width = zo_min(width, MAX_WIDTH)

    return labels, width
end
