
local pl = {}

local _offset = {x=1, y=0}
local function p(x,y)
    return tostring(x+_offset.x) .. "," .. tostring(y + _offset.y)
end


local scroll_start_y = 3 -- base offset
local recipes_per_row = 3
local recipe_spacing_y = 1.2045 -- extra dist per row
local recipe_spacing_x = 1.0 -- extra dist per column
local scroll_per_formspec_unit = 11.626 -- magic number
local scroll_per_index = (scroll_per_formspec_unit * recipe_spacing_y) / recipes_per_row

minetest.register_globalstep(function(dtime)
    for i, player in ipairs(minetest.get_connected_players()) do
        if not pl[player] then pl[player] = {scroll = 0, stash = {}} end
        for k, methodstash in pairs(pl[player].stash) do
            pl[player].stash[k].time = methodstash.time - dtime
        end
    end
end)

local function index_to_pos(c)
    return (c / recipes_per_row) * recipe_spacing_y
end

local function index_to_recipe_uid(player, method, index)
    return ((pl[player].craftable[method] or {})[index] or {}).uid
end

local function scroll_position_to_index(scroll)
    return (scroll - scroll_start_y) / scroll_per_index + 5 * recipes_per_row
end

local function scroll_position_to_uid(player, method, scroll)
    local index = math.floor(scroll_position_to_index(scroll) / recipes_per_row) * recipes_per_row
    return index_to_recipe_uid(player, method, index)
end

-- reverses the above
local function pos_to_scroll_position(pos)
    local ret = pos - 5 * recipes_per_row
    ret = ret * scroll_per_index
    return ret + scroll_start_y
end

