local _, Cell = ...
local L = Cell.L
---@type CellFuncs
local F = Cell.funcs
---@class CellIndicatorFuncs
local I = Cell.iFuncs

local UnitGUID = UnitGUID
local UnitCanAttack = UnitCanAttack
local UnitIsOtherPlayersPet = UnitIsOtherPlayersPet

-------------------------------------------------
-- events
-------------------------------------------------
local nameplates = {
    -- nameplateUnitId = true,
}

local nameplateTargets = {
    -- nameplateUnitId = targetGUID,
}

local counter = {
    -- friendGUID = {enemyGUID=true, ...},
}

local eventFrame = CreateFrame("Frame")
eventFrame:SetScript("OnEvent", function(self, event, ...)
    self[event](self, ...)
end)


function eventFrame:NAME_PLATE_UNIT_REMOVED(unit)
    nameplates[unit] = nil
    nameplateTargets[unit] = nil
end

function eventFrame:NAME_PLATE_UNIT_ADDED(unit)
    if not unit or not UnitCanAttack(unit, "player") or UnitIsOtherPlayersPet(unit) then return end
    nameplates[unit] = true
end

local function ScanNameplates()
    for _, nameplate in pairs(C_NamePlate.GetNamePlates()) do
        eventFrame:NAME_PLATE_UNIT_ADDED(nameplate.namePlateUnitToken)
    end
end

local function SetCount(b, count)
    b.indicators.targetCounter:SetCount(count)
end

local ticker
local function StartTicker()
    if ticker then ticker:Cancel() end
    ticker = C_Timer.NewTicker(0.25, function()
        -- reset
        for _, ct in pairs(counter) do
            wipe(ct)
        end

        -- check & calculate
        for unit in pairs(nameplates) do
            local target = UnitGUID(unit.."target")

            if not target then -- no target
                nameplateTargets[unit] = nil
            elseif not Cell.vars.guids[target] then -- target doesn't exists in player's group
                nameplateTargets[unit] = nil
                counter[target] = nil
            else
                nameplateTargets[unit] = target
            end

            target = nameplateTargets[unit]
            if target and Cell.vars.guids[target] then -- valid target exists
                if not counter[target] then counter[target] = {} end -- init
                counter[target][unit] = true
            end
        end

        -- update indicator
        for guid in pairs(Cell.vars.guids) do
            F.HandleUnitButton("guid", guid, SetCount, counter[guid] and F.Getn(counter[guid]) or 0)
        end
    end)
end

local function StopTicker()
    if ticker then ticker:Cancel() end
    ticker = nil
end

local counterEnabled, zoneFilters = false, {}
function eventFrame:PLAYER_ENTERING_WORLD()
    -- reset
    wipe(nameplates)
    wipe(counter)
    F.IterateAllUnitButtons(function(b)
        b.indicators.targetCounter:SetCount(0)
    end, true)

    local isIn, iType = IsInInstance()

    local isValidZone
    if not isIn or iType == "none" then
        isValidZone = zoneFilters["outdoor"]
    elseif iType == "pvp" or iType == "arena" then
        isValidZone = zoneFilters["pvp"]
    else -- party, raid, scenario
        isValidZone = zoneFilters["pve"]
    end

    if counterEnabled and isValidZone then
        eventFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
        eventFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
        ScanNameplates()
        StartTicker()
    else
        eventFrame:UnregisterEvent("NAME_PLATE_UNIT_ADDED")
        eventFrame:UnregisterEvent("NAME_PLATE_UNIT_REMOVED")
        StopTicker()
    end
end

function I.EnableTargetCounter(enabled)
    if enabled then
        eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
        counterEnabled = true
    else
        eventFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
        counterEnabled = false
    end
    eventFrame:PLAYER_ENTERING_WORLD() -- check now
    -- texplore(nameplateTargets)
end

function I.UpdateTargetCounterFilters(filters, noUpdate)
    if filters then zoneFilters = filters end
    if not noUpdate and counterEnabled then
        eventFrame:PLAYER_ENTERING_WORLD()
    end
end

-------------------------------------------------
-- CreateTargetCounter
-------------------------------------------------
function I.CreateTargetCounter(parent)
    local targetCounter = CreateFrame("Frame", parent:GetName().."TargetCounter", parent)
    parent.indicators.targetCounter = targetCounter
    targetCounter:Hide()

    local text = targetCounter:CreateFontString(nil, "OVERLAY", "CELL_FONT_STATUS")
    targetCounter.text = text
    -- stack:SetJustifyH("RIGHT")
    text:SetPoint("CENTER", 1, 0)

    function targetCounter:SetFont(font, size, outline, shadow)
        font = F.GetFont(font)

        local flags
        if outline == "None" then
            flags = ""
        elseif outline == "Outline" then
            flags = "OUTLINE"
        else
            flags = "OUTLINE,MONOCHROME"
        end

        text:SetFont(font, size, flags)

        if shadow then
            text:SetShadowOffset(1, -1)
            text:SetShadowColor(0, 0, 0, 1)
        else
            text:SetShadowOffset(0, 0)
            text:SetShadowColor(0, 0, 0, 0)
        end

        local point = targetCounter:GetPoint(1)
        text:ClearAllPoints()
        if string.find(point, "LEFT") then
            text:SetPoint("LEFT")
        elseif string.find(point, "RIGHT") then
            text:SetPoint("RIGHT")
        else
            text:SetPoint("CENTER")
        end
        targetCounter:SetSize(size+3, size+3)
    end

    targetCounter._SetPoint = targetCounter.SetPoint
    function targetCounter:SetPoint(point, relativeTo, relativePoint, x, y)
        text:ClearAllPoints()
        if string.find(point, "LEFT") then
            text:SetPoint("LEFT")
        elseif string.find(point, "RIGHT") then
            text:SetPoint("RIGHT")
        else
            text:SetPoint("CENTER")
        end
        targetCounter:_SetPoint(point, relativeTo, relativePoint, x, y)
    end

    function targetCounter:SetCount(n)
        if n == 0 then
            targetCounter:Hide()
        else
            targetCounter:Show()
        end
        text:SetText(n)
    end

    function targetCounter:SetColor(r, g, b)
        text:SetTextColor(r, g, b)
    end
end
