-- @docclass
UIMiniWindowContainer = extends(UIWidget, 'UIMiniWindowContainer')

function UIMiniWindowContainer.create()
    local container = UIMiniWindowContainer.internalCreate()
    container.scheduledWidgets = {}
    container.processingNewWindow = false  -- Flag to prevent recursive calls
    container:setFocusable(false)
    container:setPhantom(true)
    return container
end

-- TODO: connect to window onResize event
-- TODO: try to resize another widget?
-- TODO: try to find another panel?
function UIMiniWindowContainer:fitAll(noRemoveChild, isNewWindow)
    if not self:isVisible() then
        return
    end

    if self.ignoreFillAll then
        return
    end

    -- Prevent recursive calls during new window processing
    if self.processingNewWindow and not isNewWindow then
        return
    end

    -- Track if noRemoveChild was explicitly passed (window resize) or defaulted (inventory resize)
    local preserveWindow = noRemoveChild ~= nil

    if not noRemoveChild then
        local children = self:getChildren()
        if #children > 0 then
            noRemoveChild = children[#children]
        else
            return
        end
    end

    local children = self:getChildren()

    -- Default to false if not explicitly specified (only container opening passes true)
    if isNewWindow == nil then
        isNewWindow = false
    end

    -- Set new windows to minimum height BEFORE calculating total height
    if isNewWindow then
        self.processingNewWindow = true  -- Set flag BEFORE setHeight to block recursive calls

        if noRemoveChild.getMinimumHeight and noRemoveChild:getMinimumHeight() then
            local minHeight = noRemoveChild:getMinimumHeight()
            if noRemoveChild:getHeight() ~= minHeight then
                noRemoveChild:setHeight(minHeight)
            end
        end
    end

    -- Calculate total height (after setting new window to minimum)
    local sumHeight = 0
    for i = 1, #children do
        if children[i]:isVisible() then
            sumHeight = sumHeight + children[i]:getHeight()
        end
    end

    local selfHeight = self:getHeight() - (self:getPaddingTop() + self:getPaddingBottom())

    if sumHeight <= selfHeight then
        if self.processingNewWindow then
            self.processingNewWindow = false  -- Clear flag before early return
        end
        return
    end

    local removeChildren = {}

    -- New window opening logic: shrink/close ONLY the last window
    if isNewWindow then
        local deficit = sumHeight - selfHeight

        -- Find the window visually closest to new window (highest Y = bottom of screen)
        local lastWindow = nil
        local highestY = -1
        for i = 1, #children do
            local child = children[i]
            if child ~= noRemoveChild and child:isVisible() then
                local childY = child:getY()
                if childY > highestY then
                    highestY = childY
                    lastWindow = child
                end
            end
        end

        if lastWindow and deficit > 0 then
            -- First try: Shrink last window to minimum
            if lastWindow.isResizeable and lastWindow:isResizeable() then
                local currentH = lastWindow:getHeight()
                local minH = lastWindow.getMinimumHeight and lastWindow:getMinimumHeight() or currentH
                local shrinkable = currentH - minH

                if shrinkable > 0 then
                    local shrinkAmount = math.min(shrinkable, deficit)
                    local newHeight = currentH - shrinkAmount
                    lastWindow:setHeight(newHeight)
                    sumHeight = sumHeight - shrinkAmount
                    deficit = deficit - shrinkAmount
                end
            end

            -- Second try: If still not enough space, close last window (save or not)
            if deficit > 0 then
                table.insert(removeChildren, lastWindow)
            end
        end

        self.processingNewWindow = false  -- Clear flag after processing
    else
        -- Original resize logic: only shrink noRemoveChild if it was explicitly passed (window resize)
        if preserveWindow then
            local maximumHeight = selfHeight - (sumHeight - noRemoveChild:getHeight())
            if noRemoveChild:isResizeable() and noRemoveChild:getHeight() > maximumHeight and noRemoveChild:getMinimumHeight() <= maximumHeight then
                sumHeight = sumHeight - noRemoveChild:getHeight() + maximumHeight
                noRemoveChild:setHeight(maximumHeight)
            end
        end

        -- Sort children by visual position (Y coordinate) - bottom to top
        local sortedChildren = {}
        for i = 1, #children do
            -- Only exclude noRemoveChild if it was explicitly passed (preserve window being resized)
            -- When called from inventory resize (preserveWindow=false), include ALL windows
            if children[i]:isVisible() and (not preserveWindow or children[i] ~= noRemoveChild) then
                table.insert(sortedChildren, children[i])
            end
        end

        table.sort(sortedChildren, function(a, b)
            return a:getY() > b:getY()  -- Descending Y = bottom to top
        end)

        -- Shrink/close windows by visual order (bottom to top)
        -- Try to shrink first, only close if already at minimum
        for i = 1, #sortedChildren do
            if sumHeight <= selfHeight then
                break
            end

            local child = sortedChildren[i]
            local currentH = child:getHeight()
            local minH = child.getMinimumHeight and child:getMinimumHeight() or currentH
            local deficit = sumHeight - selfHeight

            -- Try to shrink if window is resizeable and above minimum
            if child.isResizeable and child:isResizeable() and currentH > minH then
                local shrinkable = currentH - minH
                local shrinkAmount = math.min(shrinkable, deficit)
                local newHeight = currentH - shrinkAmount
                child:setHeight(newHeight)
                sumHeight = sumHeight - shrinkAmount
            else
                -- Can't shrink (already at minimum or not resizeable), must close
                sumHeight = sumHeight - currentH
                table.insert(removeChildren, child)
            end
        end
    end

    -- Close widgets
    for i = 1, #removeChildren do
        removeChildren[i]:close()
    end

    -- Ensure flag is cleared (in case of early returns in future)
    if self.processingNewWindow then
        self.processingNewWindow = false
    end
end

function UIMiniWindowContainer:calculateDynamicMaximum(resizingWindow)
    if not self:isVisible() or self.ignoreFillAll then
        return 1000 -- Default maximum
    end

    local children = self:getChildren()
    local resizingIndex = self:getChildIndex(resizingWindow)
    if not resizingIndex then
        return 1000
    end

    -- Calculate container available space
    local containerHeight = self:getHeight() - (self:getPaddingTop() + self:getPaddingBottom())

    -- Sum heights of windows ABOVE resizing window
    local heightsAbove = 0
    for i = 1, resizingIndex - 1 do
        if children[i]:isVisible() then
            heightsAbove = heightsAbove + children[i]:getHeight()
        end
    end

    -- Sum minimum heights of windows BELOW resizing window
    local minHeightsBelow = 0
    for i = resizingIndex + 1, #children do
        local child = children[i]
        if child:isVisible() then
            if child.getMinimumHeight and type(child.getMinimumHeight) == 'function' then
                minHeightsBelow = minHeightsBelow + child:getMinimumHeight()
            else
                -- Fallback: use current height if no minimum defined
                minHeightsBelow = minHeightsBelow + child:getHeight()
            end
        end
    end

    -- Dynamic maximum = space available after accounting for windows above/below
    local dynamicMax = containerHeight - heightsAbove - minHeightsBelow

    -- Return at least the minimum height of resizing window
    if resizingWindow.getMinimumHeight and type(resizingWindow.getMinimumHeight) == 'function' then
        return math.max(dynamicMax, resizingWindow:getMinimumHeight())
    end

    return math.max(dynamicMax, 0)
end

function UIMiniWindowContainer:shrinkWindowsBelow(resizingWindow, newHeight)
    if not self:isVisible() or self.ignoreFillAll then
        return
    end

    local children = self:getChildren()
    local resizingIndex = self:getChildIndex(resizingWindow)
    if not resizingIndex then
        return
    end

    -- Calculate space deficit (how much we're growing)
    local currentHeight = resizingWindow:getHeight()
    local heightIncrease = newHeight - currentHeight

    if heightIncrease <= 0 then
        return -- Shrinking or no change, no need to shrink below
    end

    -- Collect windows below with their shrinkable space
    local windowsBelow = {}
    local totalShrinkable = 0

    for i = resizingIndex + 1, #children do
        local child = children[i]
        if child:isVisible() and child.isResizeable and child:isResizeable() then
            local currentH = child:getHeight()
            local minH = child.getMinimumHeight and child:getMinimumHeight() or currentH
            local shrinkable = currentH - minH

            if shrinkable > 0 then
                table.insert(windowsBelow, {
                    window = child,
                    currentHeight = currentH,
                    minHeight = minH,
                    shrinkable = shrinkable
                })
                totalShrinkable = totalShrinkable + shrinkable
            end
        end
    end

    if totalShrinkable <= 0 then
        return -- Nothing to shrink
    end

    -- Distribute shrinkage proportionally (bottom to top for smoothness)
    local remainingToShrink = math.min(heightIncrease, totalShrinkable)

    for i = #windowsBelow, 1, -1 do
        local windowData = windowsBelow[i]
        local proportionalShrink = (windowData.shrinkable / totalShrinkable) * heightIncrease
        local actualShrink = math.min(proportionalShrink, windowData.shrinkable, remainingToShrink)

        local newWindowHeight = windowData.currentHeight - actualShrink

        -- Apply shrink directly without triggering fitAll (to avoid recursion)
        windowData.window:setHeight(newWindowHeight)

        remainingToShrink = remainingToShrink - actualShrink

        if remainingToShrink <= 0 then
            break
        end
    end
end

function UIMiniWindowContainer:fits(child, minContentHeight, maxContentHeight)
    if self.ignoreFillAll then
        return 0
    end

    local containerPanel = child:getChildById('contentsPanel')
    local indispensableHeight = containerPanel:getMarginTop() + containerPanel:getMarginBottom() +
        containerPanel:getPaddingTop() + containerPanel:getPaddingBottom()

    local totalHeight = 0
    local children = self:getChildren()
    for i = 1, #children do
        if children[i]:isVisible() then
            totalHeight = totalHeight + children[i]:getHeight()
        end
    end

    local available = self:getHeight() - (self:getPaddingTop() + self:getPaddingBottom()) - totalHeight

    if maxContentHeight > 0 and available >= (maxContentHeight + indispensableHeight) then
        return maxContentHeight + indispensableHeight
    elseif available >= (minContentHeight + indispensableHeight) then
        return available
    else
        return -1
    end
end

function UIMiniWindowContainer:onDrop(widget, mousePos)
    if (self.onlyPhantomDrop and not (widget.moveOnlyToMain)) or (widget.moveOnlyToMain and not (self.onlyPhantomDrop)) then
        return true
    end

    if widget.UIMiniWindowContainer then
        local oldParent = widget:getParent()
        if oldParent == self then
            return true
        end

        if oldParent then
            oldParent:removeChild(widget)
        end

        if widget.movedWidget then
            local index = self:getChildIndex(widget.movedWidget)
            self:insertChild(index + widget.movedIndex, widget)
        else
            self:addChild(widget)
        end

        if widget:getId() == "botWindow" and
            (widget:getParent():getId() == "gameLeftPanel" or widget:getParent():getId() == "gameLeftExtraPanel" or
                widget:getParent():getId() == "gameRightExtraPanel") then
            widget:getParent():setWidth(190)
        end
        self:fitAll(widget)
        return true
    end
end

function UIMiniWindowContainer:swapInsert(widget, index)
    local oldParent = widget:getParent()
    local oldIndex = self:getChildIndex(widget)

    if oldParent == self and oldIndex ~= index then
        local oldWidget = self:getChildByIndex(index)
        if oldWidget then
            self:removeChild(oldWidget)
            self:insertChild(oldIndex, oldWidget)
        end
        self:removeChild(widget)
        self:insertChild(index, widget)
    end
end

function UIMiniWindowContainer:scheduleInsert(widget, index)
    if index - 1 > self:getChildCount() then
        if self.scheduledWidgets[index] then
            pdebug('replacing scheduled widget id ' .. widget:getId())
        end
        self.scheduledWidgets[index] = widget
    else
        local oldParent = widget:getParent()
        if oldParent ~= self then
            if oldParent then
                oldParent:removeChild(widget)
            end
            self:insertChild(index, widget)

            while true do
                local placed = false
                for nIndex, nWidget in pairs(self.scheduledWidgets) do
                    if nIndex - 1 <= self:getChildCount() then
                        self:insertChild(nIndex, nWidget)
                        self.scheduledWidgets[nIndex] = nil
                        placed = true
                        break
                    end
                end
                if not placed then
                    break
                end
            end
        end
    end
end

function UIMiniWindowContainer:order()
    local children = self:getChildren()
    for i = 1, #children do
        if not children[i].miniLoaded then
            return
        end
    end

    for i = 1, #children do
        if children[i].miniIndex then
            self:swapInsert(children[i], children[i].miniIndex)
        end
    end
end

function UIMiniWindowContainer:saveChildren()
    local children = self:getChildren()
    local ignoreIndex = 0
    for i = 1, #children do
        if children[i].save then
            children[i]:saveParentIndex(self:getId(), i - ignoreIndex)
        else
            ignoreIndex = ignoreIndex + 1
        end
    end
end
