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

local pl = pmb_wrench.pl

local function add_entity(pos, ent_type, player)
    local object = minetest.add_entity(pos, "pmb_wrench:wrench_ENTITY_"..ent_type)
    local self = object:get_luaentity()
    if not self then object:remove() return end
    self._parent = player
    if pl[player].ent[ent_type] then
        pl[player].ent[ent_type].object:remove()
    end
    pl[player].ent[ent_type] = self
end

local function update_hud(player)
    if not pl[player] then return end
    local imagename = "(pmb_wrench_wrench_ENTITY.png^[opacity:100)"
    while pl[player].node do
        local def = minetest.registered_nodes[pl[player].node.name]
        if not def then break end

        local tiles = def.tiles
        if type(tiles) == "string" then imagename = tiles break end
        for i, tile in pairs(tiles or {}) do
            if type(tile) == "string" then imagename = tile break end
            imagename = tile and tile.name break
        end

        break
    end

    -- minetest.log("`"..imagename.."`")
    -- if pl[player].node_hud then
    --     player:hud_remove(pl[player].node_hud)
    --     pl[player].node_hud = nil
    -- end

    imagename = imagename.."^(pmb_wrench_wrench_ENTITY.png^[colorize:#ffffff40:250)"
    if pl[player].node_hud then
        player:hud_change(pl[player].node_hud, "text", imagename)
    else
        pl[player].node_hud = player:hud_add({
            hud_elem_type = "image",
            alignment = {x=0.5, y=0.5},
            position = {x=0.5, y=0.8},
            name = "pmb_wrench_node",
            text = imagename,
            z_index = 800,
            scale = {x = 8, y = 8},
            offset = {x = 0, y = 0},
        })
    end
end

minetest.register_tool("pmb_wrench:wrench", {
    description = pmb_util.desc(S("Wrench"), "red"),
    _tt_long_desc = S("Fill tool for faster and more convenient building"),
    _tt_how_to_use = S("Right click to select a node, left click drag to wrench between points"),
    inventory_image = "pmb_wrench.png",
    wield_image = "pmb_wrench.png",
    wield_scale = {x=1, y=1, z=1},
    groups = { admin_tools = 1 },
    range = 6,
    -- liquids_pointable = true,
    on_use = function (itemstack, player, pointed_thing)
        if not pl[player] then pl[player] = pmb_wrench.get_shell(player) end
        local pi = player_info and player_info.get(player)
        minetest.sound_play(("pmb_wrench_plip"), {
            gain = 0.1,
            pitch = 0.6,
            object = player,
        })
        if (not pointed_thing) or pointed_thing.type ~= "node" then
            pl[player].pos1 = vector.floor(vector.offset(pmb_wrench.get_eyepos(player), 0.5, 0.5, 0.5))
        elseif pi and pi.ctrl.aux1 then
            pl[player].pos1 = vector.copy(pointed_thing.under)
        else
            pl[player].pos1 = vector.copy(pointed_thing.above)
        end
        if pl[player].pos1 then
            pl[player].build_list = nil
            pl[player].build_index = nil
            add_entity(pl[player].pos1, 1, player)
            add_entity(pl[player].pos1, 2, player)
        end
    end,
    on_place = function(itemstack, player, pointed_thing)
        if not minetest.is_player(player) then return itemstack end
        minetest.sound_play(("pmb_wrench_plip"), {
            gain = 0.1,
            pitch = 0.8,
            object = player,
        })
        if not pl[player] then pl[player] = pmb_wrench.get_shell(player) end
        pl[player].node = table.copy(minetest.get_node(pointed_thing.under))
        update_hud(player)
        pl[player].build_list = nil
        pl[player].build_index = nil
        minetest.chat_send_player(player:get_player_name(),
            "[wrench-info] Wrench set to " .. minetest.colorize("#4ff", minetest.get_node(pointed_thing.under).name or "air"))
    end,
    on_secondary_use = function(itemstack, player)
        minetest.sound_play(("pmb_wrench_plip"), {
            gain = 0.1,
            pitch = 0.8,
            object = player,
        })
        if not pl[player] then pl[player] = pmb_wrench.get_shell(player) end
        local pt = pmb_wrench.get_pointed_thing(player, nil, true)
        if pt then
            pl[player].node = table.copy(minetest.get_node(pt.under))
            minetest.chat_send_player(player:get_player_name(),
            "[wrench-info] Wrench set to " .. minetest.colorize("#4ff", pl[player].node.name or "air"))
            return itemstack
        end
        pl[player].node = {name="air"}
        update_hud(player)
        minetest.chat_send_player(player:get_player_name(),
        "[wrench-info] Wrench set to "..minetest.colorize("#e6f", "air"))
        return itemstack
    end,
})

