--------------------------------------------------------------------------------
-- Nanami-UI: MapReveal -- Reveal unexplored world map areas
-- Adapted from ShaguTweaks-extras worldmap-reveal approach
-- Uses LibMapOverlayData (from !Libs) supplemented with Turtle WoW zones
--------------------------------------------------------------------------------

SFrames.MapReveal = SFrames.MapReveal or {}

local MapReveal = SFrames.MapReveal
local origWorldMapFrame_Update = nil
local overlayDBPatched = false

local errata = {
    ["Interface\\WorldMap\\Tirisfal\\BRIGHTWATERLAKE"] = { offsetX = { 587, 584 } },
    ["Interface\\WorldMap\\Silverpine\\BERENSPERIL"]   = { offsetY = { 417, 415 } },
}

-- Turtle WoW new/modified zones not present in LibMapOverlayData
local TurtleWoW_Zones = {
    ["StonetalonMountains"] = {
        "SUNROCKRETREAT:512:256:256:256", "WINDSHEARCRAG:256:256:512:256",
        "MIRKFALLONLAKE:512:512:256:0", "THECHARREDVALE:256:512:256:256",
        "STONETALONPEAK:256:256:256:0", "WEBWINDERPATH:256:512:512:256",
        "AMANIALOR:512:256:0:0", "GRIMTOTEMPOST:512:256:512:512",
        "CAMPAPARAJE:512:256:512:512", "MALAKAJIN:512:256:512:512",
        "BOULDERSLIDERAVINE:256:256:512:512", "SISHIRCANYON:256:512:512:256",
        "VENTURECOMPANYCAMP:256:512:256:0", "BLACKSANDOILFIELDS:512:512:0:0",
        "POWDERTOWN:256:256:256:256", "BRAMBLETHORNPASS:512:512:512:256",
        "BAELHARDUL:512:256:512:256", "BROKENCLIFFMINE:256:512:256:0",
        "THEEARTHENRING:512:256:256:256",
    },
    ["UpperKarazhan2f"] = {
        "OUTLAND:1024:768:0:0",
    },
    ["GrimReaches"] = {
        "DUNKITHAS:512:256:256:256", "THEGRIMHOLLOW:512:512:256:256",
        "LAKEKITHAS:512:256:256:256", "SLATEBEARDSFORGE:512:256:256:256",
        "THEHIGHPASS:256:256:256:256", "SALGAZMINES:256:256:512:256",
        "EASTRIDGEOUTPOST:512:512:256:0", "BAGGOTHSRAMPART:256:512:256:0",
        "RUINSOFSTOLGAZKEEP:256:256:256:0", "GROLDANSEXCAVATION:256:512:512:0",
        "ZARMGETHSTRONGHOLD:512:256:256:0", "GETHKAR:512:256:256:0",
        "ZARMGETHPOINT:512:256:256:0", "SHATTERBLADEPOST:256:256:512:0",
        "BRANGARSFOLLY:256:256:512:0", "BARLEYCRESTFARMSTEAD:512:512:256:0",
    },
    ["Balor"] = {
        "GULLWINGWRECKAGE:512:256:0:0", "BILGERATCOMPOUND:256:256:256:0",
        "SIOUTPOST:256:512:512:256", "RUINSOFBREEZEHAVEN:512:256:256:256",
        "CROAKINGPLATEAU:512:512:256:0", "LANGSTONORCHARD:256:256:256:256",
        "SORROWMORELAKE:256:256:256:256", "SCURRYINGTHICKET:256:512:256:0",
        "STORMWROUGHTCASTLE:512:256:256:256", "STORMREAVERSPIRE:512:512:256:256",
        "WINDROCKCLIFFS:256:512:256:256", "TREACHEROUSCRAGS:512:512:256:256",
        "VANDERFARMSTEAD:256:256:256:256", "GRAHANESTATE:256:256:256:256",
        "STORMBREAKERPOINT:512:512:512:0",
    },
    ["Northwind"] = {
        "MERCHANTSHIGHROAD:512:512:256:256", "AMBERSHIRE:512:256:256:256",
        "AMBERWOODKEEP:512:256:0:256", "CRYSTALFALLS:512:512:512:256",
        "CRAWFORDWINERY:256:256:512:256", "NORTHWINDLOGGINGCAMP:256:512:256:0",
        "WITCHCOVEN:256:256:256:0", "RUINSOFBIRKHAVEN:512:512:512:0",
        "SHERWOODQUARRY:512:512:512:0", "BLACKROCKBREACH:512:512:512:0",
        "GRIMMENLAKE:256:512:512:256", "ABBEYGARDENS:256:256:512:0",
        "STILLHEARTPORT:512:512:0:0", "TOWEROFMAGILOU:512:512:0:0",
        "BRISTLEWHISKERCAVERN:256:512:512:0", "NORTHRIDGEPOINT:512:512:256:0",
        "CINDERFALLPASS:512:512:512:256",
    },
}

