local _AddonName, _EchoRaidTools = ...;


local module = _EchoRaidTools:GetModule("todo-list")
local displayFrame, sortBars;
local modFramePool = {}
local modFrames = {}
local barIndex = {}
local totalHeight = 0
local preDragSettings
module.display = module.display or {}


--displayFrame:SetPoint

--[[


    EchoRaidTools.TodoList.display.anchors = { frame = "UIParent", sourcePoint = "TOPLEFT", destPoint = "TOPLEFT", offsetX = 0, offsetY = 0 }
    EchoRaidTools.TodoList.display.size = { width = 200, height = 200, scale = 1 }


]] --


local function updateWindowHeight(sizeChange)
    totalHeight = totalHeight + sizeChange

    local displayInfo = module:GetDisplayInfo()
    local padding = displayInfo.barSettings.padding
    local numBars = #barIndex
    local height = totalHeight + ((numBars - 1) * padding)
    displayFrame:SetHeight(height)
end

local function getBarIndex(key)
    for i, t in ipairs(barIndex) do
        if t.key == key then
            return i
        end
    end
end



local function sortIndexTable()
    table.sort(barIndex, function(a, b)
        local indexa = a.index
        local indexb = b.index

        return indexa == indexb and a.key < b.key or indexa < indexb
    end)

    local changed = math.huge

    for index, t in ipairs(barIndex) do
        local frame = modFrames[t.key]
        local oldIndex = frame.index or math.huge
        if oldIndex ~= index then
            local minIndex = math.min(oldIndex, index)
            if changed > minIndex then
                changed = minIndex
            end
            frame.index = index
        end
    end
    sortBars(changed)
end

function module.display:updateDisplaySettings(tempDisplayInfo)
    if not displayFrame then
        return
    end
    displayFrame:ClearAllPoints()
    local displayInfo
    if tempDisplayInfo then
        displayInfo = tempDisplayInfo
    elseif module.config.open then
        local realDisplayInfo = module:GetDisplayInfo()
        displayInfo = {
            anchors = {
                frame = module.config:GetUserEditor(),
                sourcePoint = "TOPLEFT",
                destPoint = "TOPRIGHT",
                offsetX = 20,
                offsetY = 20,
                strata = "TOOLTIP"
            },
            bgColour = { unpack(realDisplayInfo.bgColour) },
            size = {
                width = realDisplayInfo.size.width,
                scale = realDisplayInfo.size.scale,
            }


        }
    else
        displayInfo = module:GetDisplayInfo()
    end
    local parent = type(displayInfo.anchors.frame) ~= "string" and displayInfo.anchors.frame or
        module:GetFrameFromString(displayInfo.anchors.frame)
    displayFrame:SetParent(parent)
    displayFrame:SetWidth(displayInfo.size.width)
    updateWindowHeight(0)
    displayFrame:SetScale(displayInfo.size.scale)
    displayFrame.bg:SetColorTexture(unpack(displayInfo.bgColour))
    displayFrame:SetPoint(
        displayInfo.anchors.sourcePoint,
        parent,
        displayInfo.anchors.destPoint,
        displayInfo.anchors.offsetX,
        displayInfo.anchors.offsetY
    )
    displayFrame:SetFrameStrata(displayInfo.anchors.strata)
end

local function createDisplayFrame()
    displayFrame = CreateFrame("Frame", "Echo_Todo_ListDisplayFrame")
    displayFrame:SetScript("OnShow", function()
        module.display.IsShown = true
        module.eventHandler:EventHandlerStart()
        module.display.checkTimerState()
    end)
    displayFrame:SetScript("OnHide", function()
        module.display.IsShown = false
        module.eventHandler:EventHandlerStop()
        module.display.checkTimerState()
    end)

    displayFrame.bg = displayFrame:CreateTexture(nil, "BACKGROUND")
    displayFrame.bg:SetAllPoints()

    displayFrame:SetFrameStrata("HIGH")
    displayFrame:Hide()


    displayFrame:SetScript("OnDragStart", function(self, button)
        local point, relativeTo, relativePoint, offsetX, offsetY = displayFrame:GetPoint()
        preDragSettings = {
            point = point,
            relativeTo = relativeTo,
            relativePoint = relativePoint,
            offsetX = offsetX,
            offsetY = offsetY,
            left = displayFrame:GetLeft(),
            top = displayFrame:GetTop(),
        }

        self:StartMoving()
    end)


    module.display:updateDisplaySettings()
