local E, C, L = select(2, ...):unpack()

------------------------------------------------------------------------
-- World Map Reward Icon
------------------------------------------------------------------------
local module = E:Module("Map"):Sub("WorldMapRewardIcon")

local cfg = C.map.worldmap

local FACTION_ASSAULT_ATLAS = UnitFactionGroup("player") == "Horde" and "worldquest-icon-horde" or "worldquest-icon-alliance"
local SPECIAL_ASSIGNMENT_WIDGET_SET = 1108

local MAP_SCALE = 1.25
local PARENT_SCALE = 1
local ZOOM_FACTOR = 0.5

------------------------------------------------------------------------
-- Continent / Child Map Utilities
------------------------------------------------------------------------

local CONTINENTS = {
    [2274] = { -- Khaz Algar
        [2248] = true, -- Isle of Dorn
        [2215] = true, -- Hallowfall
        [2214] = true, -- The Ringing Deeps
        [2255] = true, -- Azj-Kahet
        [2256] = true, -- Azj-Kahet - Lower
        [2213] = true, -- City of Threads
        [2216] = true, -- City of Threads - Lower
    },
    [1978] = { -- Dragon Isles
        [2022] = true, -- The Walking Shores
        [2023] = true, -- Ohn'ahran Plains
        [2024] = true, -- The Azure Span
        [2025] = true, -- Thaldraszus
        [2151] = true, -- The Forbidden Reach
    },
    [1550] = { -- Shadowlands
        [1525] = true, -- Revendreth
        [1533] = true, -- Bastion
        [1536] = true, -- Maldraxxus
        [1565] = true, -- Ardenwald
        [1543] = true, -- The Maw
    },
    [619] = { -- Broken Isles
        [630] = true, -- Azsuna
        [641] = true, -- Val'sharah
        [650] = true, -- Highmountain
        [634] = true, -- Stormheim
        [680] = true, -- Suramar
        [627] = true, -- Dalaran
        [790] = true, -- Eye of Azshara
        [646] = true, -- Broken Shore
    },
    [424] = { -- Pandaria
        [1530] = true, -- Vale of Eternal Blossoms
    },
    [875] = { -- Zandalar
        [862] = true, -- Zuldazar
        [864] = true, -- Vol'Dun
        [863] = true, -- Nazmir
    },
    [876] = { -- Kul Tiras
        [895] = true, -- Tiragarde Sound
        [896] = true, -- Drustvar
        [942] = true, -- Stormsong Valley
    },
    [13] = { -- Eastern Kingdoms
        [14] = true, -- Arathi Highlands
    },
    [12] = { -- Kalimdor
        [62] = true, -- Darkshore
        [1527] = true, -- Uldum
    },
    [947] = { -- Azeroth
        [13] = true,
        [12] = true,
        [619] = true,
        [875] = true,
        [876] = true,
        [424] = true,
        [1978] = true,
        [2274] = true,
    },
}

local function isParentMap(mapID) return not not CONTINENTS[mapID] end

local function isChildMap(parentMapID, mapID)
    local mapInfo = C_Map.GetMapInfo(mapID)
    return parentMapID and mapID and mapInfo and mapInfo.parentMapID and mapInfo.parentMapID == parentMapID
end

local function translatePosition(position, fromMapID, toMapID)
    local continentID, worldPos = C_Map.GetWorldPosFromMapPos(fromMapID, position)
    local _, newPos = C_Map.GetMapPosFromWorldPos(continentID, worldPos, toMapID)
    return newPos
end

------------------------------------------------------------------------
-- Pin Mixin
------------------------------------------------------------------------

DarkUIWorldQuestPinMixin = CreateFromMixins(WorldMap_WorldQuestPinMixin)

function DarkUIWorldQuestPinMixin:OnLoad()
    WorldMap_WorldQuestPinMixin.OnLoad(self)

    local TrackedCheck = self:CreateTexture(nil, "OVERLAY", nil, 7)
    TrackedCheck:SetPoint("BOTTOM", self, "BOTTOMRIGHT", 0, -2)
    TrackedCheck:SetAtlas("worldquest-emissary-tracker-checkmark", true)
    TrackedCheck:Hide()
    self.TrackedCheck = TrackedCheck

    local TimeLowFrame = CreateFrame("Frame", nil, self)
    TimeLowFrame:SetPoint("CENTER", 9, -9)
    TimeLowFrame:SetSize(22, 22)
    TimeLowFrame:Hide()
    self.TimeLowFrame = TimeLowFrame

    local TimeLowIcon = TimeLowFrame:CreateTexture(nil, "OVERLAY")
    TimeLowIcon:SetAllPoints()
    TimeLowIcon:SetAtlas("worldquest-icon-clock")
    TimeLowFrame.Icon = TimeLowIcon

    local Reward = self:CreateTexture(nil, "OVERLAY")
    Reward:SetPoint("CENTER", self.PushedTexture)
    Reward:SetSize(self:GetWidth() - 4, self:GetHeight() - 4)
    Reward:SetTexCoord(0.1, 0.9, 0.1, 0.9)
    self.Reward = Reward

    local RewardMask = self:CreateMaskTexture()
    RewardMask:SetTexture([[Interface\CharacterFrame\TempPortraitAlphaMask]])
    RewardMask:SetAllPoints(Reward)
    Reward:AddMaskTexture(RewardMask)

    local Indicator = self:CreateTexture(nil, "OVERLAY", nil, 2)
    Indicator:SetPoint("CENTER", self, "TOPLEFT", 4, -4)
    self.Indicator = Indicator

    local Reputation = self:CreateTexture(nil, "OVERLAY", nil, 2)
    Reputation:SetPoint("CENTER", self, "BOTTOM", 0, 2)
    Reputation:SetSize(10, 10)
    Reputation:SetAtlas("socialqueuing-icon-eye")
    Reputation:Hide()
    self.Reputation = Reputation

    local Bounty = self:CreateTexture(nil, "OVERLAY", nil, 3)
    Bounty:SetAtlas("QuestNormal", true)
    Bounty:SetScale(0.65)
    Bounty:SetPoint("LEFT", self, "RIGHT", -(Bounty:GetWidth() / 2), 0)
    self.Bounty = Bounty
