local mod_name = minetest.get_current_modname()
local mod_path = minetest.get_modpath(mod_name)
local S = minetest.get_translator(mod_name)


local save_path = minetest.get_worldpath()
local function find_backend()
    local file = io.open(save_path..DIR_DELIM.."world.mt", "r")
    if not file then
        minetest.chat_send_all("ERROR: CANNOT OPEN world.mt TO CHECK BACKEND. ASSUMING \"sqlite3\".")
        return "sqlite3"
    end
    local content = file:read("*a")
    file:close()
    content = string.split(content, "\n")
    for i, line in ipairs(content) do
        local st = string.split(line, " = ")
        if st[1] == "backend" then
            return st[2]
        end
    end
end
local backend = find_backend()

world_storage = {
    domain = {},
    domain_list = {},
}

function world_storage.get_file(mode, filename)
    local folder_path = save_path..DIR_DELIM.."pmb_world_storage"
    if not filename then minetest.log("error", "World Storage: no filename given when getting file") return false end
    local file_path = folder_path..DIR_DELIM..(filename)..".txt"

    local file = io.open(file_path, mode)
    if not file then
        minetest.mkdir(folder_path)
        file = io.open(file_path, mode)
    end
    return file
end

function world_storage.save(domain)
    if not domain then return end
    local file = world_storage.get_file("w", domain)
    local datastring = minetest.serialize(world_storage.domain[domain])
    if file then
        file:write(datastring)
        file:close()
        -- minetest.log("action", "saving storage for " .. dump(domain))
    else
        -- minetest.log("warning", "saving storage failed for " .. dump(domain))
    end
end

function world_storage.load(domain)
    local file = world_storage.get_file("r", domain)
    if not file then
        world_storage.save(domain)
        return
    end
    local data = minetest.deserialize(file:read("*a"))
    file:close()
    if data then
        world_storage.domain[domain] = table.copy(data)
    else
        -- world_storage.save(domain)
        minetest.log("warning", "CANNOT READ world_storage FILE: "..dump(domain)..". This file will no longer be tracked.\
        Set a key with this domain in order to start tracking again")
        -- remove it from the list too
        world_storage.domain_list[domain] = nil
        world_storage.set("world_storage", "domain_list", world_storage.domain_list)
    end
end

-- Deprecated funcs
function world_storage:get_key(domain, key)
    return world_storage.get(domain, key)
end
function world_storage:set_key(domain, key, val, flags)
    world_storage.set(domain, key, val)
end


function world_storage.get(domain, key)
    if world_storage.domain[domain] then
        return world_storage.domain[domain][key]
    end
end

function world_storage.set(domain, key, val)
    if not world_storage.domain[domain] then
        world_storage.domain[domain] = {}
        world_storage.domain_list[domain] = true
        world_storage.set("world_storage", "domain_list", world_storage.domain_list)
    end

    -- don't increase the time
    if (not world_storage.domain[domain].time) or world_storage.domain[domain].time > (10) then
        world_storage.domain[domain].time = 10
    end

    world_storage.domain[domain].changes_made = true

    world_storage.domain[domain][key] = val
end

function world_storage.set_timeout(domain, timeout)
    if not world_storage.domain[domain] then return end
    world_storage.domain[domain].time = timeout
    world_storage.domain[domain].refresh_time = timeout
end

world_storage.load("world_storage")
world_storage.domain_list = world_storage.get("world_storage", "domain_list") or {}

if backend == "dummy" then
    world_storage.domain = {}
    for i, d in pairs(world_storage.domain_list) do
        world_storage.domain[i] = {time=1}
        world_storage.save(i)
    end
else
    for i, d in pairs(world_storage.domain_list) do
        world_storage.load(i)
    end
end

minetest.register_globalstep(function(dtime)
    for d, val in pairs(world_storage.domain) do
        if world_storage.domain[d].time and world_storage.domain[d].time > 0 then
            world_storage.domain[d].time = world_storage.domain[d].time - dtime
        elseif world_storage.domain[d].changes_made then
            world_storage.domain[d].changes_made = false
            world_storage.domain[d].time = world_storage.domain[d].refresh_time or 10
            world_storage.save(d)
        end
    end
end)

function world_storage.wget(setting_name)
    if not world_storage.domain._ws then return end
    return world_storage.domain._ws[setting_name]
end
function world_storage.wset(setting_name, value)
    world_storage.set("_ws", setting_name, value)
    world_storage.set_timeout("_ws", 0.1)
end
function world_storage.wcheck(setting_name, default_value)
    if world_storage.wget(setting_name) == nil then
        world_storage.set("_ws", setting_name, default_value)
    end
end

-- allow users to set custom settings by command if admin; only supports numbers (n) or strings (s)
minetest.register_chatcommand("wset", {
    params = "",
    description = S("sets a world setting; wset s key_name nil, wset n key 38.2, wset s key_name some long sentence"),
    privs = { server = 1 },
    func = function(name, param)
        if not param then return false, "no" end
        local p = string.split(param, " ")
        --[1] option [2] key [3-9999] value
        if (not p) or #p < 2 then return false, "no" end

        local key = p[2]

        local raw_val = ""
        for i = 3, #p do
            raw_val = raw_val .. (p[i] or "")
        end

        if raw_val == "nil" then
            world_storage.wset(key, nil)
        end

        local val
        if p[1] == "n" then
            val = tonumber(raw_val)
        elseif p[1] == "s" then
            val = raw_val
        else
            return false, "no"
        end

        if type(world_storage.wget(key)) ~= type(val) then
            return false, (
                "[world_storage] Cannot set key to that value, it is not of the same type. '"..
                (key).."' is of type '"..type(world_storage.wget(key)).."'"
            )
        end
        world_storage.wset(key, val)

        return true, "[world_storage] Set key " .. key .. " to " .. tostring(val)
    end
})