local function IsTurtleWoW()
    return TargetHPText and TargetHPPercText
end

local function GetOverlayDB()
    return MapOverlayData or LibMapOverlayData or zMapOverlayData or mapOverlayData
end

local function PatchOverlayDB()
    if overlayDBPatched then return end
    overlayDBPatched = true

    if not IsTurtleWoW() then return end

    local db = GetOverlayDB()
    if not db then return end

    for zone, data in pairs(TurtleWoW_Zones) do
        db[zone] = data
    end
end

local function GetConfig()
    if not SFramesDB or type(SFramesDB.MapReveal) ~= "table" then
        return { enabled = true, unexploredAlpha = 0.7 }
    end
    return SFramesDB.MapReveal
end

local function NextPowerOf2(n)
    local p = 16
    while p < n do
        p = p * 2
    end
    return p
end

local function DoMapRevealUpdate()
    local db = GetOverlayDB()
    if not db then return end

    local mapFileName = GetMapInfo and GetMapInfo()
    if not mapFileName then mapFileName = "World" end

    local zoneData = db[mapFileName]
    if not zoneData then return end

    local prefix = "Interface\\WorldMap\\" .. mapFileName .. "\\"

    local numExploredOverlays = GetNumMapOverlays and GetNumMapOverlays() or 0
    local explored = {}
    for i = 1, numExploredOverlays do
        local textureName = GetMapOverlayInfo(i)
        if textureName and textureName ~= "" then
            explored[textureName] = true
        end
    end

    local cfg = GetConfig()
    local dimR, dimG, dimB = 0.4, 0.4, 0.4
    if cfg.unexploredAlpha then
        dimR = cfg.unexploredAlpha
        dimG = cfg.unexploredAlpha
        dimB = cfg.unexploredAlpha
    end

    local textureCount = 0

    for idx = 1, table.getn(zoneData) do
        local entry = zoneData[idx]
        local _, _, name, sW, sH, sX, sY = string.find(entry, "^(%u+):(%d+):(%d+):(%d+):(%d+)$")
        if not name then
            _, _, name, sW, sH, sX, sY = string.find(entry, "^([^:]+):(%d+):(%d+):(%d+):(%d+)$")
        end
        if name then
            local textureWidth  = tonumber(sW)
            local textureHeight = tonumber(sH)
            local offsetX       = tonumber(sX)
            local offsetY       = tonumber(sY)
            local textureName   = prefix .. name

            local isExplored = explored[textureName]

            if cfg.enabled or isExplored then
                if errata[textureName] then
                    local e = errata[textureName]
                    if e.offsetX and e.offsetX[1] == offsetX then
                        offsetX = e.offsetX[2]
                    end
                    if e.offsetY and e.offsetY[1] == offsetY then
                        offsetY = e.offsetY[2]
                    end
                end

                local numTexturesHorz = math.ceil(textureWidth / 256)
                local numTexturesVert = math.ceil(textureHeight / 256)
                local neededTextures = textureCount + (numTexturesHorz * numTexturesVert)

                if neededTextures > NUM_WORLDMAP_OVERLAYS then
                    for j = NUM_WORLDMAP_OVERLAYS + 1, neededTextures do
                        WorldMapDetailFrame:CreateTexture("WorldMapOverlay" .. j, "ARTWORK")
                    end
                    NUM_WORLDMAP_OVERLAYS = neededTextures
                end

                for row = 1, numTexturesVert do
                    local texturePixelHeight, textureFileHeight
                    if row < numTexturesVert then
                        texturePixelHeight = 256
                        textureFileHeight  = 256
                    else
                        texturePixelHeight = math.mod(textureHeight, 256)
                        if texturePixelHeight == 0 then texturePixelHeight = 256 end
                        textureFileHeight = NextPowerOf2(texturePixelHeight)
                    end

                    for col = 1, numTexturesHorz do
                        if textureCount > NUM_WORLDMAP_OVERLAYS then return end

                        local texture = _G["WorldMapOverlay" .. (textureCount + 1)]

                        local texturePixelWidth, textureFileWidth
                        if col < numTexturesHorz then
                            texturePixelWidth = 256
                            textureFileWidth  = 256
                        else
                            texturePixelWidth = math.mod(textureWidth, 256)
                            if texturePixelWidth == 0 then texturePixelWidth = 256 end
                            textureFileWidth = NextPowerOf2(texturePixelWidth)
                        end

                        texture:SetWidth(texturePixelWidth)
                        texture:SetHeight(texturePixelHeight)
                        texture:SetTexCoord(0, texturePixelWidth / textureFileWidth,
                                            0, texturePixelHeight / textureFileHeight)
                        texture:ClearAllPoints()
                        texture:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT",
                                         offsetX + (256 * (col - 1)),
                                         -(offsetY + (256 * (row - 1))))

                        local tileIndex = ((row - 1) * numTexturesHorz) + col
                        texture:SetTexture(textureName .. tileIndex)

                        if not isExplored then
                            texture:SetVertexColor(dimR, dimG, dimB, 1)
                        else
                            texture:SetVertexColor(1, 1, 1, 1)
                        end

                        texture:Show()
                        textureCount = textureCount + 1
                    end
                end
            end
        end
    end
