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


aom_inventory.player = {}

local me = aom_inventory.player

function aom_inventory.get_itemslot_array(x, y, width, height, hotbar, color, opacity)
    local ret = {}
    color = color or "#2c2c37"
    if opacity == nil then opacity = "170" end
    local o = -0.05
    local p = -0.05
    for k = 0, height - 1, 1 do
        if hotbar and k == height - 1 then y = y + hotbar end
        for i = 0, width - 1, 1 do
            table.insert(ret, "image[")
            table.insert(ret, (x+i-0.02)) -- pos
            table.insert(ret, ",")
            table.insert(ret, (y+k-0.02))
            table.insert(ret, ";")
            table.insert(ret, (1+o)) -- size
            table.insert(ret, ",")
            table.insert(ret, (1+p))
            table.insert(ret, ";aom_inv_itemslot_bg.png^[multiply:") -- tex
            table.insert(ret, color)
            if opacity then
                table.insert(ret, "^[opacity:" .. opacity)
            end
            table.insert(ret, "]")
        end
    end
    return table.concat(ret)
end

me.p = {}

-- inventory sizes
me.width = 10
me.height = 4

function aom_inventory.player.set_size(width, height)
    me.width = width
    me.height = height
end

function aom_inventory.player.init_hotbar(player)
    player:hud_set_hotbar_itemcount(me.width)
    player:hud_set_hotbar_selected_image("aom_inv_hotbar_select.png^[colorize:#2c2c5d:255")
    player:hud_set_hotbar_image("white.png^[colorize:#4f4072:255")
    -- player:hud_set_flags({hotbar=false})
    me.p[player] = {}
end

function aom_inventory.get_9patch(x, y, w, h, texture, clip, middle)
    return "background9["..
    x..","..y..";"..
    w..","..h..";"..texture..";"..clip..";"..middle.."]"
end
function aom_inventory.get_bg9patch_texture(color)
    return "aom_inv_bg.png^[multiply:"..color.."^aom_inv_bg_outline.png"
end

aom_inventory.register_formspec_process("inventory", "aom_inventory:main", function(flags)
    local offset, size = flags.offset, flags.size
    if offset==nil then offset = {x=6, y=11.5} end
    if size==nil then size = {x=me.width, y=me.height} end
    return table.concat({
        "formspec_version[6]",
        "size[24.00,12.0]",
        "no_prepend[]",
        "listcolors[#00000000;#00000000;#00000000;#111;#eee]",
        "bgcolor[#00000000;neither]",
        "style_type[list;spacing=0.1;size=0.9]",
        aom_inventory.get_itemslot_array(offset.x, offset.y - me.height, me.width, me.height, 0.3),
        "list[current_player;main;",tostring(offset.x), ",", tostring(offset.y - me.height), ";", (me.width), ",", (me.height-1), ";10]",
        "list[current_player;main;",tostring(offset.x), ",", tostring(0.3+offset.y-1), ";", (me.width), ",", (1), ";]",
        "listcolors[#66504f00;#ffffff20]",
        aom_inventory.player.get_armor({x=offset.x+10.3,y=offset.y-me.height}),
        aom_inventory.player.get_accessories({x=offset.x+11.3,y=offset.y-me.height}),
        "background9[",
        (offset.x-0.5), ",", (offset.y-0.6 - me.height), ";",
        (size.x+3.5),",", (size.y+1.5), ";aom_inv_bg.png^[multiply:#5a5a6a^aom_inv_bg_outline.png;false;32]"
    })
end)

local function get_formspec(offset, size)
    return aom_inventory.get_formspec_of_type("inventory", {offset=offset, size=size}),
        (size or {x=me.width, y=me.height})
end
aom_inventory.player.get_formspec = get_formspec

function aom_inventory.player.get_armor(o)
    return (
        aom_inventory.get_itemslot_array(o.x, o.y, 1, 4)..
        "image["..(o.x)..","..(o.y+0)..";0.9,0.9;aom_inv_head.png^[colorize:#00000040:255]"..
        "image["..(o.x)..","..(o.y+1)..";0.9,0.9;aom_inv_torso.png^[colorize:#00000040:255]"..
        "image["..(o.x)..","..(o.y+2)..";0.9,0.9;aom_inv_legs.png^[colorize:#00000040:255]"..
        "image["..(o.x)..","..(o.y+3)..";0.9,0.9;aom_inv_boots.png^[colorize:#00000040:255]"..
        "list[current_player;armor;"..(o.x)..","..(o.y)..";1,4;]"
    )
end

function aom_inventory.player.get_accessories(o)
    return (
        aom_inventory.get_itemslot_array(o.x, o.y, 1, 1)..
        "image["..(o.x)..","..(o.y)..";0.9,0.9;aom_inv_backpack.png^[colorize:#00000040:255]"..
        -- left out for now since there probably won't even be items for this
        -- "image["..(o.x)..","..(o.y+3)..";1,1;aom_inv_necklace.png^[colorize:#00000040:255]"..
        -- "image["..(o.x)..","..(o.y+1)..";1,1;aom_inv_ring.png^[colorize:#00000040:255]"..
        -- "image["..(o.x)..","..(o.y+2)..";1,1;aom_inv_ring.png^[colorize:#00000040:255]"
        "list[current_player;accessories;"..(o.x)..","..(o.y)..";1,1;]"
    )
end

