-- cmarker - Midnight 12.0.1 compatible

-- Defaults
local DEFAULT_SIZE    = 47
local DEFAULT_YOFFSET = -75
local ROTATION        = 3 * math.pi / 2

-- Zoom offset calibration (hyperbolic curve: a/zoom + b)
-- Fitted on 4 real in-game data points:
--   zoom 27.5 -> -85.6  |  zoom 21.5 -> -104.4
--   zoom 12.5 -> -175.0 |  zoom  6.5 -> -318.8
local ZOOM_MIN         = 4.5
local HYPERBOLA_A      = -1989.9531
local HYPERBOLA_B      = -13.3723

CharacterMarkerDB = CharacterMarkerDB or {
    enabled      = true,
    size         = DEFAULT_SIZE,
    yOffset      = 0,       -- delta on top of auto offset (0 = no fine tuning)
    onlyCombat   = false,
    autoOffset   = true,    -- automatic offset based on camera zoom
}

-- Official WoW class colors (exact 0.0-1.0 values)
local CLASS_COLORS = {
    WARRIOR     = { r=0.78, g=0.61, b=0.43 },  -- #C69B6D
    PALADIN     = { r=0.96, g=0.55, b=0.73 },  -- #F48CBA
    HUNTER      = { r=0.67, g=0.83, b=0.45 },  -- #AAD372
    ROGUE       = { r=1.00, g=0.96, b=0.41 },  -- #FFF468
    PRIEST      = { r=1.00, g=1.00, b=1.00 },  -- #FFFFFF
    DEATHKNIGHT = { r=0.77, g=0.12, b=0.23 },  -- #C41E3A
    SHAMAN      = { r=0.00, g=0.44, b=0.87 },  -- #0070DD
    MAGE        = { r=0.25, g=0.78, b=0.92 },  -- #3FC7EB
    WARLOCK     = { r=0.53, g=0.53, b=0.93 },  -- #8788EE
    MONK        = { r=0.00, g=1.00, b=0.60 },  -- #00FF98
    DRUID       = { r=1.00, g=0.49, b=0.04 },  -- #FF7C0A
    DEMONHUNTER = { r=0.64, g=0.19, b=0.79 },  -- #A330C9
    EVOKER      = { r=0.20, g=0.58, b=0.50 },  -- #33937F
}

local marker = nil
local zoomTicker = nil

local function Log(...)
    -- Debug logging disabled
end

local function GetClassColor()
    local _, classToken = UnitClass("player")
    if not classToken then return 1, 1, 1 end
    local src = (CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[classToken])
             or (RAID_CLASS_COLORS   and RAID_CLASS_COLORS[classToken])
             or CLASS_COLORS[classToken]
             or { r=1, g=1, b=1 }
    return src.r, src.g, src.b
end

-- Calculate offset based on current zoom (calibrated hyperbolic curve)
local function CalcOffsetFromZoom()
    local zoom = GetCameraZoom() or ZOOM_MIN
    zoom = math.max(zoom, ZOOM_MIN)
    return HYPERBOLA_A / zoom + HYPERBOLA_B
end

local function RepositionMarker()
    if not marker then return end
    local offset
    if CharacterMarkerDB.autoOffset then
        offset = CalcOffsetFromZoom() + (CharacterMarkerDB.yOffset or 0)
    else
        offset = CharacterMarkerDB.yOffset or DEFAULT_YOFFSET
    end
    marker:ClearAllPoints()
    marker:SetPoint("CENTER", UIParent, "CENTER", 0, offset)
    Log("offset:", offset, "zoom:", GetCameraZoom())
end

local function StartZoomTicker()
    if zoomTicker then return end
    -- Update position 2 times per second while the addon is active
    zoomTicker = C_Timer.NewTicker(0.5, function()
        if marker and marker:IsShown() and CharacterMarkerDB.autoOffset then
            RepositionMarker()
        end
    end)
end

local function StopZoomTicker()
    if zoomTicker then
        zoomTicker:Cancel()
        zoomTicker = nil
    end
end

