---@diagnostic disable: deprecated
local _, PUP = ...

local bytetoB64 = {
    [0]="a","b","c","d","e","f","g","h",
    "i","j","k","l","m","n","o","p",
    "q","r","s","t","u","v","w","x",
    "y","z","A","B","C","D","E","F",
    "G","H","I","J","K","L","M","N",
    "O","P","Q","R","S","T","U","V",
    "W","X","Y","Z","0","1","2","3",
    "4","5","6","7","8","9","(",")"
}

-- Generates a unique random 11 digit number in base64
-- Taken from WeakAuras
function PUP:GenerateUniqueID()
    local s = {}

    for _ = 1, 11 do
        tinsert(s, bytetoB64[math.random(0, 63)])
    end

    return table.concat(s)
end

-- Rounds a value, optionally to a certain number of decimals
function PUP:Round(value, decimals)
    if not decimals then decimals = 0 end
    
    local p = math.pow(10, decimals)
    
    value = value * p
    value = Round(value)
    value = value / p
    
    return value
end

-- Same as the game's SecondsToClock, except adds a single decimal to the seconds
function PUP:SecondsToClock(seconds, displayZeroHours)
	local units = ConvertSecondsToUnits(seconds)

	if units.hours > 0 or displayZeroHours then
		return format("%.2d:%.2d:%04.1f", units.hours, units.minutes, units.seconds + units.milliseconds)
	else
		return format("%.2d:%04.1f", units.minutes, units.seconds + units.milliseconds)
	end
end

-- Iterates group units
-- Usage: <for unit in LRP:IterateGroupMembers() do>
-- Taken from WeakAuras
function PUP:IterateGroupMembers(reversed, forceParty)
    local unit = (not forceParty and IsInRaid()) and "raid" or "party"
    local numGroupMembers = unit == "party" and GetNumSubgroupMembers() or GetNumGroupMembers()
    local i = reversed and numGroupMembers or (unit == "party" and 0 or 1)

    return function()
        local ret

        if i == 0 and unit == "party" then
            ret = "player"
        elseif i <= numGroupMembers and i > 0 then
            ret = unit .. i
        end

        i = i + (reversed and -1 or 1)

        return ret
    end
end

-- Adds a tooltip to a frame
-- Can be called repeatedly to change the tooltip
function PUP:AddTooltip(frame, tooltipText, secondaryTooltipText) 
    if not tooltipText then tooltipText = "" end

    frame.secondaryTooltipText = secondaryTooltipText -- Used for stuff like warnings/additional info that shouldn't change the main tooltip text

    -- If this frame already has a tooltip applied to it, simply change the tooltip text
    if frame.tooltipText then
        frame.tooltipText = tooltipText
    else
        frame.tooltipText = tooltipText

        -- The tooltip should be handled in a hook, in case the OnEnter/OnLeave script changes later on
        -- If there is no OnEnter/OnLeave script present, add an empty one
        if not frame:HasScript("OnEnter") then
            frame:SetScript("OnEnter", function() end)
        end

        if not frame:HasScript("OnLeave") then
            frame:SetScript("OnLeave", function() end)
        end

        frame:HookScript(
            "OnEnter",
            function()
                if not frame.tooltipText or frame.tooltipText == "" then return end
                
                PUP.Tooltip:Hide()
                PUP.Tooltip:SetOwner(frame, "ANCHOR_RIGHT")

                if frame.secondaryTooltipText and frame.secondaryTooltipText ~= "" then
                    PUP.Tooltip:SetText(string.format("%s|n|n%s", frame.tooltipText, frame.secondaryTooltipText), 0.9, 0.9, 0.9, 1, true)
                else
                    PUP.Tooltip:SetText(frame.tooltipText, 0.9, 0.9, 0.9, 1, true)
                end

                PUP.Tooltip:Show()
            end
        )

        frame:HookScript(
            "OnLeave",
            function()
                PUP.Tooltip:Hide()
            end
        )
    end
end

