-- [ ORBIT HANDLE CORE ] -----------------------------------------------------------------------------
local _, Orbit = ...
local Engine = Orbit.Engine

Engine.HandleCore = {}
local HandleCore = Engine.HandleCore

-- [ CONFIGURATION ] ---------------------------------------------------------------------------------
local DEFAULT_BORDER_SIZE = 1
local MIN_HANDLE_WIDTH = 50
local MIN_HANDLE_HEIGHT = 20

local COLOR_IDLE = { r = 0.3, g = 0.8, b = 0.3, bgA = 0, borderA = 0 }
local COLOR_HOVER = { r = 0.3, g = 0.8, b = 0.3, bgA = 0.1, borderA = 0.4 }
local COLOR_SELECTED = { r = 0.5, g = 0.9, b = 0.3, bgA = 0.1, borderA = 0.5 }
local COLOR_DRAG = { r = 0.3, g = 1.0, b = 0.3, bgA = 0.35, borderA = 0.8 }

HandleCore.Colors = {
    IDLE = COLOR_IDLE,
    HOVER = COLOR_HOVER,
    SELECTED = COLOR_SELECTED,
    DRAG = COLOR_DRAG,
}

HandleCore.MIN_WIDTH = MIN_HANDLE_WIDTH
HandleCore.MIN_HEIGHT = MIN_HANDLE_HEIGHT

-- [ FRAME POOL ] ------------------------------------------------------------------------------------
local handlePool = {}

function HandleCore:AcquireFromPool()
    return table.remove(handlePool)
end

function HandleCore:ReturnToPool(handle)
    if not handle then
        return
    end

    handle:SetScript("OnEnter", nil)
    handle:SetScript("OnLeave", nil)
    handle:SetScript("OnMouseDown", nil)
    handle:SetScript("OnMouseUp", nil)
    handle:SetScript("OnDragStart", nil)
    handle:SetScript("OnDragStop", nil)
    handle:SetScript("OnUpdate", nil)

    handle:Hide()
    handle:ClearAllPoints()
    handle.component = nil
    handle.container = nil
    handle.callbacks = nil
    handle.isDragging = false

    table.insert(handlePool, handle)
end

function HandleCore:ClearPool()
    for _, h in ipairs(handlePool) do
        h:SetParent(nil)
    end
    wipe(handlePool)
end

-- [ CREATE HANDLE FRAME ] ---------------------------------------------------------------------------
function HandleCore:CreateFrame(options)
    options = options or {}
    local strata = options.strata or "FULLSCREEN_DIALOG"
    local level = options.level or 200
    local borderSize = options.borderSize or DEFAULT_BORDER_SIZE

    local handle = CreateFrame("Frame", nil, UIParent)
    handle:SetFrameStrata(strata)
    handle:SetFrameLevel(level)

    handle.bg = handle:CreateTexture(nil, "BACKGROUND")
    handle.bg:SetAllPoints()
    handle.bg:SetColorTexture(COLOR_IDLE.r, COLOR_IDLE.g, COLOR_IDLE.b, COLOR_IDLE.bgA)

    local handleScale = handle:GetEffectiveScale()
    local pxBorder = Engine.Pixel:Multiple(borderSize, handleScale)

    handle.borderTop = handle:CreateTexture(nil, "BORDER")
    handle.borderTop:SetColorTexture(COLOR_IDLE.r, COLOR_IDLE.g, COLOR_IDLE.b, COLOR_IDLE.borderA)
    handle.borderTop:SetPoint("TOPLEFT", 0, 0)
    handle.borderTop:SetPoint("TOPRIGHT", 0, 0)
    handle.borderTop:SetHeight(pxBorder)

    handle.borderBottom = handle:CreateTexture(nil, "BORDER")
    handle.borderBottom:SetColorTexture(COLOR_IDLE.r, COLOR_IDLE.g, COLOR_IDLE.b, COLOR_IDLE.borderA)
    handle.borderBottom:SetPoint("BOTTOMLEFT", 0, 0)
    handle.borderBottom:SetPoint("BOTTOMRIGHT", 0, 0)
    handle.borderBottom:SetHeight(pxBorder)

    handle.borderLeft = handle:CreateTexture(nil, "BORDER")
    handle.borderLeft:SetColorTexture(COLOR_IDLE.r, COLOR_IDLE.g, COLOR_IDLE.b, COLOR_IDLE.borderA)
    handle.borderLeft:SetPoint("TOPLEFT", 0, 0)
    handle.borderLeft:SetPoint("BOTTOMLEFT", 0, 0)
    handle.borderLeft:SetWidth(pxBorder)

    handle.borderRight = handle:CreateTexture(nil, "BORDER")
    handle.borderRight:SetColorTexture(COLOR_IDLE.r, COLOR_IDLE.g, COLOR_IDLE.b, COLOR_IDLE.borderA)
    handle.borderRight:SetPoint("TOPRIGHT", 0, 0)
    handle.borderRight:SetPoint("BOTTOMRIGHT", 0, 0)
    handle.borderRight:SetWidth(pxBorder)

    handle.borderSize = borderSize

    function handle:SetHandleColor(r, g, b, bgAlpha, borderAlpha)
        self.bg:SetColorTexture(r, g, b, bgAlpha)
        self.borderTop:SetColorTexture(r, g, b, borderAlpha)
        self.borderBottom:SetColorTexture(r, g, b, borderAlpha)
        self.borderLeft:SetColorTexture(r, g, b, borderAlpha)
        self.borderRight:SetColorTexture(r, g, b, borderAlpha)
    end

    function handle:ApplyColorPreset(preset)
        self:SetHandleColor(preset.r, preset.g, preset.b, preset.bgA, preset.borderA)
    end

    return handle
end

-- [ UTILITY FUNCTIONS ] -----------------------------------------------------------------------------
-- Native Blizzard frames may return secret-tainted dimensions in 12.0+; guard with issecretvalue before any comparison.
function HandleCore:SafeGetSize(frame)
    if not frame then
        return MIN_HANDLE_WIDTH, MIN_HANDLE_HEIGHT
    end

    local width, height = MIN_HANDLE_WIDTH, MIN_HANDLE_HEIGHT

    local w = frame:GetWidth()
    if type(w) == "number" and not (issecretvalue and issecretvalue(w)) and w > 0 then
        width = w
    end

    local h = frame:GetHeight()
    if type(h) == "number" and not (issecretvalue and issecretvalue(h)) and h > 0 then
        height = h
    end

    return width, height
end

-- Position handle over component with minimum size enforcement
function HandleCore:PositionOverComponent(handle, component)
    if not handle or not component then
        return
    end

    local width, height = self:SafeGetSize(component)
    local minWidth = component.orbitHandleMinWidth
    local minHeight = component.orbitHandleMinHeight
    if type(minWidth) ~= "number" then minWidth = MIN_HANDLE_WIDTH end
    if type(minHeight) ~= "number" then minHeight = MIN_HANDLE_HEIGHT end
    width = math.max(width, minWidth)
    height = math.max(height, minHeight)

    local scale = handle:GetEffectiveScale()
    handle:SetSize(Engine.Pixel:Snap(width, scale), Engine.Pixel:Snap(height, scale))
    handle:ClearAllPoints()
    handle:SetPoint("CENTER", component, "CENTER", 0, 0)
end
