require "ISUI/ISPanel"
require "ISUI/ISButton"
require "ISUI/ISMouseDrag"
require "ISUI/ISInventoryPage"

local SET_MANUALLY = "ReorderContainers_SetManually"

local FONT_HGT_SMALL = getTextManager():getFontHeight(UIFont.Small)
local BUTTON_HGT = FONT_HGT_SMALL + 6

ISInventoryPage.pre_reorder_addContainerButton = ISInventoryPage.addContainerButton
function ISInventoryPage:addContainerButton(container, texture, name, tooltip)
    local button = self.pre_reorder_addContainerButton(self, container, texture, name, tooltip)

    -- Buttons can be reused, so we need to make sure we don't overwrite the original functions

    if not button.pre_reorder_onMouseDown then
        button.pre_reorder_onMouseDown = button.onMouseDown
    end
    button.onMouseDown = ReorderContainers_Mod.onMouseDown
    
    if not button.pre_reorder_onMouseMove then
        button.pre_reorder_onMouseMove = button.onMouseMove
    end
    button.onMouseMove = ReorderContainers_Mod.onMouseMove

    if not button.pre_reorder_onMouseMoveOutside then
        button.pre_reorder_onMouseMoveOutside = button.onMouseMoveOutside
    end
    button.onMouseMoveOutside = ReorderContainers_Mod.onMouseMoveOutside

    if not button.pre_reorder_onMouseUp then
        button.pre_reorder_onMouseUp = button.onMouseUp
    end
    button.onMouseUp = ReorderContainers_Mod.onMouseUp

    return button
end

ISInventoryPage.pre_reorder_createChildren = ISInventoryPage.createChildren
ISInventoryPage.createChildren = function(self)
    self.pre_reorder_createChildren(self)

    self:createReorderContainersSortButton()
    self:createReorderContainersLockButton()
end

--[[ ISInventoryPage.createReorderContainersSortButton = function(self)
    local buttonSize = self.buttonSize/2 -- Half the size of the inventory container icons
    local xPos = self:getWidth() - buttonSize
    local yPos = self:getHeight() - buttonSize*0.5 - self:titleBarHeight()
    local reorderButton = ISButton:new(xPos, yPos, buttonSize, buttonSize, "", self)

    reorderButton.anchorLeft = false
    reorderButton.anchorRight = true
    reorderButton.anchorTop = false
    reorderButton.anchorBottom = true
    reorderButton:setImage(getTexture("media/ui/ReorderContainers/reorder-icon.png"))

    reorderButton.onMouseDown = function(self, x, y)
        local selectedButton = self:getParent().selectedButton
        if selectedButton then
            local x = getCore():getScreenWidth() / 2
            local y = getCore():getScreenHeight() / 2
            local popup = ReorderContainers_ManualPopup:new(x - 100, y - 60, self:getParent(), selectedButton.inventory)
            popup:initialise()
            popup:setAlwaysOnTop(true)
            popup:setCapture(true)
            popup:addToUIManager()
        end
    end

    reorderButton:setTooltip(getTextOrNull("UI_ReorderContainers_Tooltip") or "Sort Options")

    reorderButton:initialise()
    reorderButton:instantiate()
    self:addChild(reorderButton)
    self.reorderOptionsButton = reorderButton
end ]]

local function updateLock(self, lockButton)
    local newState = nil
    if self.onCharacter then
        newState = ReorderContainers_Mod.toggleInventoryLock(getSpecificPlayer(self.player))
    else
        newState = ReorderContainers_Mod.toggleLootLock(getSpecificPlayer(self.player))
    end
    
    if newState or not ReorderContainers_Mod.canReorderBackpacks(self) then 
        lockButton:setImage(getTexture("media/ui/ReorderContainers/reorder-locked.png"))
    else
        lockButton:setImage(getTexture("media/ui/ReorderContainers/reorder-unlocked.png"))
    end
end

ISInventoryPage.updateReorderContainersLock = function(self)
    if self.onCharacter then
        return
    end

    ReorderContainers_Mod.toggleLootLock(getSpecificPlayer(self.player))
    updateLock(self, self.reorderLockButton)
end

-- =========================================================
-- Shared UI helpers
-- =========================================================

--- Compute X/Y for a button in the vanilla bottom-right row.
-- @param self ISInventoryPage
-- @param indexFromRight number (1 = rightmost, 2 = second-from-right, etc.)
-- @param size number (button width/height)
-- @return x, y numbers
local function _computeButtonFrame(self, indexFromRight, size)
    -- X: pack from the right edge
    local x = self:getWidth() - (size * indexFromRight)

    -- Y: safe fallback using current page height + title bar (early frames)
    local y = self:getHeight() - size * 0.5 - self:titleBarHeight()
    return x, y
