--[[

    settings.lua
    ------------
    Defines the settings menu, handles the saving & loading of user settings.

--]]

--
--  Saving & Loading Settings
--

local function save_user_settings ()
    local userSettings = {}
    local windowSettingFields = {"scale", "height", "width", "letterbox"}
    for _, fieldName in ipairs(windowSettingFields) do
        table.insert(userSettings, string.format("Window.%s = %s", fieldName,
                                                 Window[fieldName]))
    end
    Storage["user_settings.lua"] = table.concat(userSettings, "\n")
end

local function load_user_settings ()
    if Storage["user_settings.lua"] then
        loadstring(Storage["user_settings.lua"])()
    end
end

load_user_settings();

--
--  Handling of Arrows
--

local function verify_setting_arrows ()
    -- Checks whether further change in left/right directions are possible
    --   for the currently selected mint, and alters the visibility of the
    --   left/right arrows accordingly.
    local selectedButton = self.mints[self.selectedMint]

    -- Make visible by default
    self.leftArrow.isVisible = true
    self.rightArrow.isVisible = true

    local function verify_setting_arrow (direction)
        local origValue = selectedButton.text.string
        local changePossible = false
        -- Attempt to change in given direction
        if pcall(function() selectedButton.alterationCallback(direction) end) then
            if origValue ~= selectedButton.text.string then
                -- Change is possible, restore to original state
                changePossible = true
                selectedButton.alterationCallback(-direction)
            end
        end
        if not changePossible then
            -- Further change is not possible, do not display the arrow
            if direction == -1 then
                self.leftArrow.isVisible = false
            else
                self.rightArrow.isVisible = false
            end
        end
    end

    verify_setting_arrow(-1)
    verify_setting_arrow(1)
end

local function position_setting_arrows ()
    -- Positions the setting arrows using to the position of the currently
    --   selected setting mint.
    local selectedButton = self.mints[self.selectedMint]

    -- Build the arrow positions in relation to the currently selected button
    local leftArrowXPos = tostring(selectedButton.position.x) .. " + 1 PIXEL"
    local rightArrowXPos = tostring(selectedButton.position.x) .. " + 35 PIXELS"
    local arrowYPos = tostring(selectedButton.position.y) .. " + 3 PIXELS"

    -- Place the arrows
    if not self.leftArrow then
        -- Arrows don't exist on the canvas, place them
        self.leftArrow = self.canvas:Place("COMMON_UI_TILESET LEFTARROW",
                                           leftArrowXPos, arrowYPos, -1)
        self.rightArrow = self.canvas:Place("COMMON_UI_TILESET LEFTARROW",
                                            rightArrowXPos, arrowYPos, -1,
                                            "HORIZONTAL_FLIP")
    else
        -- Arrows already exist on the canvas, adjust their positions
        self.leftArrow.x = leftArrowXPos
        self.leftArrow.y = arrowYPos
        self.rightArrow.x = rightArrowXPos
        self.rightArrow.y = arrowYPos
    end
end

--
--  Setting Mint Generation
--

local function setting_button_selection_script ()
    -- Function to be called when a setting mint is selected
    position_setting_arrows()
    verify_setting_arrows()
end

local function generate_mints_for_setting (labelText, buttonText,
                                           alterationCallback,
                                           basePos, i)
    -- Generates a pair of label - button mints for the given setting.
    -- Inserts generated mints into the menu.
    local labelMint = CreateMint({TEXT = labelText,
                                  TYPE = "LABEL",
                                  POSITION = basePos,
                                  DIMENSIONS = "(92 PIXELS x 1 TILE)"})
    local buttonMint = CreateMint({TEXT = buttonText,
                                  TYPE = "BUTTON",
                                  POSITION = basePos,
                                  DIMENSIONS = "(42 PIXELS x 13 PIXELS)",
                                  APPEARANCE = {
                                      TEXT = {
                                          ALIGNMENT = "CENTER",
                                          MARGIN = "3 PIXELS"
                                      },
                                  },
                                  SELECTION_SCRIPT = setting_button_selection_script})
    -- Adjust the positions
    labelMint.position.y.tiles = labelMint.position.y.tiles + i
    buttonMint.position.y.tiles = buttonMint.position.y.tiles + i
    buttonMint.position.y.pixels = buttonMint.position.y.pixels + 1
    buttonMint.position.x.tiles = 6
    -- Register the callback
    buttonMint.alterationCallback = alterationCallback
    -- Insert into the menu
    table.insert(self.mints, labelMint)
    table.insert(self.mints, buttonMint)
