

aom_hammer.player = {}
local pl = aom_hammer.player

function aom_hammer.get_player_defaults()
    return {
        node = nil,
        cooldown = 0,
    }
end

function aom_hammer.set_paint_node(player, node_name)
    if not minetest.is_player(player) then return itemstack end
    pl[player].node = node_name
end

function aom_hammer.get_paint_node_from_pointed(itemstack, player, pointed_thing)
    if not minetest.is_player(player) then return itemstack end
    if (not pointed_thing) or (pointed_thing.type ~= "node") then return end
    local node = minetest.get_node(pointed_thing.under)
    pl[player].node = node.name
    pl[player].param2 = node.param2
end

function aom_hammer.find_item_index_in_inv(player, stack_name)
    local inv = player:get_inventory()
    for i = 1, inv:get_size("main") do
        local stack = inv:get_stack("main", i)
        if stack_name == stack:get_name() and stack:get_count() > 0 then
            return i
        end
    end
end

local function use_item_index_in_inventory(inv, stack_index)
    local stack = inv:get_stack("main", stack_index)
    stack:take_item(1)
    inv:set_stack("main", stack_index, stack)
    return true
end

local FULLWEAR = 65536
local function wear_tool(itemstack, group)
    local caps = itemstack:get_tool_capabilities().groupcaps
    if caps[group].uses == 0 then return itemstack end
    local wear = FULLWEAR / (caps[group].uses)
    itemstack:add_wear(wear)
    return itemstack
end

local function do_node_sound(pos, node)
    local def = minetest.registered_nodes[node.name]
    if not def then return end
    local sound = def.sounds.dug
    minetest.sound_play(sound.name, {
        gain = sound.gain,
        pos = pos,
    })
end

function aom_hammer.get_pointed_thing(player, eyepos, liquids)
    if not eyepos then eyepos = aom_util.get_eyepos(player) end
    local range = aom_util.get_tool_range(player:get_wielded_item())
    local ray = minetest.raycast(eyepos, vector.add(eyepos, vector.multiply(player:get_look_dir(), range)), false, (liquids == true))
    for pointed_thing in ray do
        if pointed_thing.type == "node" then
            return pointed_thing
        end
    end
    return
end

function aom_hammer.paint_node_on_step(itemstack, player)
    local ctrl = player:get_player_control()
    if ctrl and ctrl.dig then
        aom_hammer.paint_node(itemstack, player)
    end
end

minetest.register_globalstep(function(dtime)
    for player, pi in pairs(pl) do
        if pi.cooldown > 0 then
            pi.cooldown = pi.cooldown - dtime
        end
    end
end)

function aom_hammer.paint_node(itemstack, player, pointed_thing)
    local pi = pl[player]
    if pi.cooldown > 0 then return end
    if (not pointed_thing) or (pointed_thing.type ~= "node") then
        pointed_thing = aom_hammer.get_pointed_thing(player, false)
    end
    if not pointed_thing then return end
    if not pi.node then return end
    if minetest.get_item_group(pi.node, "player_place_only") > 0 then return end
    local ctrl = player:get_player_control()
    local keep_param = ctrl.aux1
    -- make sure you have a stack
    local stack_index = aom_hammer.find_item_index_in_inv(player, pi.node)
    if not stack_index then return end

    -- figure out if the node you're pointing at can actually be painted
    local tool_caps = itemstack:get_tool_capabilities().groupcaps
    local node = minetest.get_node(pointed_thing.under)
    if (node.name == pi.node) and (keep_param or node.param2 == pi.param2) then return end
    local def = minetest.registered_nodes[node.name]
    if not def then return itemstack end
    local node_caps = def.groups
    local can_paint = false
    local group
    -- check to make sure you could dig this
    for groupname, groupcap in pairs(tool_caps) do
        if node_caps[groupname]
        and (groupcap.maxlevel
        and groupcap.maxlevel >= node_caps[groupname]) then
            can_paint = true
            group = groupname
            break
        end
    end
    if not can_paint then return end
    local inv = player:get_inventory()
    if use_item_index_in_inventory(inv, stack_index) then
        local stacks = minetest.get_node_drops(node, itemstack)
        for i, stack in pairs(stacks) do
            stack = inv:add_item("main", stack)
            if stack:get_count() > 0 then
                minetest.add_item(player:get_pos(), stack)
            end
        end
        if group then itemstack = wear_tool(itemstack, group) end
        node.name = pi.node
        if not keep_param then
            node.param2 = pi.param2
        end
        minetest.set_node(pointed_thing.under, node, true)
        do_node_sound(pointed_thing.under, node)
        pi.cooldown = 0.1
    end
    return itemstack
end

function aom_hammer.init_player(player, last_login)
    pl[player] = aom_hammer.get_player_defaults()
    aom_hammer.set_paint_node(player, nil)
end
minetest.register_on_joinplayer(aom_hammer.init_player)