local function do_building(player, dtime, itemstack)
    while pl[player].build_list do
        local creative = pl[player].creative

        -- only at start of build, after releasing click
        if not pl[player].build_index then
            pl[player].build_index = 0
            pl[player]._timer = 1
            minetest.sound_play(("pmb_wrench_clicks"), {
                gain = 0.8,
                pitch = 1,
                object = player,
            })
            return
        end

        -- only place nodes every n seconds
        pl[player]._timer = (pl[player]._timer or 0) - dtime
        if (not creative) and pl[player]._timer > 0 then break end
        pl[player]._timer = 0.1

        pl[player].build_index = (pl[player].build_index or 0) + 1
        local build_pos = pl[player].build_list[pl[player].build_index]
        -- if you reach the end of the stack, quit
        if not build_pos then
            pl[player].build_list = nil
            pl[player].build_index = nil
            minetest.chat_send_player(player:get_player_name(), "[wrench-info] Finished building!")
            break
        end


        -- check they actually have the items
        local inv = player:get_inventory()
        if (not creative) and not (inv:contains_item("main", ItemStack(pl[player].node.name))) then
            minetest.sound_play(("pmb_not_allowed"), {
                gain = 0.4,
                pitch = 0.95,
                object = player,
            })
            pl[player].build_list = nil
            pl[player].build_index = nil
            break
        end

        -- don't overwrite existing nodes, but you are allowed to dig buildable_to nodes
        local node = minetest.get_node(build_pos)
        local nd = minetest.registered_nodes[node.name]
        if (not creative) and not (nd and nd.buildable_to) then
            break
        end

        -- go through inventory and subtract an item
        local found_item = creative
        if not found_item then
            for i=0, inv:get_size("main") do
                local stack = inv:get_stack("main", i)
                if stack:get_name() == pl[player].node.name then
                    stack:take_item(1)
                    inv:set_stack("main", i, stack)
                    found_item = true
                    break
                end
            end
        end

        if found_item then
            if (not creative) and (node.name ~= "air") then
                minetest.dig_node(build_pos)
            end
            -- actually place the node
            minetest.set_node(build_pos, pl[player].node)
            pmb_node_update.update_node_propagate(build_pos, "place", player, 15)
            minetest.sound_play(("pmb_wrench_plip"), {
                gain = 0.1,
                pitch = 0.5,
                max_hear_distance = 30,
                pos = build_pos,
            })
        else
            -- reset and give up building this shape
            pl[player].build_list = nil
            pl[player].build_index = nil
        end

        if not creative then
            break
        end
    end
end

local function squaredist(p1, p2)
    return (((p1.x - p2.x) ^ 2) + ((p1.y - p2.y) ^ 2) + ((p1.z - p2.z) ^ 2))
end

pmb_util.register_on_wield({
    name = "pmb_wrench:wrench",
    on_step = function(player, dtime, itemstack)
        if not pl[player] then pl[player] = pmb_wrench.get_shell(player) return end
        if pl[player].pos1 then
            local creative = pl[player].creative
            local eyepos = pmb_wrench.get_eyepos(player)
            local pointed_thing = pmb_wrench.get_pointed_thing(player, eyepos)
            local pi = player_info and player_info.get(player)
            -- allow player to wrench solid blocks too when pressing aux1
            if pointed_thing and pi and pi.ctrl.aux1 then
                pl[player].pos2 = vector.copy(pointed_thing.under)
            elseif pointed_thing then
                pl[player].pos2 = vector.copy(pointed_thing.above)
            end
            -- if not pointing at a node, just use the eyepos
            if not pl[player].pos2 then pl[player].pos2 = eyepos end

            -- make sure you're not able to make giant server-crashing cubes:
            local dist = squaredist(pl[player].pos1, pl[player].pos2)
            if (not creative) and dist > 16*16 then
                local dir = vector.direction(pl[player].pos1, pl[player].pos2)
                pl[player].pos2 = vector.add(vector.multiply(dir, 16), pl[player].pos1)
            end

            -- move the end marker
            pl[player].pos2 = vector.floor(vector.offset(pl[player].pos2, 0.5, 0.5, 0.5))
            if pl[player].ent[2] then
                pl[player].ent[2].object:set_pos(pl[player].pos2)
            end

            -- actually place nodes
            if pi and (pi.just_released.dig) then
                pl[player].pos1, pl[player].pos2 = pmb_wrench.sort_positions(pl[player].pos1, pl[player].pos2)
                local nodelist = pmb_wrench.all_nodes_in(pl[player].pos1, pl[player].pos2)
                -- set the list of node positions to place at
                pl[player].build_list = (pl[player].node.name ~= "air" or creative) and nodelist
                pl[player].pos1, pl[player].pos2 = nil, nil
                pmb_wrench.remove_all_ents(player)

                -- if they already have no items for this, just don't bother
                local inv = player:get_inventory()
                if (not creative) and not (inv:contains_item("main", ItemStack(pl[player].node.name))) then
                    minetest.sound_play(("pmb_not_allowed"), {
                        gain = 0.4,
                        pitch = 0.95,
                        object = player,
                    })
                    pl[player].build_list = nil
                    pl[player].build_index = nil
                end
            end
            pl[player].pos2 = nil
        end

        -- do the building tick
        do_building(player, dtime, itemstack)
    end,
    on_change_to_item = function(player, tostack)
        if not pl[player] then pl[player] = pmb_wrench.get_shell(player) end
        update_hud(player)
    end,
    on_change_from_item = function(player)
        pmb_wrench.remove_all_ents(player)
        pl[player].pos2 = nil
        pl[player].pos1 = nil
        if pl[player].build_list then
            minetest.sound_play(("pmb_not_allowed"), {
                gain = 0.4,
                pitch = 0.95,
                object = player,
            })
            pl[player].build_list = nil
            pl[player].build_index = nil
        end
        if pl[player].node_hud then
            player:hud_remove(pl[player].node_hud)
            pl[player].node_hud = nil
        end
    end,
})

if minetest.get_modpath("pmb_tcraft") then
    pmb_tcraft.register_craft({
        output = "pmb_wrench:wrench",
        items = {
            ["pmb_items:iron_bar"] = 10,
        },
    })
end