end

function DarkUIWorldQuestPinMixin:RefreshVisuals()
    WorldMap_WorldQuestPinMixin.RefreshVisuals(self)

    self.Bounty:Hide()
    self.Reward:Hide()
    self.Reputation:Hide()
    self.Indicator:Hide()
    self.Display.Icon:Hide()

    local mapID = self:GetMap():GetMapID()
    if mapID == 947 then
        self:SetScalingLimits(1, PARENT_SCALE / 2, (PARENT_SCALE / 2) + ZOOM_FACTOR)
    elseif isParentMap(mapID) then
        self:SetScalingLimits(1, PARENT_SCALE, PARENT_SCALE + ZOOM_FACTOR)
    else
        self:SetScalingLimits(1, MAP_SCALE, MAP_SCALE + ZOOM_FACTOR)
    end

    if self:IsSelected() then
        self.NormalTexture:SetAtlas("worldquest-questmarker-epic-supertracked", true)
    else
        self.NormalTexture:SetAtlas("worldquest-questmarker-epic", true)
    end

    local questID = self.questID
    local currencyRewards = C_QuestLog.GetQuestRewardCurrencies(questID)
    if GetNumQuestLogRewards(questID) > 0 then
        local _, texture, _, _, _, itemID = GetQuestLogRewardInfo(1, questID)
        if C_Item.IsAnimaItemByID(itemID) then texture = 3528287 end
        self.Reward:SetTexture(texture)
        self.Reward:Show()
    elseif #currencyRewards > 0 then
        self.Reward:SetTexture(currencyRewards[1].texture)
        self.Reward:Show()
    elseif GetQuestLogRewardMoney(questID) > 0 then
        self.Reward:SetTexture([[Interface\Icons\INV_MISC_COIN_01]])
        self.Reward:Show()
    else
        self.Display.Icon:Show()
    end

    local questInfo = C_QuestLog.GetQuestTagInfo(questID)
    if questInfo then
        if questInfo.worldQuestType == Enum.QuestTagType.PvP then
            self.Indicator:SetAtlas("Warfronts-BaseMapIcons-Empty-Barracks-Minimap")
            self.Indicator:SetSize(18, 18)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.PetBattle then
            self.Indicator:SetAtlas("WildBattlePetCapturable")
            self.Indicator:SetSize(10, 10)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.Profession then
            self.Indicator:SetAtlas(WORLD_QUEST_ICONS_BY_PROFESSION[questInfo.tradeskillLineID])
            self.Indicator:SetSize(10, 10)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.Dungeon then
            self.Indicator:SetAtlas("Dungeon")
            self.Indicator:SetSize(20, 20)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.Raid then
            self.Indicator:SetAtlas("Raid")
            self.Indicator:SetSize(20, 20)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.Invasion then
            self.Indicator:SetAtlas("worldquest-icon-burninglegion")
            self.Indicator:SetSize(10, 10)
            self.Indicator:Show()
        elseif questInfo.worldQuestType == Enum.QuestTagType.FactionAssault then
            self.Indicator:SetAtlas(FACTION_ASSAULT_ATLAS)
            self.Indicator:SetSize(10, 10)
            self.Indicator:Show()
        end
    end

    local bountyQuestID = self.dataProvider:GetBountyInfo()
    if bountyQuestID and C_QuestLog.IsQuestCriteriaForBounty(questID, bountyQuestID) then self.Bounty:Show() end

    local _, factionID = C_TaskQuest.GetQuestInfoByQuestID(questID)
    if factionID then
        local factionInfo = C_Reputation.GetFactionDataByID(factionID)
        if factionInfo and factionInfo.isWatched then self.Reputation:Show() end
    end
end

function DarkUIWorldQuestPinMixin:AddIconWidgets() end

------------------------------------------------------------------------
-- World Quest Provider Override
------------------------------------------------------------------------