local function CreateMarker()
    if marker then return end

    local h = CharacterMarkerDB.size or DEFAULT_SIZE

    marker = CreateFrame("Frame", "CharacterMarkerFrame", UIParent)
    marker:SetSize(h / 2, h)
    marker:SetFrameStrata("BACKGROUND")
    marker:SetFrameLevel(1)
    marker:EnableMouse(false)

    marker.tex = marker:CreateTexture(nil, "BACKGROUND")
    marker.tex:SetAllPoints(marker)

    local atlasOk = pcall(function()
        marker.tex:SetAtlas("CovenantSanctum-Renown-Arrow-Depressed")
    end)
    if not atlasOk then
        marker.tex:SetTexture("Interface\\BUTTONS\\Arrow-Down-Up")
    end
    marker.tex:SetDesaturated(true)
    marker.tex:SetRotation(ROTATION)

    local r, g, b = GetClassColor()
    marker.tex:SetVertexColor(r, g, b, 1)

    marker:Hide()
    
    -- Actualizar referencia global
    _G.CharacterMarkerFrame = marker
end

local function ResizeMarker()
    if not marker then CreateMarker() end
    if not marker then return end
    local h = CharacterMarkerDB.size or DEFAULT_SIZE
    marker:SetSize(h / 2, h)
end

local function Enable()
    if not marker then CreateMarker() end
    marker:Show()
    local r, g, b = GetClassColor()
    marker.tex:SetVertexColor(r, g, b, 1)
    RepositionMarker()
    if CharacterMarkerDB.autoOffset then StartZoomTicker() end
end

local function Disable()
    if marker then marker:Hide() end
    StopZoomTicker()
end

-- Exponer funciones globalmente para el panel de opciones
local function GlobalEnable()
    if not marker then CreateMarker() end
    Enable()
end

local function GlobalDisable()
    Disable()
end

_G["CharacterMarkerFrame_Enable"] = GlobalEnable
_G["CharacterMarkerFrame_Disable"] = GlobalDisable
_G["CharacterMarkerFrame_ResizeMarker"] = ResizeMarker
_G["CharacterMarkerFrame_RepositionMarker"] = RepositionMarker
_G["CharacterMarkerFrame_StartZoomTicker"] = StartZoomTicker
_G["CharacterMarkerFrame_StopZoomTicker"] = StopZoomTicker
_G["CharacterMarkerFrame_CalcOffsetFromZoom"] = CalcOffsetFromZoom

-- Events
local ev = CreateFrame("Frame")
ev:RegisterEvent("PLAYER_LOGIN")
ev:RegisterEvent("PLAYER_ENTERING_WORLD")
ev:RegisterEvent("PLAYER_REGEN_DISABLED")
ev:RegisterEvent("PLAYER_REGEN_ENABLED")

ev:SetScript("OnEvent", function(_, event)
    if event == "PLAYER_REGEN_DISABLED" then
        if CharacterMarkerDB.enabled then Enable() end

    elseif event == "PLAYER_REGEN_ENABLED" then
        if CharacterMarkerDB.onlyCombat then Disable() end

    else
        C_Timer.After(0.3, function()
            if not CharacterMarkerDB.enabled then return end
            if CharacterMarkerDB.onlyCombat and not InCombatLockdown() then
                Disable()
            else
                Enable()
            end
        end)
    end
end)