local function leftpad(str, len, char)
    if string.len(str) >= len then return str end
    local t = {}
    for i = 1, len - string.len(str) do
        t[#t+1] = char
    end
    return table.concat(t, "") .. str
end

local function craft_index_to_fsu_y(k)
    return (
        math.floor((k-1) / recipes_per_row) * (recipe_spacing_y)
    )
end
local function craft_index_to_fsu_x(k)
    return (
        ((k-1) % recipes_per_row) * (recipe_spacing_x) - 0.1
    )
end

function pmb_inventory.tcraft_get_formspec(player, method, flags)
    if not method then method = "normal" end
    if not flags then flags = {} end
    if not pl[player] then pl[player] = {scroll = 0, stash = {}, craftable = {}} end

    if not pl[player].stash[method] then
        pl[player].stash[method] = {time = 1}
    elseif pl[player].stash[method].time > 0 and pl[player].stash[method].last_formspec ~= nil then
        return pl[player].stash[method].last_formspec
    else
        pl[player].stash[method].time = 1
    end

    local scroll = pl[player].scroll

    local fs = ""
    local pli = pmb_tcraft.get_player_craftable(player)
    pl[player].craftable[method] = {}
    local craftable = pl[player].craftable[method]
    for i, def in ipairs(pli.craftable) do
        if ((not def.method) and method == "normal") or table.indexof(def.method, method) > 0 then
            craftable[#craftable+1] = def
            -- this scrolls to the last item you were looking at if it's available
            if pl[player].last_viewed_uid == def.uid then
                scroll = pos_to_scroll_position(i) + (pl[player].scroll_offset or 0)
            end
        end
    end

    -- so that the scroll doesn't affect the formspec refresh
    -- TODO: stop doing hacky stuff like this
    scroll = leftpad(tostring(math.floor(scroll)), 12, " ")
    local maxscroll = (#craftable) * (scroll_per_index)

    local scrollcontwidth = (craft_index_to_fsu_x(recipes_per_row)) + 2.3
    local scrollcontx = 0

    fs = fs ..
    "background9["..p(scrollcontx-0.54, 0.18)..";"..tostring(scrollcontwidth-0.9)..","..tostring(11.20)..
    ";pmb_tcraft_panel.png^[multiply:"..(flags.color or "#637aa6").."^pmb_tcraft_panel_overlay.png;false;32]"..
    "scrollbaroptions[arrows=hide;smallstep="..tostring(math.round(scroll_per_index * recipes_per_row))..";thumbsize=100;max="..tostring(maxscroll).."]"..
    "scrollbar["..p(scrollcontx - 1.04, 0.5)..";0.45,10.5;vertical;tcraft_scroll;"..tostring(scroll).."]"..
    "scroll_container["..p(scrollcontx, 1)..";"..tostring(scrollcontwidth+recipe_spacing_x)..",12;tcraft_scroll;vertical;0.1]"..
    "style_type[item_image_button;bgcolor=#fff;bgcolor_hovered=#fff;bgcolor_pressed=#fff]"..
    "style_type[item_image_button;textcolor=#8c3f5d;border=false]"

    -- button to craft and visual for item crafted
    fs = fs .. "style_type[item_image_button;bgimg=pmb_tcraft_bg.png^[opacity:100;bgimg_hovered=pmb_tcraft_bg.png^[opacity:180]"

    for k, recipe in ipairs(craftable) do
        local dims = tostring(-0.1 + craft_index_to_fsu_x(k))..","..tostring(craft_index_to_fsu_y(k))..";1,1;"

        fs = fs .. "item_image_button["..dims..recipe.output..";".."tcraftid_"..tostring(recipe.uid).."_1".."; ]"
    end

    -- visual for recipe ingredients
    fs = fs .. "style_type[item_image_button;"..
            "bgimg=pmb_tcraft_circle.png^[colorize:#333333a0:255;"..
    "bgimg_hovered=pmb_tcraft_circle.png^[colorize:#111111a0:255]"
    fs = fs .. "style_type[label;font_size=*0.75;textcolor=#fff;font=normal]"
    local id = 0
    for k, recipe in ipairs(craftable) do
        local l = -0.2
        for name, count in pairs(recipe.items) do
            -- item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
            fs = fs .. "item_image_button["..
            tostring(l + craft_index_to_fsu_x(k))..","..
            tostring(0.75 + craft_index_to_fsu_y(k))..";0.5,0.5;"..
            name..";tcinf_"..tostring(id).. "; "..--tostring(count > 1 and count or "")..
            "]"

            fs = fs .. "label["..
            tostring(l + 0.1 + craft_index_to_fsu_x(k))..","..
            tostring(0.8 + craft_index_to_fsu_y(k))..";"..
            tostring(tostring(count > 1 and count or "")).."]"

            l = l + 0.28
            id = id + 1
        end
    end

    -- x10 craft button
    fs = fs .. "style_type[image_button;bgimg=pmb_tcraft_x10.png;bgimg_hovered=pmb_tcraft_x10_hover.png]"
    fs = fs .. "style_type[image_button;border=false]"
    for k, recipe in ipairs(craftable) do
        local dims = tostring(-0.2 + craft_index_to_fsu_x(k))..","..tostring(0.5 + craft_index_to_fsu_y(k))..";0.4,0.4;"

        fs = fs .. "image_button["..dims.."blank.png;".."tcraftid_"..tostring(recipe.uid).."_10".."; ]"
    end

    fs = fs .. "scroll_container_end[]"
    pl[player].stash[method].last_formspec = fs
    return fs
end

function pmb_inventory.on_fields_tcraft(player, formname, fields)
    if not pl[player] then pl[player] = {scroll = 0} end

    for field, val in pairs(fields) do
        local _split = string.split(field, "_", false, 3)
        if _split and _split[1] == "tcraftid" then
            local uid = tonumber(_split[2] or "-1")
            local count = tonumber(_split[3] or "1")
            local success = pmb_tcraft.try_to_craft_uid(player, uid, count)
            pmb_tcraft.delay_recalc(player)

            if success then
                minetest.sound_play("pmb_pickup_item", {
                    gain = 0.3 + math.random() * 0.1,
                    to_player = player:get_player_name(),
                    pitch = 0.8 + math.random() * 0.2
                })
            else
                minetest.sound_play("pmb_not_allowed", {
                    gain = 0.2,
                    to_player = player:get_player_name(),
                })
            end
        end
    end
    if fields.tcraft_scroll then
        local scroll_event = minetest.explode_scrollbar_event(fields.tcraft_scroll)
        if scroll_event.type == "CHG" then
            pl[player].scroll = scroll_event.value
            local method = (formname == "" and "normal") or string.split(formname, ":")[2]
            if not method then
                pl[player].scroll_offset = 0
                pl[player].last_viewed_uid = -1
                return
            end
            pl[player].last_viewed_uid = scroll_position_to_uid(player, method, scroll_event.value)

            local sp = scroll_position_to_index(scroll_event.value) % 1
            pl[player].scroll_offset = math.floor(sp * scroll_per_index)
            pmb_tcraft.delay_recalc(player, 0.1)
        end
    end
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
    if formname ~= "" then return end
    pmb_inventory.on_fields_tcraft(player, formname, fields)
end)