local function setupWorldQuestProvider()
    local provider
    for dp in next, WorldMapFrame.dataProviders do
        if not dp.GetPinTemplates and type(dp.GetPinTemplate) == "function" then
            if dp:GetPinTemplate() == "WorldMap_WorldQuestPinTemplate" then
                provider = dp
                break
            end
        end
    end

    if not provider then return end

    provider.GetPinTemplate = function() return "DarkUIWorldQuestPinTemplate" end

    provider.ShouldOverrideShowQuest = function() end

    local originalShouldShowQuest = provider.ShouldShowQuest or WorldQuestDataProviderMixin.ShouldShowQuest
    provider.ShouldShowQuest = function(self, questInfo)
        local mapID = self:GetMap():GetMapID()

        if originalShouldShowQuest(self, questInfo) then return true end

        local mapInfo = C_Map.GetMapInfo(mapID)
        if mapInfo.mapType == Enum.UIMapType.Continent then return true end

        return isChildMap(mapID, questInfo.mapID)
    end

    module:RegisterEvent("MODIFIER_STATE_CHANGED", function()
        if WorldMapFrame:IsShown() then
            for pin in WorldMapFrame:EnumeratePinsByTemplate("DarkUIWorldQuestPinTemplate") do
                pin:SetShown(not IsAltKeyDown())
            end
        end
    end)

    WorldMapFrame:HookScript("OnHide", function()
        for pin in WorldMapFrame:EnumeratePinsByTemplate("DarkUIWorldQuestPinTemplate") do
            pin:SetShown(true)
        end
    end)
end

------------------------------------------------------------------------
-- POI Provider (Special Assignments on Continent)
------------------------------------------------------------------------

local function setupPOIProvider()
    local provider = CreateFromMixins(AreaPOIDataProviderMixin)

    function provider:GetPinTemplate() return "DarkUIWorldQuestPOITemplate" end

    function provider:RefreshAllData()
        self:RemoveAllData()

        local map = self:GetMap()
        local mapID = map:GetMapID()

        if mapID == 947 then return end

        if isParentMap(mapID) then
            for _, mapInfo in next, C_Map.GetMapChildrenInfo(mapID, Enum.UIMapType.Zone, true) do
                if mapInfo.flags == 6 or mapInfo.flags == 4 then
                    for _, poiID in next, GetAreaPOIsForPlayerByMapIDCached(mapInfo.mapID) do
                        local poiInfo = C_AreaPoiInfo.GetAreaPOIInfo(mapInfo.mapID, poiID)
                        if poiInfo and poiInfo.tooltipWidgetSet == SPECIAL_ASSIGNMENT_WIDGET_SET then
                            poiInfo.dataProvider = self
                            -- Our pins run tainted; laying out widget-set tooltips
                            -- compares secret widget sizes and errors. Name/description only.
                            poiInfo.tooltipWidgetSet = nil
                            poiInfo.position = translatePosition(poiInfo.position, mapInfo.mapID, mapID)
                            if poiInfo.position then map:AcquirePin(self:GetPinTemplate(), poiInfo) end
                        end
                    end
                end
            end
        end
    end

    WorldMapFrame:AddDataProvider(provider)
end

------------------------------------------------------------------------
-- Event Provider (Area Events on Continent)
------------------------------------------------------------------------

local function setupEventProvider()
    local provider = CreateFromMixins(AreaPOIEventDataProviderMixin)

    function provider:GetPinTemplate() return "DarkUIWorldQuestEventTemplate" end

    function provider:RefreshAllData()
        self:RemoveAllData()

        local map = self:GetMap()
        local mapID = map:GetMapID()

        if mapID == 947 then return end

        if isParentMap(mapID) then
            for _, mapInfo in next, C_Map.GetMapChildrenInfo(mapID, Enum.UIMapType.Zone, true) do
                if mapInfo.flags == 6 or mapInfo.flags == 4 then
                    for _, poiID in next, C_AreaPoiInfo.GetEventsForMap(mapInfo.mapID) do
                        local poiInfo = C_AreaPoiInfo.GetAreaPOIInfo(mapInfo.mapID, poiID)
                        if poiInfo then
                            poiInfo.dataProvider = self
                            -- Our pins run tainted; laying out widget-set tooltips
                            -- compares secret widget sizes and errors. Name/description only.
                            poiInfo.tooltipWidgetSet = nil
                            poiInfo.position = translatePosition(poiInfo.position, mapInfo.mapID, mapID)
                            if poiInfo.position then map:AcquirePin(self:GetPinTemplate(), poiInfo) end
                        end
                    end
                end
            end
        end
    end

    WorldMapFrame:AddDataProvider(provider)
end

------------------------------------------------------------------------
-- Module Init
------------------------------------------------------------------------

function module:OnInit()
    if not cfg or not cfg.enable then return end
    if C_AddOns.IsAddOnLoaded("BetterWorldQuests") then return end

    setupWorldQuestProvider()
    setupPOIProvider()
    setupEventProvider()
end