end

--- Wire an anchored, pinned tooltip to a button.
-- Tooltip text is provided by getTextFn() each hover tick.
-- @param btn ISButton
-- @param getTextFn function():string
local function _wirePinnedTooltip(btn, getTextFn)
    btn.onMouseMove = function(selfBtn, dx, dy)
        if not selfBtn._tooltipWnd then
            local t = ISToolTip:new()
            t:initialise()
            t:addToUIManager()
            t:setOwner(selfBtn)
            t.maxLineWidth = 250
            -- Temporary Hack (Build 42.11) — keep above other UI
            if type(t.setAlwaysOnTop) == "function" then t:setAlwaysOnTop(true) end
            selfBtn._tooltipWnd = t
        end
        local text = ""
        if type(getTextFn) == "function" then
            text = tostring(getTextFn() or "")
        elseif type(selfBtn._tooltipText) == "string" then
            -- Fallback for legacy callers
            text = selfBtn._tooltipText
        end
        selfBtn._tooltipWnd.description = text
        selfBtn._tooltipWnd:setDesiredPosition(selfBtn:getAbsoluteX(), selfBtn:getAbsoluteY() + selfBtn:getHeight() + 8)
        if type(selfBtn._tooltipWnd.bringToTop) == "function" then selfBtn._tooltipWnd:bringToTop() end
    end

    local function _killTip(selfBtn)
        if selfBtn._tooltipWnd then
            selfBtn._tooltipWnd:removeFromUIManager()
            selfBtn._tooltipWnd = nil
        end
    end

    btn.onMouseMoveOutside = _killTip
    btn.onRightMouseUpOutside = _killTip
end

--- Center vs controls row when usable; otherwise recompute a bottom-aligned Y
--- from current page geometry each frame (so resizes never drift).
local function _centerInControlsRow(page, btn)
    local originalPrerender = btn.prerender
    btn.prerender = function(b)
        if originalPrerender then originalPrerender(b) end

        local row = page.controlsUI
        local rowOK = row and row:isVisible() and (row:getHeight() or 0) > 0

        --[[if rowOK then
            local centeredY = row:getY() + (row:getHeight() - b:getHeight()) * 0.5
            if math.abs(b:getY() - centeredY) > 0.1 then
                b:setY(math.floor(centeredY))
            end]]
        --else
            -- Recompute every frame based on current height + titlebar.
			local fallbackY = page:getHeight() - b:getHeight() * 0.5 - page:titleBarHeight() - 6
            fallbackY = math.floor(fallbackY)
            if math.abs(b:getY() - fallbackY) > 0.1 then
                b:setY(fallbackY)
            end
        --end
    end
end

--- Factory to create a bottom-right anchored icon button with pinned tooltip & row-centering.
-- @param self ISInventoryPage
-- @param opt table {
--   indexFromRight: number,            -- 1=rightmost, 2=second, ...
--   size: number,                      -- button size
--   getTooltipText: function():string, -- tooltip text provider (called on hover)
--   onClick: function(btn)             -- primary click handler (LMB)
--   onMouseDown: function(btn,x,y)     -- alternative mouse down (optional)
--   applyVisualState: function(btn)    -- set icon/tooltip based on state (optional; called now and after onClick if provided)
-- }
-- @return ISButton
local function _makeIconButton(self, opt)
    local size = assert(opt.size, "size required")
    local idx  = opt.indexFromRight or 1
    local x, y = _computeButtonFrame(self, idx, size)

    local btn = ISButton:new(x, y, size, size, "", self)

    -- Anchors: stick to bottom-right
    btn.anchorLeft, btn.anchorRight = false, true
    btn.anchorTop,  btn.anchorBottom = false, true

    -- Optional state visual setup (icon + default tooltip text)
    if type(opt.applyVisualState) == "function" then
        opt.applyVisualState(btn)
    end

    -- Click behavior
    if type(opt.onClick) == "function" then
        btn:setOnClick(function()
            opt.onClick(btn)
            -- Refresh visuals after state change
            if type(opt.applyVisualState) == "function" then
                opt.applyVisualState(btn)
            end
        end)
    end
    if type(opt.onMouseDown) == "function" then
        btn.onMouseDown = opt.onMouseDown
    end

    -- Tooltip lifecycle
    _wirePinnedTooltip(btn, opt.getTooltipText)

    -- Vertical centering vs vanilla row
    _centerInControlsRow(self, btn)

    btn:initialise()
    btn:instantiate()
    self:addChild(btn)
    return btn
end

-- =========================================================
-- Button creators (refactored to use the shared factory)
-- =========================================================

