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

local pl = {}

local function get_shell()
    return {
        name = "",
        node = {name="air"},
        ent = {}
    }
end

local function get_eyepos(player)
    local eyepos = vector.add(player:get_pos(), vector.multiply(player:get_eye_offset(), 0.1))
    eyepos.y = eyepos.y + 1.6
    return eyepos
end

local function get_pointed_thing(player, eyepos, liquids)
    if not eyepos then eyepos = get_eyepos(player) end
    local range = (minetest.registered_tools[player:get_wielded_item():get_name()].range or 0)
    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 nil
end

local function remove_all_ents(player)
    for i=1, 2 do
        if pl[player].ent[i] then
            pl[player].ent[i].object:remove()
        end
    end
end

local function tex(name)
    return {name,name,name,name,name,name}
end
local entity = {
    physical = false,
    textures = tex("pmb_admin_tools_fill_ENTITY.png^[colorize:#04f:100"),
    visual = "cube",
    visual_size = {x=1.1, y=1.1},
    collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2,},
    use_texture_alpha = true,
    pointable = false,
    _parent = false,
    _timer = 1,
    _type = 1,
    glow = 14,
    on_step = function(self, dtime, moveresult)
        if self._timer < 0 then self._timer = 1
        else self._timer = self._timer - dtime return end

        if (not self._parent) or (not pl[self._parent]) or (not pl[self._parent]["pos"..self._type]) then
            self.object:remove()
            return
        end
    end,
}
minetest.register_entity("pmb_admin_tools:fill_ENTITY_1", entity)
entity = table.copy(entity)
entity.textures = tex("pmb_admin_tools_fill_ENTITY.png^[colorize:#ff0:100")
minetest.register_entity("pmb_admin_tools:fill_ENTITY_2", entity)


local function add_entity(pos, ent_type, player)
    local object = minetest.add_entity(pos, "pmb_admin_tools:fill_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


minetest.register_tool("pmb_admin_tools:fill", {
    description = pmb_util.desc(S("Fill Tool"), "purple"),
    _tt_long_desc = S("Fill tool"),
    _tt_how_to_use = S("Right click to select a node, left click drag to fill between points"),
    inventory_image = "pmb_admin_tools_fill.png",
    wield_image = "pmb_admin_tools_fill.png",
    wield_scale = {x=0.5, y=0.5, z=5},
    groups = { admin_tools = 1 },
    range = 10,
    -- liquids_pointable = true,
    on_use = function (itemstack, player, pointed_thing)
        if not pl[player] then pl[player] = get_shell() end
        local pi = player_info and player_info.get(player)
        minetest.sound_play(("pmb_admin_tools_click"), {
            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(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
            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_admin_tools_click"), {
            gain = 0.1,
            pitch = 0.8,
            object = player,
        })
        if not pl[player] then pl[player] = get_shell() end
        pl[player].node = table.copy(minetest.get_node(pointed_thing.under))
        minetest.log(minetest.get_node(pointed_thing.under).name)
    end,
    on_secondary_use = function(itemstack, player)
        minetest.sound_play(("pmb_admin_tools_click"), {
            gain = 0.1,
            pitch = 0.8,
            object = player,
        })
        if not pl[player] then pl[player] = get_shell() end
        local pt = get_pointed_thing(player, nil, true)
        if pt then
            pl[player].node = table.copy(minetest.get_node(pt.under))
            minetest.log(pl[player].node.name)
            return itemstack
        end
        pl[player].node = {name="air"}
        minetest.log("air")
        return itemstack
    end,
})

local function sort_positions(v, b)
    return vector.new(
        ((v.x <= b.x) and v.x) or b.x,
        ((v.y <= b.y) and v.y) or b.y,
        ((v.z <= b.z) and v.z) or b.z
    ),
    vector.new(
        ((v.x >= b.x) and v.x) or b.x,
        ((v.y >= b.y) and v.y) or b.y,
        ((v.z >= b.z) and v.z) or b.z
    )
end
local function all_nodes_in(v, b)
    local list = {}
    for z=v.z, b.z do
        for y=v.y, b.y do
            for x=v.x, b.x do
                list[#list+1] = vector.new(x,y,z)
            end
        end
    end
    return list
end

pmb_util.register_on_wield({
    name = "pmb_admin_tools:fill",
    on_step = function(player, dtime, itemstack)
        if not pl[player] then pl[player] = get_shell() return end
        if not pl[player].pos1 then return end

        local eyepos = get_eyepos(player)
        local pointed_thing = get_pointed_thing(player, eyepos)
        local pi = player_info and player_info.get(player)
        -- allow player to fill 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
        -- 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 = sort_positions(pl[player].pos1, pl[player].pos2)
            local nodelist = all_nodes_in(pl[player].pos1, pl[player].pos2)
            minetest.bulk_set_node(nodelist, pl[player].node)
            minetest.sound_play(("pmb_admin_tools_click"), {
                gain = 0.1,
                pitch = 0.95,
                object = player,
            })
            pl[player].pos1, pl[player].pos2 = nil, nil
            remove_all_ents(player)
        end
        pl[player].pos2 = nil
    end,
    on_change_from_item = function(player)
        remove_all_ents(player)
        pl[player].pos2 = nil
        pl[player].pos1 = nil
    end,
})
