require "ISUI/ISPanel"
require "ISUI/ISTextEntryBox"
require "ISUI/ISLabel"
require "MS/SettingPanel/MS_SquareButton"
require "MS/SettingPanel/MS_LongButton"
require "MS/MS_GridManager"

MS_GridSizeDialog = ISPanel:derive("MS_GridSizeDialog")

local FONT_HGT_SMALL = getTextManager():getFontHeight(UIFont.Small)
local GRID_MIN_VALUE = 1
local GRID_MAX_VALUE = 50
local GRID_STEP = 5

MS_GridSizeDialog.CurrentPanel = nil

function MS_GridSizeDialog.calculateRequiredSize()
    local rowHeight = math.floor(FONT_HGT_SMALL * 1.5)
    local titleBarHeight = math.floor(FONT_HGT_SMALL * 1.2)
    local buttonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    local padding = math.floor(FONT_HGT_SMALL * 0.2)
    local width = 360
    local height = titleBarHeight + padding * 5 + rowHeight * 2 + buttonHeight
    return { width = width, height = height }
end

local function sanitizeGridValue(rawValue, fallback)
    local value = tonumber(rawValue)
    if not value then
        value = fallback or 10
    end

    -- Clamp user input so typed values match the limits enforced by the grid manager.
    value = math.floor(value)
    if value < GRID_MIN_VALUE then
        value = GRID_MIN_VALUE
    elseif value > GRID_MAX_VALUE then
        value = GRID_MAX_VALUE
    end

    return value
end

local function stepGridValue(currentValue, delta)
    local value = sanitizeGridValue(currentValue, 10)
    if delta > 0 and value <= GRID_MIN_VALUE then
        return math.min(GRID_MAX_VALUE, GRID_STEP)
    end
    return sanitizeGridValue(value + delta, value)
end

function MS_GridSizeDialog.openForPlayer(playerIndex, anchorX, anchorY)
    if MS_GridSizeDialog.CurrentPanel then
        MS_GridSizeDialog.CurrentPanel:close()
    end

    local screenWidth = getCore():getScreenWidth()
    local screenHeight = getCore():getScreenHeight()
    local size = MS_GridSizeDialog.calculateRequiredSize()

    local x = anchorX or math.floor((screenWidth - size.width) / 2)
    local y = anchorY or math.floor((screenHeight - size.height) / 2)

    local panel = MS_GridSizeDialog:new(x, y, nil, nil, playerIndex)
    panel:initialise()
    panel:addToUIManager()
    panel:setVisible(true)

    panel:setX(math.max(10, math.min(screenWidth - panel:getWidth() - 10, panel:getX())))
    panel:setY(math.max(10, math.min(screenHeight - panel:getHeight() - 10, panel:getY())))

    MS_GridSizeDialog.CurrentPanel = panel
    return panel
end

function MS_GridSizeDialog:initialise()
    ISPanel.initialise(self)
end

function MS_GridSizeDialog:new(x, y, width, height, playerIndex)
    local size = MS_GridSizeDialog.calculateRequiredSize()
    width = width or size.width
    height = height or size.height

    local o = ISPanel:new(x, y, width, height)
    setmetatable(o, self)
    self.__index = self

    o.playerIndex = playerIndex
    o.padding = math.floor(FONT_HGT_SMALL * 0.2)
    o.titleBarHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.rowHeight = math.floor(FONT_HGT_SMALL * 1.5)
    o.buttonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.contentButtonHeight = math.floor(FONT_HGT_SMALL * 1.2)
    o.buttonSize = math.floor(FONT_HGT_SMALL * 1.2)
    o.moving = false
    o.moveWithMouse = false
    o:noBackground(true)

    o.minusIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Minus.png")
    o.plusIcon = getTexture("media/ui/ModernStatus/SettingPanel/Icon_Plus.png")
    o.title = getText("IGUI_ModernStatus_EnterGridSize")

    o.titlebarbkg = NinePatchTexture and NinePatchTexture.getSharedTexture and NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/TitleBarBG.png") or nil
    o.contentbkg = NinePatchTexture and NinePatchTexture.getSharedTexture and NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/ContentBackground.png") or nil

    return o
end

function MS_GridSizeDialog:close()
    if MS_GridSizeDialog.CurrentPanel == self then
        MS_GridSizeDialog.CurrentPanel = nil
    end
    self:setVisible(false)
    self:removeFromUIManager()
end

function MS_GridSizeDialog:createChildren()
    self:createTitleBar()
    self:createContent()
    self:createBottomButtons()