-- Create the "Sorting Priority" button (rightmost)
ISInventoryPage.createReorderContainersSortButton = function(self)
    local size = self.buttonSize / 2

    local sortBtn = _makeIconButton(self, {
        indexFromRight = 1,
        size = size,

        applyVisualState = function(btn)
            btn:setImage(getTexture("media/ui/ReorderContainers/reorder-icon.png"))
            -- keep a fallback string on the button; factory also supports a provider fn
            btn._tooltipText = getTextOrNull("UI_ReorderContainers_Priority_tooltip") or "Sorting Priority"
        end,

        getTooltipText = function()
            return getTextOrNull("UI_ReorderContainers_Priority_tooltip") or "Sorting Priority"
        end,

        -- Open manual popup centered on screen for the selected container
        onMouseDown = function(btn, mx, my)
            local page = btn:getParent()
            local selectedButton = page and page.selectedButton
            if not selectedButton then return end

            local cx = getCore():getScreenWidth() / 2
            local cy = getCore():getScreenHeight() / 2
            local popup = ReorderContainers_ManualPopup:new(cx - 100, cy - 60, page, selectedButton.inventory)
            popup:initialise()
            popup:setAlwaysOnTop(true) -- Temporary Hack (Build 42.11) — may be no-op if API absent
            popup:setCapture(true)
            popup:addToUIManager()
        end,
    })

    self.reorderOptionsButton = sortBtn
end

-- Create the Lock/Unlock button (second from right), stateful visuals
ISInventoryPage.createReorderContainersLockButton = function(self)
    local size = self.buttonSize / 2

    local function _applyLockVisuals(btn)
        local locked = ReorderContainers_Mod.isLocked(self) or not ReorderContainers_Mod.canReorderBackpacks(self)
        if locked then
            btn:setImage(getTexture("media/ui/ReorderContainers/reorder-locked.png"))
            btn._tooltipText = getTextOrNull("UI_ReorderContainers_Unlock_tooltip") or "Unlock Container Order"
        else
            btn:setImage(getTexture("media/ui/ReorderContainers/reorder-unlocked.png"))
            btn._tooltipText = getTextOrNull("UI_ReorderContainers_Lock_tooltip") or "Lock Container Order"
        end
        -- keep any live tooltip window in sync if open
        if btn._tooltipWnd then btn._tooltipWnd.description = btn._tooltipText end
    end

    local lockBtn = _makeIconButton(self, {
        indexFromRight = 2,
        size = size,

        applyVisualState = _applyLockVisuals,

        getTooltipText = function()
            -- Always derive from current state
            local locked = ReorderContainers_Mod.isLocked(self) or not ReorderContainers_Mod.canReorderBackpacks(self)
            if locked then
                return getTextOrNull("UI_ReorderContainers_Unlock_tooltip") or "Unlock Container Order"
            else
                return getTextOrNull("UI_ReorderContainers_Lock_tooltip") or "Lock Container Order"
            end
        end,

        onClick = function(btn)
            -- If reordering is disallowed, bounce to the options popup for clarity
            if not ReorderContainers_Mod.canReorderBackpacks(self) then
                if self.reorderOptionsButton and self.reorderOptionsButton.onMouseDown then
                    self.reorderOptionsButton:onMouseDown(0, 0)
                end
                return
            end
            -- Your existing toggle logic encapsulated in updateLock(self, btn)
            updateLock(self, btn)
            -- visuals will be refreshed by factory via applyVisualState
        end,
    })

    self.reorderLockButton = lockBtn
end

local function isButtonValid(invPage, button)
    local panel = invPage and invPage.containerButtonPanel
    return button:getIsVisible() and panel and panel.children and panel.children[button.ID] ~= nil
end