-- Refreshes the tooltip that is currently showing
function PUP:RefreshTooltip()
    if PUP.Tooltip:IsVisible() then
        local frame = PUP.Tooltip:GetOwner()

        if frame and frame.tooltipText then
            if frame.secondaryTooltipText and frame.secondaryTooltipText ~= "" then
                PUP.Tooltip:SetText(string.format("%s|n|n%s", frame.tooltipText, frame.secondaryTooltipText), 0.9, 0.9, 0.9, 1, true)
            else
                PUP.Tooltip:SetText(frame.tooltipText, 0.9, 0.9, 0.9, 1, true)
            end
        end
    end
end

-- Takes an icon ID and returns an in-line icon string
function PUP:IconString(iconID)
    return CreateTextureMarkup(iconID, 64, 64, 0, 0, 5/64, 59/64, 5/64, 59/64)
end

-- Save the size/position of a frame in SavedVariables, keyed by some name
function PUP:SaveSize(frame, name)
    if not name then return end

    if not PrepullUpdaterSaved.settings.frames[name] then
        PrepullUpdaterSaved.settings.frames[name] = {}
    end

    local width, height = frame:GetSize()

    PrepullUpdaterSaved.settings.frames[name].width = width
    PrepullUpdaterSaved.settings.frames[name].height = height
end

function PUP:SavePosition(frame, name)
    if not name then return end

    if not PrepullUpdaterSaved.settings.frames[name] then
        PrepullUpdaterSaved.settings.frames[name] = {}
    end

    PrepullUpdaterSaved.settings.frames[name].points = {}

    local numPoints = frame:GetNumPoints()

    for i = 1, numPoints do
        local point, relativeTo, relativePoint, offsetX, offsetY = frame:GetPoint(i)

        if relativeTo == nil or relativeTo == UIParent then -- Only consider points relative to UIParent
            table.insert(
                PrepullUpdaterSaved.settings.frames[name].points,
                {
                    point = point,
                    relativePoint = relativePoint,
                    offsetX = offsetX,
                    offsetY = offsetY
                }
            )
        end
    end
end

-- Restore and apply saved size/position to a frame, keyed by some name
function PUP:RestoreSize(frame, name)
    if not name then return end

    local settings = PrepullUpdaterSaved.settings.frames[name]

    if not settings then return end
    if not settings.width then return end
    if not settings.height then return end

    frame:SetSize(settings.width, settings.height)
end

function PUP:RestorePosition(frame, name)
    if not name then return end
    
    local settings = PrepullUpdaterSaved.settings.frames[name]

    if not (settings and settings.points) then return end

    for _, pointInfo in ipairs(settings.points) do
        frame:SetPoint(pointInfo.point, UIParent, pointInfo.relativePoint, pointInfo.offsetX, pointInfo.offsetY)
    end
end