end

function MS_GridSizeDialog:createTitleBar()
    local titleX = math.floor((self.width - getTextManager():MeasureStringX(UIFont.Small, self.title)) / 2)
    self.titleLabel = ISLabel:new(titleX, (self.titleBarHeight - FONT_HGT_SMALL) / 2, self.titleBarHeight, self.title, 1, 1, 1, 1, UIFont.Small, true)
    self.titleLabel:initialise()
    self:addChild(self.titleLabel)
end

function MS_GridSizeDialog:createLabel(text, y)
    local label = ISLabel:new(self.padding, y + ((self.rowHeight - FONT_HGT_SMALL) / 2), 0, text, 1, 1, 1, 1, UIFont.Small, true)
    label:initialise()
    self:addChild(label)
    return label
end

function MS_GridSizeDialog:createStepperControl(x, y, labelText, currentValue, onValueChanged)
    self:createLabel(labelText, y)

    local textBoxWidth = getTextManager():MeasureStringX(UIFont.Small, "999") + self.padding * 6

    local textBox

    local function updateTextBox()
        textBox:setText(tostring(sanitizeGridValue(currentValue(), 10)))
    end

    local function commitValue(rawValue)
        local value = sanitizeGridValue(rawValue, currentValue())
        onValueChanged(value)
        updateTextBox()
        return value
    end

    textBox = ISTextEntryBox:new(tostring(sanitizeGridValue(currentValue(), 10)), x + self.buttonSize + self.padding, y + ((self.rowHeight - self.buttonSize) / 2), textBoxWidth, self.buttonSize)
    textBox:initialise()
    textBox:instantiate()
    textBox:setOnlyNumbers(true)
    textBox.borderColor = {r = 0.4, g = 0.4, b = 0.4, a = 0.0}
    textBox.backgroundColor = {r = 0.1, g = 0.1, b = 0.1, a = 0.0}
    textBox.onCommandEntered = function() commitValue(textBox:getText()) end
    textBox.onOtherKey = function(key)
        if key == 28 then
            commitValue(textBox:getText())
        end
    end
    textBox.onMouseDownOutside = function()
        commitValue(textBox:getText())
    end
    textBox.prerender = function(control)
        ISTextEntryBox.prerender(control)
        if not control:isFocused() then
            updateTextBox()
        end
    end
    self:addChild(textBox)

    local decBtn = MS_SquareButton:new(x, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.minusIcon, self, function()
        commitValue(stepGridValue(currentValue(), -GRID_STEP))
    end)
    decBtn:initialise()
    self:addChild(decBtn)

    local incBtn = MS_SquareButton:new(x + self.buttonSize + self.padding + textBoxWidth + self.padding, y + ((self.rowHeight - self.buttonSize) / 2), self.buttonSize, self.plusIcon, self, function()
        commitValue(stepGridValue(currentValue(), GRID_STEP))
    end)
    incBtn:initialise()
    self:addChild(incBtn)

    return { decBtn = decBtn, textBox = textBox, incBtn = incBtn, commitValue = commitValue }
end

function MS_GridSizeDialog:createContent()
    local currentX, currentY = MS_GridManager.getDisplaySize(self.playerIndex)
    self.pendingGridX = sanitizeGridValue(currentX, 10)
    self.pendingGridY = sanitizeGridValue(currentY, 10)

    local middleX = math.floor(self.width / 2)
    local controlX = middleX + self.padding
    local y = self.titleBarHeight + self.padding * 2

    self.xControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_X"),
        function() return self.pendingGridX end,
        function(value) self.pendingGridX = sanitizeGridValue(value, self.pendingGridX) end)
    y = y + self.rowHeight

    self.yControls = self:createStepperControl(controlX, y, getText("IGUI_ModernStatus_Y"),
        function() return self.pendingGridY end,
        function(value) self.pendingGridY = sanitizeGridValue(value, self.pendingGridY) end)
end