end

--
--  Callback Function Generation
--

local function boolean_to_onoff (x)
    if x then
        return "ON"
    else
        return "OFF"
    end
end

local function generate_window_setting_callback(fieldName, minVal)
    -- Generates alteration callbacks for numerical window settings
    return function (direction)
        local newVal = Window[fieldName] + direction
        if (not minVal or newVal >= minVal) and
            pcall(function () Window[fieldName] = newVal end) then
            self.mints[self.selectedMint].text = tostring(newVal)
        end
    end
end

local function letterbox_callback (direction)
    -- Alteration callback for the Window.letterbox option
    Window.letterbox = not Window.letterbox
    self.mints[self.selectedMint].text = boolean_to_onoff(Window.letterbox)
end

local function setting_toggle (direction)
    -- Generic setting alteration function, used by the menu keymap on
    --   left/right presses.
    local selectedMint = self.mints[self.selectedMint]
    local origValue = selectedMint.text.string
    if selectedMint.alterationCallback then
	selectedMint.alterationCallback(direction)
        -- If the value has changed, verify the arrows and save the setting
        if origValue ~= selectedMint.text.string then
            verify_setting_arrows()
            save_user_settings()
        end
    end
end

--
--  Menu Definition
--

CreateElement(
    "SETTINGS_MENU",
    "MENU OBJECT",
    {
        POSITION = "(180 SLICES - 5 TILES, 180 SLICES - 3 TILES - 8 PIXELS)",
        DIMENSIONS = "(10 TILES x 7 TILES)",
        MINTS = {
            MENU_TITLE = {
                TEXT = "Settings",
                TYPE = "LABEL",
                POSITION = "(0 TILE, 4 PIXELS)",
                DIMENSIONS = "(10 TILES x 1 TILE)",
                APPEARANCE = {
                    TEXT = {
                        ALIGNMENT = "CENTER"
                    }
                }
            },
            WINDOW_SETTINGS_TITLE = {
                TEXT = "Window",
                TYPE = "LABEL",
                POSITION = "(1 TILE, 1 TILE + 4 PIXELS)",
                DIMENSIONS = "(56 PIXELS x 1 TILE)",
                APPEARANCE = {
                    INACTIVE_COLOR = CommonPalette.MenuBG
                }
            },
            WINDOW_SETTINGS_BORDER = {
                TYPE = "LABEL",
                POSITION = "(8 PIXELS, 1 TILE + 12 PIXELS)",
                DIMENSIONS = "(9 TILES x 4 TILES + 12 PIXELS)",
                APPEARANCE = {
                    BORDER = {
                        WIDTH = "1 PIXEL",
                        INACTIVE_COLOR = "WHITE"
                    }
                }
            },
        },
        APPEARANCE = {
            BORDER = {
                WIDTH = "1 PIXEL",
                ACTIVE_COLOR = "WHITE",
            },
        },
        INIT_SCRIPT = function ()
            -- Guarantee that WINDOW_SETTINGS_BORDER is rendered first
            self.mints[1], self.mints.WINDOW_SETTINGS_BORDER =
                self.mints.WINDOW_SETTINGS_BORDER, nil
            -- Generate the setting mints
            local basePos = "(8 PIXELS, 4 PIXELS + 2 TILES)"
            generate_mints_for_setting("Scale", tostring(Window.scale),
                                       generate_window_setting_callback("scale"),
                                       basePos, 0)
            generate_mints_for_setting("Width", tostring(Window.width),
                                       generate_window_setting_callback("width", 16),
                                       basePos, 1)
            generate_mints_for_setting("Height", tostring(Window.height),
                                       generate_window_setting_callback("height", 14),
                                       basePos, 2)
            generate_mints_for_setting("Letterbox", boolean_to_onoff(Window.letterbox),
                                       letterbox_callback, basePos, 3)
        end,
        KEYMAP = {
            LEFT = function () setting_toggle(-1) end,
            RIGHT = function () setting_toggle(1) end
        }
    }
)