end

function MapReveal:Initialize()
    local db = GetOverlayDB()
    if not db then
        SFrames:Print("MapReveal: LibMapOverlayData 未找到，地图揭示功能不可用。")
        return
    end

    PatchOverlayDB()

    if not origWorldMapFrame_Update and WorldMapFrame_Update then
        origWorldMapFrame_Update = WorldMapFrame_Update
        WorldMapFrame_Update = function()
            for i = 1, NUM_WORLDMAP_OVERLAYS do
                local tex = _G["WorldMapOverlay" .. i]
                if tex then tex:Hide() end
            end

            origWorldMapFrame_Update()

            local cfg = GetConfig()
            if cfg.enabled then
                DoMapRevealUpdate()
            end
        end
    end
end

function MapReveal:Toggle()
    if not SFramesDB then SFramesDB = {} end
    if type(SFramesDB.MapReveal) ~= "table" then
        SFramesDB.MapReveal = { enabled = true, unexploredAlpha = 0.7 }
    end

    SFramesDB.MapReveal.enabled = not SFramesDB.MapReveal.enabled

    if SFramesDB.MapReveal.enabled then
        SFrames:Print("地图迷雾揭示: |cff00ff00已开启|r")
        if not origWorldMapFrame_Update and WorldMapFrame_Update then
            self:Initialize()
        end
    else
        SFrames:Print("地图迷雾揭示: |cffff0000已关闭|r")
    end

    if WorldMapFrame and WorldMapFrame:IsShown() then
        WorldMapFrame_Update()
    end
end

function MapReveal:SetAlpha(alpha)
    if not SFramesDB or type(SFramesDB.MapReveal) ~= "table" then return end
    SFramesDB.MapReveal.unexploredAlpha = alpha
    if SFramesDB.MapReveal.enabled and WorldMapFrame and WorldMapFrame:IsShown() then
        WorldMapFrame_Update()
    end
end

function MapReveal:Refresh()
    if WorldMapFrame and WorldMapFrame:IsShown() then
        WorldMapFrame_Update()
    end
end
