
dofile(core.get_builtin_path().."common/settings/settingtypes.lua")

-- Some categories, like Accessibility and Theme, do not appear
-- in the builtin settingtypes.txt, so they are added to the state table here.
local function inject_settings(state)
    -- Theme
    table.insert(state.categories, 1, "Theme")
    state.settings.Theme = {
        {
            comment = "The background color of containers.",
            context = "client",
            default = "#fff",
            name = "theme.container_bg_color",
            readable_name = "Container background color",
            requires = {},
            type = "string",
            value = imfs.state("#fff")
        }
    }
    
    -- Accessibility
    table.insert(state.categories, 2, "Accessibility")
    state.settings.Accessibility = {
        {
            comment = "Smooths rotation of camera, also called look or mouse smoothing. 0 to disable.",
            context = "client",
            default = "0.0",
            max = 0.99,
            min = 0,
            name = "camera_smoothing",
            readable_name = "Camera smoothing",
            requires = {},
            type = "float",
            value = imfs.state("0.0")
        }
    }
end

local function search_settings(state, search)
    if state.search == "" then
        return state.settings, state.categories
    end
    
    local words = search:lower():trim():split(" ")
    
    local out = {}
    for category, settings in pairs(state.settings) do
        for i = 1, #settings do
            for j = 1, #words do
                local passed = true
                if not (settings[i].readable_name:lower():find(words[j], 1, true)
                        or settings[i].name:find(words[j], 1, true)) then
                    passed = false
                end
                
                if passed then
                    if not out[category] then
                        out[category] = {settings[i]}
                    else
                        table.insert(out[category], settings[i])
                    end
                end
            end
        end
    end
    
    local categories = {}
    for i = 1, #state.categories do
        local category = state.categories[i]
        if category.name then
            for j = 1, #state.categories do
                if out[category[j]] then
                    if not categories[category.name] then
                        table.insert(categories, {name = category.name})
                    end
                    table.insert(categories[#categories], category[j])
                    break
                end
            end
        else
            if out[category] then
                table.insert(categories, category)
                break
            end
        end
    end
        
    return out, categories
end

return function(state)
    imfs.begin(size.x, size.y)
        :padding(0, 0)
        :bgcolor(theme.styles.bg_color, true)
    
    if not state.settings then
        state.settings = {}
        state.categories = {}
        local category
        local settings = settingtypes.parse_config_file(false, false)
        for _, x in ipairs(settings) do
            if x.type == "category" then
                if x.level == 0 then
                    state.categories[#state.categories + 1] = {name = x.name}
                elseif x.level == 1 then
                    local group = state.categories[#state.categories]
                    group[#group + 1] = x.name
                    state.settings[x.name] = {}
                    category = x.name
                end
            else
                local s = state.settings[category]
                x.value = imfs.state()
                if x.type == "string" or x.type == "float" or x.type == "int" or x.type == "enum" then
                    x.value._val = minetest.settings:get(x.name) or x.default
                elseif x.type == "bool" then
                    x.value._val = minetest.settings:get_bool(x.name, x.default)
                end
                s[#s +1] = x
            end
        end
        
        inject_settings(state)
        
        state.settings_shown._val = state.settings
        state.categories_shown._val = state.categories
    end
    
    imfs.box(0, 0, "100%", 1, theme.styles.container.bg_color)

    imfs.row(0.2, 0.125, "100% - 0.4", 0.75)
        :gap(0.1)
        theme.field(0, 0, "1x", "100%", state.search)
            :onchange(function(value)
                state.search = value
            end)
            :onenter(function()
                local settings, categories = search_settings(state, state.search)
                if categories[1] then
                    local first = categories[1].name and categories[1][1] or categories[1]
                    state.category(first)
                end
                state.categories_shown(categories)
                state.settings_shown(settings)
            end)
        imfs.button(0, 0, 0.75, 0.75)
            :image(theme.icon("search"))
            :style({
                bgimg = theme.get_background_image("button"),
                bgimg_middle = theme.styles.button.border_width,
            })
            :tooltip("Search settings")
            :onclick(function()
                local settings, categories = search_settings(state, state.search)
                if categories[1] then
                    local first = categories[1].name and categories[1][1] or categories[1]
                    state.category(first)
                end
                state.categories_shown(categories)
                state.settings_shown(settings)
            end)

         imfs.button(0, 0, 0.75, 0.75)
             :image(theme.icon("cancel"))
             :style({
                 bgimg = theme.get_background_image("button"),
                 bgimg_middle = theme.styles.button.border_width,
             })
             :tooltip("Clear search")
             :onclick(function()
                 state.search = ""
                 state.settings_shown(state.settings)
                 state.categories_shown(state.categories)
             end)
        imfs.button(0, 0, 2, "100%", "Back")
            :onclick(function()
                show_meta_menu()
            end)
    imfs.row_end()
        
    imfs.box(0, 1, "20%", "100% - 1", theme.styles.container.bg_color)
    imfs.box(0, 0.95, "100%", 0.1, theme.styles.container.border_color)
        
    imfs.scroll_container(0.2, 1.05, "20% - 0.45", "100% - 1.05")
        local categories = state.categories_shown()
        if categories then
            local y = 0.1
            for i = 1, #categories do
                local group = categories[i]
                if not group then break end
                -- Top-level categories.
                if type(group) == "string" then
                    imfs.button(0, y, "100%", 0.5, group)
                        :onclick(function()
                            if state.category() ~= group then
                                state.category(group)
                            end
                        end)
                    y = y + 0.55
                -- Grouped categories.
                else
                    imfs.arealabel(0, y + 0.125, "100%", 0.375, group.name)
                    y = y + 0.425
                    local h = 0
                    for j = 1, #group do
                        local category = group[j]
                        imfs.box(0.12, y + 0.245, 0.25, 0.01, theme.styles.text_color)
                        imfs.button(0.25, y, "100% - 0.25", 0.5, category)
                            :onclick(function()
                                if state.category() ~= category then
                                    state.category(category)
                                end
                            end)
                        h = h + 0.55
                        y = y + 0.55
                    end
                    imfs.box(0.12, y - h, 0.01, h - 0.3, theme.styles.text_color)
                end
            end
        end
    imfs.scroll_container_end()
    
    imfs.box("20% - 0.05", 1, 0.1, "100% - 1", theme.styles.container.border_color)
    
    imfs.scroll_container("20% + 0.25", 1.05, "80% - 0.45", "100% - 1.05")
    imfs.column(0, 0, "100%", 1000)
        :gap(0.1)
        local settings = state.settings_shown()[state.category()]
        if settings then
            for i = 1, #settings do
                local setting = settings[i]
                if not setting then break end
                if setting.type == "bool" then
                    local active = minetest.settings:get_bool(setting.name, setting.default)
                    theme.checkbox(0, 0, setting.readable_name, setting.value, setting.comment)
                elseif setting.type == "float" or setting.type == "int" then
                    imfs.arealabel(0, 0, "100%", 0.325, setting.readable_name)
                    imfs.row(0, 0, "100%", 0.75)
                        :gap(0.1)
                        theme.number_field(0, 0, "1x", 0.75, setting.value)
                            :tooltip(setting.comment)
                        imfs.button(0, 0, 2, 0.75, "Set")
                    imfs.row_end()
                elseif setting.type == "string" then
                    imfs.arealabel(0, 0, "100%", 0.325, setting.readable_name)
                    imfs.row(0, 0, "100%", 0.75)
                        :gap(0.1)
                        theme.field(0, 0, "1x", 0.75, setting.value)
                            :tooltip(setting.comment)
                        if setting.value._val ~= setting.default then
                            imfs.button(0, 0, 0.75, 0.75)
                                :image(theme.icon("refresh"))
                                :style({
                                    bgimg = theme.get_background_image("button"),
                                    bgimg_middle = theme.styles.button.border_width,
                                })
                                :tooltip("Reset to default")
                        end
                        imfs.button(0, 0, 2, 0.75, "Set")
                    imfs.row_end()
                elseif setting.type == "enum" then
                    imfs.scope(setting.name)
                    imfs.arealabel(0, 0, "100%", 0.325, setting.readable_name)
                    imfs.row(0, 0, "100%", "fit")
                        :gap(0.1)
                        theme.dropdown(0, 0, "1x", 0.75, setting.values, setting.value)
                            :tooltip(setting.comment)
                        imfs.button(0, 0, 2, 0.75, "Set")
                    imfs.row_end()
                    imfs.scope_end()
                else
                    print(dump(setting))
                end
            end
        end
    imfs.column_end()
    imfs.scroll_container_end()
    
    return imfs.end_()
end