end

---gets called from backend
function module.display.ShowDisplay()
    if not displayFrame then
        createDisplayFrame()
    end
    displayFrame:Show()
end

function module.display.HideDisplay()
    displayFrame:Hide()
end

function sortBars(index)
    local totalBars = #barIndex
    if index > totalBars then return end

    local displayInfo = module:GetDisplayInfo()
    local padding = displayInfo.barSettings.padding
    for i = index, min(totalBars, index + 1) do
        local frame = modFrames[barIndex[i].key]
        if i == 1 then
            frame:SetPoint("TOPRIGHT", displayFrame, "TOPRIGHT", 0, 0)
        else
            frame:SetPoint("TOPRIGHT", modFrames[barIndex[i - 1].key], "BOTTOMRIGHT", 0, -padding)
        end
    end
end

---comment
---@param identifier string
---@param value string display string
function module.display.UpdateText(identifier, value)
    --modFrames[identifier].text:Show()
    modFrames[identifier].text:SetText(value)
end

---comment
---@param identifier string
---@param value string display string
function module.display.UpdateProgressText(identifier, value)
    modFrames[identifier].progressText:SetText(value)
end

---comment
---@param identifier string
---@param value integer
function module.display.UpdateValue(identifier, value)
    modFrames[identifier]:SetValue(value)
    --print("set current value", value, "type", type(value), "  got value", modFrames[identifier]:GetValue(value))
end

---comment
---@param identifier string
---@param value integer sort Index
function module.display.UpdateIndex(identifier, value)
    local i = getBarIndex(identifier)
    barIndex[i].index = value
    sortIndexTable()
end

---comment
---@param identifier string
---@param value table {r,g,b,a}
function module.display.UpdateColour(identifier, value)
    modFrames[identifier]:SetStatusBarColor(unpack(value))
end

---comment
---@param identifier string
---@param value table {r,g,b,a}
function module.display.UpdateProgressTextColour(identifier, value)
    modFrames[identifier].progressText:SetTextColor(unpack(value))
end

---comment
---@param identifier string
---@param value table {r,g,b,a}
function module.display.UpdateTextColour(identifier, value)
    modFrames[identifier].text:SetTextColor(unpack(value))
end

---comment
---@param identifier string
---@param value table {r,g,b,a}
function module.display.UpdateBgColour(identifier, value)
    modFrames[identifier].bg:SetColorTexture(unpack(value))
end

---comment
---@param identifier string
---@param value table {cooldown,duration,offset,}
function module.display.UpdateTimer(identifier, value)
    --offset by this I guess C_DateAndTime.GetWeeklyResetStartTime()
end

local function updateIconPosition(identifier)
    local displayInfo = module:GetDisplayInfo()
    local width = displayInfo.size.width
    local iconExists = modFrames[identifier].icon:GetTexture() or modFrames[identifier].icon:GetAtlas()

    if iconExists then
        local height = modFrames[identifier]:GetHeight()
        modFrames[identifier].icon:Show()
        modFrames[identifier].iconBg:Show()
        modFrames[identifier].icon:SetSize(height, height)
        modFrames[identifier]:SetWidth(width - height)
    else
        modFrames[identifier].icon:Hide()
        modFrames[identifier].iconBg:Hide()
        modFrames[identifier]:SetWidth(width)
    end
end

---comment
---@param identifier string
---@param value number height
function module.display.UpdateHeight(identifier, value)
    updateWindowHeight(value - modFrames[identifier]:GetHeight())

    modFrames[identifier]:SetHeight(value)


    updateIconPosition(identifier)
end

---comment
---@param identifier string
---@param value string texture
function module.display.UpdateTexture(identifier, value)
    local texture = module.lsm:GetStatusbarPath(value)
    modFrames[identifier]:SetStatusBarTexture(texture)
end

---comment
---@param identifier string
---@param value string fontName
function module.display.UpdateFont(identifier, value)
    local font = module.lsm:GetFontPath(value)
    modFrames[identifier].progressText:SetFont(font, modFrames[identifier].progressText:GetFontHeight())
    modFrames[identifier].text:SetFont(font, modFrames[identifier].text:GetFontHeight())
end

---comment
---@param identifier string
---@param value string fontName
function module.display.UpdateTextFontSize(identifier, value)
    modFrames[identifier].text:SetFontHeight(value)