function MS_GridSizeDialog:createBottomButtons()
    local buttonWidth = math.floor((self:getWidth() - self.padding * 4) / 3)
    local buttonY = self:getHeight() - self.buttonHeight - self.padding

    self.backButton = MS_LongButton:new(self.padding, buttonY, buttonWidth, self.buttonHeight, getText("IGUI_ModernStatus_Back"), self, MS_GridSizeDialog.onBackClick)
    self.backButton:initialise()
    self.backButton:setActive(true)
    self.backButton:setActiveColor(0.8, 0.2, 0.2)
    self:addChild(self.backButton)

    self.applyButton = MS_LongButton:new(self.backButton:getRight() + self.padding, buttonY, buttonWidth, self.buttonHeight, getText("IGUI_ModernStatus_Apply"), self, MS_GridSizeDialog.onApplyClick)
    self.applyButton:initialise()
    self.applyButton:setActive(true)
    self.applyButton:setActiveColor(0.2, 0.65, 0.2)
    self:addChild(self.applyButton)

    self.acceptButton = MS_LongButton:new(self.applyButton:getRight() + self.padding, buttonY, buttonWidth, self.buttonHeight, getText("IGUI_ModernStatus_Accept"), self, MS_GridSizeDialog.onAcceptClick)
    self.acceptButton:initialise()
    self.acceptButton:setActive(true)
    self.acceptButton:setActiveColor(0.2, 0.75, 0.2)
    self:addChild(self.acceptButton)
end

function MS_GridSizeDialog:applyValues(closeAfter)
    -- Commit any manually typed value before saving the shared grid settings.
    if self.xControls and self.xControls.commitValue then
        self.xControls.commitValue(self.xControls.textBox:getText())
    end
    if self.yControls and self.yControls.commitValue then
        self.yControls.commitValue(self.yControls.textBox:getText())
    end

    MS_GridManager.setSize(self.playerIndex, self.pendingGridX, self.pendingGridY)

    if closeAfter then
        self:close()
    end
end

function MS_GridSizeDialog:onBackClick()
    self:close()
end

function MS_GridSizeDialog:onApplyClick()
    self:applyValues(false)
end

function MS_GridSizeDialog:onAcceptClick()
    self:applyValues(true)
end

function MS_GridSizeDialog:onMouseDown(x, y)
    if y <= self.titleBarHeight then
        self.moving = true
        self:setCapture(true)
        return true
    end
    return ISPanel.onMouseDown(self, x, y)
end

function MS_GridSizeDialog:onMouseUp(x, y)
    if self.moving then
        self.moving = false
        self:setCapture(false)
    end
    return ISPanel.onMouseUp(self, x, y)
end

function MS_GridSizeDialog:onMouseMove(dx, dy)
    if self.moving then
        self:setX(self.x + dx)
        self:setY(self.y + dy)
        return true
    end
    return ISPanel.onMouseMove(self, dx, dy)
end

function MS_GridSizeDialog:onMouseMoveOutside(dx, dy)
    if self.moving then
        self:setX(self.x + dx)
        self:setY(self.y + dy)
        return true
    end
    return ISPanel.onMouseMoveOutside(self, dx, dy)
end

function MS_GridSizeDialog:onMouseUpOutside(x, y)
    if self.moving then
        self.moving = false
        self:setCapture(false)
    end
    return ISPanel.onMouseUpOutside(self, x, y)
end

function MS_GridSizeDialog:prerender()
    local titlebarbkg = self.titlebarbkg
    if not titlebarbkg and NinePatchTexture and NinePatchTexture.getSharedTexture then
        titlebarbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/TitleBarBG.png")
        self.titlebarbkg = titlebarbkg
    end

    if titlebarbkg and titlebarbkg.render then
        titlebarbkg:render(self:getAbsoluteX(), self:getAbsoluteY(), self:getWidth(), self.titleBarHeight, 0.5, 0.5, 0.5, 0.95)
    else
        self:drawRect(0, 0, self:getWidth(), self.titleBarHeight, 0.95, 0.5, 0.5, 0.5)
    end

    local contentbkg = self.contentbkg
    if not contentbkg and NinePatchTexture and NinePatchTexture.getSharedTexture then
        contentbkg = NinePatchTexture.getSharedTexture("media/ui/ModernStatus/SettingPanel/ContentBackground.png")
        self.contentbkg = contentbkg
    end

    if contentbkg and contentbkg.render then
        contentbkg:render(self:getAbsoluteX(), self:getAbsoluteY() + self.titleBarHeight, self:getWidth(), self:getHeight() - self.titleBarHeight, 0.08, 0.08, 0.08, 0.95)
    else
        self:drawRect(0, self.titleBarHeight, self:getWidth(), self:getHeight() - self.titleBarHeight, 0.95, 0.08, 0.08, 0.08)
    end

    self:drawRectBorder(0, 0, self:getWidth(), self:getHeight(), 0.4, 0.8, 0.8, 0.8)
end

return MS_GridSizeDialog