-- Adds a 1 pixel border to a frame
function PUP:AddBorder(parent, thickness, horizontalOffset, verticalOffset)
    if not thickness then thickness = 1 end
    if not horizontalOffset then horizontalOffset = 0 end
    if not verticalOffset then verticalOffset = 0 end
    
    parent.border = {
        top = parent:CreateTexture(nil, "OVERLAY"),
        bottom = parent:CreateTexture(nil, "OVERLAY"),
        left = parent:CreateTexture(nil, "OVERLAY"),
        right = parent:CreateTexture(nil, "OVERLAY"),
    }

    parent.border.top:SetHeight(thickness)
    parent.border.top:SetPoint("TOPLEFT", parent, "TOPLEFT", -horizontalOffset, verticalOffset)
    parent.border.top:SetPoint("TOPRIGHT", parent, "TOPRIGHT", horizontalOffset, verticalOffset)
    parent.border.top:SetSnapToPixelGrid(false)
    parent.border.top:SetTexelSnappingBias(0)

    parent.border.bottom:SetHeight(thickness)
    parent.border.bottom:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", -horizontalOffset, -verticalOffset)
    parent.border.bottom:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", horizontalOffset, -verticalOffset)
    parent.border.bottom:SetSnapToPixelGrid(false)
    parent.border.bottom:SetTexelSnappingBias(0)

    parent.border.left:SetWidth(thickness)
    parent.border.left:SetPoint("TOPLEFT", parent, "TOPLEFT", -horizontalOffset, verticalOffset)
    parent.border.left:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", -horizontalOffset, -verticalOffset)
    parent.border.left:SetSnapToPixelGrid(false)
    parent.border.left:SetTexelSnappingBias(0)

    parent.border.right:SetWidth(thickness)
    parent.border.right:SetPoint("TOPRIGHT", parent, "TOPRIGHT", horizontalOffset, verticalOffset)
    parent.border.right:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", horizontalOffset, -verticalOffset)
    parent.border.right:SetSnapToPixelGrid(false)
    parent.border.right:SetTexelSnappingBias(0)

    function parent:SetBorderColor(r, g, b)
        for _, tex in pairs(parent.border) do
            tex:SetColorTexture(r, g, b)
        end
    end

    function parent:ShowBorder()
        for _, tex in pairs(parent.border) do
            tex:Show()
        end
    end

    function parent:HideBorder()
        for _, tex in pairs(parent.border) do
            tex:Hide()
        end
    end

    function parent:SetBorderShown(shown)
        if shown then
            parent:ShowBorder()
        else
            parent:HideBorder()
        end
    end

    parent:SetBorderColor(0, 0, 0)
end

-- Adds a highlight to a frame, displayed when the cursor hovers over it
-- If an alt frame is provided, the highlight will show on the alt frame when the cursor is hovered over the main frame
function PUP:AddHoverHighlight(frame, altFrame, width, r, g, b, a)
    if not altFrame then altFrame = frame end

    if not frame.highlight then
        frame.highlight = {
            top = frame:CreateTexture(nil, "HIGHLIGHT"),
            left = frame:CreateTexture(nil, "HIGHLIGHT"),
            bottom = frame:CreateTexture(nil, "HIGHLIGHT"),
            right = frame:CreateTexture(nil, "HIGHLIGHT")
        }
        
        frame.highlight.top:SetPoint("TOPLEFT", altFrame, "TOPLEFT", 1, -1)
        frame.highlight.top:SetPoint("TOPRIGHT", altFrame, "TOPRIGHT", -1, -1)
        frame.highlight.top:SetHeight(width or 1)

        frame.highlight.bottom:SetPoint("BOTTOMLEFT", altFrame, "BOTTOMLEFT", 1, 1)
        frame.highlight.bottom:SetPoint("BOTTOMRIGHT", altFrame, "BOTTOMRIGHT", -1, 1)
        frame.highlight.bottom:SetHeight(width or 1)

        frame.highlight.left:SetPoint("TOPLEFT", frame.highlight.top, "BOTTOMLEFT")
        frame.highlight.left:SetPoint("BOTTOMLEFT", frame.highlight.bottom, "TOPLEFT")
        frame.highlight.left:SetWidth(width or 1)

        frame.highlight.right:SetPoint("TOPRIGHT", frame.highlight.top, "BOTTOMRIGHT")
        frame.highlight.right:SetPoint("BOTTOMRIGHT", frame.highlight.bottom, "TOPRIGHT")
        frame.highlight.right:SetWidth(width or 1)
    end

    for _, tex in pairs(frame.highlight) do
        tex:SetColorTexture(r or (56/255), g or (119/255), b or (245/255), a or 0.6)
    end
end

function PUP:ErrorPrint(text)
    print(string.format("AuraUpdater |cffff0000ERROR|r: %s", text))
end

function PUP:ClassColorName(unit)
    if not UnitExists(unit) then return unit end
    
    local name = UnitNameUnmodified(unit)
    local class = UnitClassBase(unit)

    local colorStr = RAID_CLASS_COLORS[class].colorStr
    
    return string.format("|c%s%s|r", colorStr, name)
end