end

---comment
---@param identifier string
---@param value string fontName
function module.display.UpdateProgressTextFontSize(identifier, value)
    modFrames[identifier].progressText:SetFontHeight(value)
end

---comment
---@param identifier string
---@param value integer | string iconFileID or atlasString
function module.display.UpdateIcon(identifier, value)
    local num = tonumber(value)
    if num then
        modFrames[identifier].icon:SetTexture(value)
    else
        modFrames[identifier].icon:SetAtlas(value)
    end
    updateIconPosition(identifier)
end

local function resetBar(frame)
    local displayInfo = module:GetDisplayInfo()
    frame:SetSize(displayInfo.size.width, 1)
    frame:SetPoint("TOPRIGHT", displayFrame, "TOPRIGHT", 0, 0)



    frame.icon:SetPoint("TOPRIGHT", frame, "TOPLEFT", 0, 0)
    frame.icon:SetSize(1, 1)
    frame.icon:Hide()
    frame.icon:SetTexture(nil)
    frame.icon:SetAtlas(nil)

    frame.text:SetPoint("LEFT", frame, "LEFT", 2, 0)
    frame.text:SetSize(frame:GetWidth(), frame:GetHeight())
    frame.text:SetJustifyH("LEFT")
    frame.text:SetJustifyV("MIDDLE")
    frame.text:SetWordWrap(false)
    frame.text:SetText("")
    frame.progressText:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
    frame.progressText:SetSize(frame:GetWidth(), frame:GetHeight())
    frame.progressText:SetJustifyH("RIGHT")
    frame.progressText:SetJustifyV("MIDDLE")
    frame.progressText:SetWordWrap(false)
    frame.progressText:SetText("")


    frame:SetValue(0)
    frame.index = nil
    frame:Show()



    return frame
end

local function createNewBar()
    local frame = CreateFrame("StatusBar", nil, displayFrame)
    frame:SetStatusBarTexture("Interface\\TARGETINGFRAME\\UI-StatusBar")
    frame.bg = frame:CreateTexture(nil, "BACKGROUND")
    frame.bg:SetAllPoints()
    frame.icon = frame:CreateTexture(nil, "ARTWORK")
    frame.iconBg = frame:CreateTexture(nil, "BACKGROUND")
    frame.iconBg:SetColorTexture(0, 0, 0, 1)
    frame.iconBg:SetAllPoints(frame.icon)
    frame.text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    frame.progressText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    frame:SetMinMaxValues(0, 1)

    return frame
end



function module:CreateBar(identifier)
    if not displayFrame then
        return
    end
    if modFrames[identifier] then return end
    local frame
    if #modFramePool > 0 then
        frame = modFramePool[1]
        table.remove(modFramePool, 1)
    else
        frame = createNewBar()
    end
    frame = resetBar(frame)
    modFrames[identifier] = frame
    table.insert(barIndex, { key = identifier, index = math.huge })
    sortIndexTable()
    updateWindowHeight(frame:GetHeight())
    --return frame
end

function module:DestroyBar(identifier)
    if not modFrames[identifier] then return end
    local frame = modFrames[identifier]
    updateWindowHeight(-frame:GetHeight())

    frame:Hide()

    table.insert(modFramePool, frame)
    modFrames[identifier] = nil
    --move frame from modFrames to modFramePool
    local index = getBarIndex(identifier)

    table.remove(barIndex, index)

    sortIndexTable()
end

function module.display:GetDisplayFrame()
    if not displayFrame then createDisplayFrame() end
    return displayFrame
end

function module.display:EnableDrag(onDragFunc)
    displayFrame:SetMovable(true)
    displayFrame:EnableMouse(true)
    displayFrame:RegisterForDrag("LeftButton")
    displayFrame:SetScript("OnDragStop", function(frame)
        frame:StopMovingOrSizing()


        local old = preDragSettings

        local offsetX = old.offsetX + (frame:GetLeft() - old.left)
        local offsetY = old.offsetY + (frame:GetTop() - old.top)


        frame:ClearAllPoints()
        frame:SetPoint(
            old.point,
            old.relativeTo,
            old.relativePoint,
            offsetX,
            offsetY
        )


        onDragFunc(frame)
    end)
end

function module.display:DisableDrag()
    displayFrame:SetMovable(false)
    displayFrame:EnableMouse(false)
    displayFrame:RegisterForDrag("LeftButton")
end