ISInventoryPage.reorderContainerButtons = function(self, draggedButton)
    -- Don't reorder if the button hasn't moved far enough
    if draggedButton and math.abs(draggedButton:getY() - draggedButton.reorderStartY) <= 32 then
        draggedButton:setY(draggedButton.reorderStartY)
        return
    end

    local playerObj = getSpecificPlayer(self.player)

    local inventoriesAndY = {}
    for index, button in ipairs(self.backpacks) do
        if isButtonValid(self, button) then
            table.insert(inventoriesAndY, {inventory = button.inventory, y = button:getY()})
        end
    end
    table.sort(inventoriesAndY, function(a, b) return a.y < b.y end)

    local seenObjs = {}
    local lastSort = 0
    for index, data in ipairs(inventoriesAndY) do
        local targetModData, sortKey, parent = ReorderContainers_Mod.getTargetModDataAndSortKeyAndParentObject(playerObj, data.inventory)
        local isManual = targetModData and targetModData[SET_MANUALLY]
        local isDraggedButton = data.inventory == draggedButton.inventory

        if not isDraggedButton and parent and seenObjs[parent] then
            -- Skip this button, some IsoObjects have multiple inventories
        else
            if parent then
                seenObjs[parent] = true
            end

            local savedSort = tonumber(targetModData[sortKey])
            if not isManual or isDraggedButton or savedSort == nil then
                lastSort = lastSort + 10
                targetModData[sortKey] = lastSort
                targetModData[SET_MANUALLY] = nil
            else
                lastSort = savedSort
                -- Look back one button
                if index > 1 then
                    local prevInventory = inventoriesAndY[index - 1].inventory
                    local prevSort = ReorderContainers_Mod.getSortPriority(playerObj, prevInventory)
                    if prevSort >= lastSort then
                        ReorderContainers_Mod.setSortPriority(playerObj, prevInventory, lastSort - 1, false)
                    end
                end
            end
        end
    end
end

ISInventoryPage.applyBackpackOrder = function(self)
    local playerObj = getSpecificPlayer(self.player)
    local panel = self.containerButtonPanel -- buttons live here now

    local buttonsAndSort = {}
    for index, button in ipairs(self.backpacks) do
        if isButtonValid(self, button) then
            local sort = 1000 + index
            local targetModData, sortKey = ReorderContainers_Mod.getTargetModDataAndSortKeyAndParentObject(playerObj, button.inventory)
            if targetModData then
                sort = targetModData[sortKey] or (1000 + index)
            end
            table.insert(buttonsAndSort, { button = button, sort = sort })
        end
    end

    table.sort(buttonsAndSort, function(a, b) return a.sort < b.sort end)

    -- Y coordinates are relative to the PANEL (top of the button list),
    -- so don't add page:titleBarHeight() anymore.
    for index, data in ipairs(buttonsAndSort) do
        data.button:setY((index - 1) * self.buttonSize)
    end
end

ISInventoryPage.pre_reorder_refreshBackpacks = ISInventoryPage.refreshBackpacks
ISInventoryPage.refreshBackpacks = function(self)
    if self.killTheChoice then -- Makes controller work...?
        self.backpackChoice = nil
        self.killTheChoice = false
    end
    
    self:pre_reorder_refreshBackpacks()
    if ReorderContainers_Mod.canReorderBackpacks(self) then
        self:applyBackpackOrder()
    end
    self.pendingReorder = false
end

ISInventoryPage.pre_reorder_onMouseWheel = ISInventoryPage.onMouseWheel
ISInventoryPage.onMouseWheel = function(self, del)
    -- Store the original order of the backpacks
    local originalOrder = {}
    for index, button in ipairs(self.backpacks) do
        originalOrder[button] = index
    end

    -- Sort the backpacks by their Y position so that scrolling works as expected
    table.sort(self.backpacks, function(a, b) return a:getY() < b:getY() end)
    
    -- The backpacks *might* get refreshed by the mousescroll, so we track that
    self.pendingReorder = true

    local retVal = self:pre_reorder_onMouseWheel(del)

    -- The backpacks were not refreshed, so we need to restore the original order
    if self.pendingReorder then
        table.sort(self.backpacks, function(a, b) return originalOrder[a] < originalOrder[b] end)
        self.pendingReorder = false
    end

    return retVal
end

local function handleJoypadDown(self, target, button)
    -- Store the original order of the backpacks
    local originalOrder = {}
    for index, button in ipairs(target.backpacks) do
        originalOrder[button] = index
    end

    -- Sort the backpacks by their Y position so that scrolling works as expected
    table.sort(target.backpacks, function(a, b) return a:getY() < b:getY() end)

    -- Clear the 'backpackChoice', not sure what its actually for, but we stop it from existing on bumper inputs
    target.killTheChoice = true

    local retVal = self:pre_reorder_onJoypadDown(button)
    table.sort(target.backpacks, function(a, b) return originalOrder[a] < originalOrder[b] end)
    return retVal
end

ISInventoryPage.pre_reorder_onJoypadDown = ISInventoryPage.onJoypadDown
ISInventoryPage.onJoypadDown = function(self, button)
    if button == Joypad.LBumper then
        return handleJoypadDown(self, getPlayerInventory(self.player), button)
    end

    if button == Joypad.RBumper then
        local lootPage = getPlayerLoot(self.player)
        if ReorderContainers_Mod.canReorderBackpacks(lootPage) then
            return handleJoypadDown(self, lootPage, button)
        end
    end

    return self:pre_reorder_onJoypadDown(button)
end