local function get_craft_menu(player, method)
    return table.concat({
        "formspec_version[6]",
        "size[24.00,12.0]",
        "no_prepend[]",
        "style_type[list;spacing=0.1;size=0.9]",
        aom_inventory.get_formspec_of_type("inventory", {offset={x=7.5, y=11.5}, size={x=me.width, y=me.height}}),
        aom_inventory.get_formspec_of_type("crafting", {player=player, method=method}),
    })
end
aom_inventory.player.get_craft_menu = get_craft_menu

local pl = {}

function aom_inventory.player.force_update_formspec(player)
    if not pl[player] then pl[player] = {} end
    pl[player].force_update = true
    player:set_inventory_formspec(aom_inventory.player.get_craft_menu(player))
end

local _t = 0.1
local on_globalstep = function(dtime)
    _t = _t - dtime
    if _t > 0 then return end
    _t = 0.2

    for i, player in ipairs(minetest.get_connected_players()) do
        local new_spec = ""
        if not pl[player] then pl[player] = {} end
        repeat
            if (minetest.get_modpath("aom_gamemodes")) and not aom_gamemodes.player_has_tag(player, "inventory") then
                pl[player].last_fs = new_spec
                player:set_inventory_formspec(new_spec)
                break
            end

            new_spec = aom_inventory.player.get_craft_menu(player)
            if (pl[player].force_update) or (string.len(new_spec) ~= string.len(pl[player].last_fs or "")) then
                pl[player].last_fs = new_spec
                pl[player].force_update = false
                player:set_inventory_formspec(new_spec)
                break
            end
        until true
    end
end
minetest.register_globalstep(on_globalstep)

minetest.register_on_joinplayer(function(player, last_login)
    player:set_inventory_formspec(aom_inventory.player.get_craft_menu(player, "normal"))
end)

if core.get_modpath("aom_bform") then
    -- PREPEND
    local col = "#a69a8e"
    aom_bform.get_form("global_prepend"):add_children({
        -- remove the ugly square box menu bg
        aom_bform.element.custom.new(nil, "bgcolor", {
            "bgcolor[#00000000;neither]",
        }),
        -- style the buttons in the menu
        aom_bform.element.custom.new(nil, "buttonstyle", {
            "style_type[button;bgimg=bform_btn.png\\^\\[multiply:"..col..";bgimg_middle=8;",
            "bgimg_hovered=bform_btn.png\\^\\[multiply:"..col..";bgimg_pressed=bform_btn_press.png\\^\\[multiply:"..col..";",
            "bgcolor_hovered=#eee;bgcolor_pressed=#fff]",
            "listcolors[#00000000;#00000000;#00000000;#111;#eee]",
            "style_type[button;border=false]",
        }),
        -- add 9 patch bg
        aom_bform.element.background9.new(nil, "bform_bg.png^[multiply:#113^bform_bg_outline.png", nil, true, 28),
    })
end


aom_inventory.player.inventory_slot_groups = {}

function aom_inventory.player.bind_group_to_inventory_slot(group, listname, listindex)
    local invslots = aom_inventory.player.inventory_slot_groups
    if not invslots[listname] then invslots[listname] = {} end
    invslots[listname][listindex] = group
end

aom_inventory.player.bind_group_to_inventory_slot("armor_head", "armor", 1)
aom_inventory.player.bind_group_to_inventory_slot("armor_chest", "armor", 2)
aom_inventory.player.bind_group_to_inventory_slot("armor_legs", "armor", 3)
aom_inventory.player.bind_group_to_inventory_slot("armor_boots", "armor", 4)
aom_inventory.player.bind_group_to_inventory_slot("accessory", "accessories", 1)
aom_inventory.player.bind_group_to_inventory_slot("necklace", "accessories", 2)
aom_inventory.player.bind_group_to_inventory_slot("ring", "accessories", 3)
aom_inventory.player.bind_group_to_inventory_slot("ring", "accessories", 4)


local function fix_bad_inventory_action_args(player, action, inventory, inventory_info)
    local info = {}
    -- deal with the silly way this callback gives its parameters
    if (action == "move") then
        info.to_list = inventory_info.to_list
        info.to_index = inventory_info.to_index
        info.from_list = inventory_info.from_list
        info.from_index = inventory_info.from_index
        info.from_stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index)
        info.to_stack = inventory:get_stack(inventory_info.to_list, inventory_info.to_index)
    elseif (action == "put") then
        info.to_list = inventory_info.listname
        info.to_index = inventory_info.index
        info.from_stack = inventory_info.stack
    elseif (action == "take") then
        info.from_list = inventory_info.listname
        info.from_index = inventory_info.index
        info.from_stack = inventory_info.stack
    end
    return info
end

minetest.register_allow_player_inventory_action(function(player, action, inventory, inventory_info)
    local to_list = inventory_info.listname or inventory_info.to_list

    -- nothing is defined for this list name so just forget about it
    if not aom_inventory.player.inventory_slot_groups[to_list] then return end

    local info = fix_bad_inventory_action_args(player, action, inventory, inventory_info)
    if not info then return end
    local slotgroups = aom_inventory.player.inventory_slot_groups[to_list]

    -- nothing is defined for this index so just forget about it
    if not slotgroups[info.to_index] then return end

    -- now test if it has the group
    if minetest.get_item_group(info.from_stack:get_name(), slotgroups[info.to_index]) > 0 then return end

    -- return 0 will prevent any item from being moved
    return 0
end)