-- Slash commands
SLASH_CMARKER1 = "/cmarker"
SlashCmdList["CMARKER"] = function(input)
    local msg = (input or ""):match("^%s*(.-)%s*$"):lower()

    if msg == "on" then
        CharacterMarkerDB.enabled = true
        if not CharacterMarkerDB.onlyCombat or InCombatLockdown() then Enable() end
        print("|cFF00FFFF[CMarker]|r Enabled")

    elseif msg == "off" then
        CharacterMarkerDB.enabled = false
        Disable()
        print("|cFF00FFFF[CMarker]|r Disabled")

    elseif msg == "combat" then
        CharacterMarkerDB.onlyCombat = not CharacterMarkerDB.onlyCombat
        if CharacterMarkerDB.onlyCombat and not InCombatLockdown() then
            Disable()
        elseif not CharacterMarkerDB.onlyCombat and CharacterMarkerDB.enabled then
            Enable()
        end
        print("|cFF00FFFF[CMarker]|r Combat only:", CharacterMarkerDB.onlyCombat and "ON" or "OFF")

    elseif msg == "auto" then
        CharacterMarkerDB.autoOffset = not CharacterMarkerDB.autoOffset
        if CharacterMarkerDB.autoOffset then
            StartZoomTicker()
        else
            StopZoomTicker()
        end
        RepositionMarker()
        print("|cFF00FFFF[CMarker]|r Auto offset:", CharacterMarkerDB.autoOffset and "ON" or "OFF")

    elseif msg:match("^size%s+%d+") then
        local n = tonumber(msg:match("%d+"))
        if n and n >= 10 and n <= 200 then
            CharacterMarkerDB.size = n
            ResizeMarker()
            print("|cFF00FFFF[CMarker]|r Size:", n, "(default: "..DEFAULT_SIZE..")")
        else
            print("|cFF00FFFF[CMarker]|r Size must be between 10 and 200")
        end

    elseif msg:match("^offset%s+") then
        local n = tonumber(msg:match("offset%s+(%-?%d+)"))
        if n then
            CharacterMarkerDB.yOffset = n
            RepositionMarker()
            if CharacterMarkerDB.autoOffset then
                print("|cFF00FFFF[CMarker]|r Offset delta:", n, "(added on top of auto)")
            else
                print("|cFF00FFFF[CMarker]|r Offset:", n)
            end
        else
            print("|cFF00FFFF[CMarker]|r Usage: /cmarker offset -80")
        end

    elseif msg == "pos" then
        if marker then
            print("|cFF00FFFF[CMarker]|r autoOffset:", tostring(CharacterMarkerDB.autoOffset))
            print("|cFF00FFFF[CMarker]|r current zoom:", GetCameraZoom())
            print("|cFF00FFFF[CMarker]|r auto offset:", CalcOffsetFromZoom())
            print("|cFF00FFFF[CMarker]|r manual delta:", CharacterMarkerDB.yOffset or 0)
            print("|cFF00FFFF[CMarker]|r total offset:", CalcOffsetFromZoom() + (CharacterMarkerDB.yOffset or 0))
            print("|cFF00FFFF[CMarker]|r combatOnly:", tostring(CharacterMarkerDB.onlyCombat))
            local point, _, rpoint, x, y = marker:GetPoint(1)
            print("|cFF00FFFF[CMarker]|r GetPoint:", tostring(point), tostring(rpoint), "x="..tostring(x), "y="..tostring(y))
        else
            print("|cFF00FFFF[CMarker]|r Marker does not exist yet")
        end

    elseif msg == "reset" then
        CharacterMarkerDB = { enabled = true, size = DEFAULT_SIZE, yOffset = 0, onlyCombat = false, autoOffset = true }
        if marker then marker:Hide(); marker = nil end
        Enable()
        print("|cFF00FFFF[CMarker]|r Settings reset to defaults")

    else
        print("|cFF00FFFF[CMarker]|r Commands:")
        print("  /cmarker on / off")
        print("  /cmarker combat           (toggle combat only mode)")
        print("  /cmarker auto             (toggle automatic zoom-based offset, on by default)")
        print("  /cmarker size <10-200>    (default: "..DEFAULT_SIZE..")")
        print("  /cmarker offset <number>  (manual offset / delta when auto is on)")
        print("  /cmarker pos              (diagnostics)")
        print("  /cmarker reset")
    end

    local eb = (SELECTED_CHAT_FRAME and SELECTED_CHAT_FRAME.editBox) or ChatFrame1EditBox
    if eb and eb.IsShown and eb:IsShown() then
        eb:ClearFocus(); eb:SetText(""); eb:Hide()
    end
end

C_Timer.After(1, function()
    if CharacterMarkerDB.enabled then
        if CharacterMarkerDB.onlyCombat and not InCombatLockdown() then
            Disable()
        else
            Enable()
        end
    end
end)