

artifact.register_node("forcefield", {
    drawtype = "glasslike_framed",
    use_texture_alpha = "blend",
    light_source = 6,
    tiles = {"artifact_forcefield_border.png", "artifact_forcefield_inner.png"},
    visual_scale = 0.75,
    diggable = false
})

local vm_data = {}
local vm_p2_data = {}
local c_air = minetest.get_content_id("air")
local c_forcefield = minetest.get_content_id("forcefield")
function artifact.place_forcefield(pos, def)
    if def.type == "cube" then
        if def.offset then pos = pos:add(def.offset) end
        local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
        local va = VoxelArea(vm:get_emerged_area())
        vm:get_data(vm_data)
        vm:get_param2_data(vm_p2_data)
        for x = pos.x -def.radius, pos.x +def.radius, def.radius *2 do
            for y = pos.y -def.radius, pos.y +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        end
        for y = pos.y -def.radius, pos.y +def.radius, def.radius *2 do
            for x = pos.x -def.radius, pos.x +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        end
        for z = pos.z -def.radius, pos.z +def.radius, def.radius *2 do
            for y = pos.y -def.radius, pos.y +def.radius do
                for x = pos.x -def.radius, pos.x +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        end
        vm:set_data(vm_data)
        vm:set_param2_data(vm_p2_data)
        vm:write_to_map()
    elseif def.type == "plane" then
        local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
        local va = VoxelArea(vm:get_emerged_area())
        vm:get_data(vm_data)
        vm:get_param2_data(vm_p2_data)
        
        local x
        local y
        local z
        if def.axis == "x" then
            local x = pos.x
            for y = pos.y -def.radius, pos.y +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        elseif def.axis == "y" then
            local y = pos.y
            for x = pos.x -def.radius, pos.x +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        elseif def.axis == "z" then
            local z = pos.z
            for y = pos.y -def.radius, pos.y +def.radius do
                for x = pos.x -def.radius, pos.x +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_air then
                        vm_data[idx] = c_forcefield
                        vm_p2_data[idx] = 1
                    elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
                        vm_p2_data[idx] = vm_p2_data[idx] +1
                    end
                end
            end
        end
        
        vm:set_data(vm_data)
        vm:set_param2_data(vm_p2_data)
        vm:write_to_map()
    end
end

function artifact.remove_forcefield(pos, def)
    if def.type == "cube" then
        if def.offset then pos = pos:add(def.offset) end
        local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
        local va = VoxelArea(vm:get_emerged_area())
        vm:get_data(vm_data)
        vm:get_param2_data(vm_p2_data)
        for x = pos.x -def.radius, pos.x +def.radius, def.radius *2 do
            for y = pos.y -def.radius, pos.y +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        end
        for y = pos.y -def.radius, pos.y +def.radius, def.radius *2 do
            for x = pos.x -def.radius, pos.x +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        end
        for z = pos.z -def.radius, pos.z +def.radius, def.radius *2 do
            for y = pos.y -def.radius, pos.y +def.radius do
                for x = pos.x -def.radius, pos.x +def.radius do
                    local idx = va:index(x, y, z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        end
        vm:set_data(vm_data)
        vm:set_param2_data(vm_p2_data)
        vm:write_to_map()
    elseif def.type == "plane" then
        local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
        local va = VoxelArea(vm:get_emerged_area())
        vm:get_data(vm_data)
        vm:get_param2_data(vm_p2_data)
        
        local x
        local y
        local z
        if def.axis == "x" then
            local x = pos.x
            for y = pos.y -def.radius, pos.y +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        elseif def.axis == "y" then
            local y = pos.y
            for x = pos.x -def.radius, pos.x +def.radius do
                for z = pos.z -def.radius, pos.z +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        elseif def.axis == "z" then
            local z = pos.z
            for y = pos.y -def.radius, pos.y +def.radius do
                for x = pos.x -def.radius, pos.x +def.radius do
                    local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
                    if vm_data[idx] == c_forcefield then
                        if vm_p2_data[idx] <= 1 then
                            vm_data[idx] = c_air
                            vm_p2_data[idx] = 0
                        else
                            vm_p2_data[idx] = vm_p2_data[idx] -1
                        end
                    end
                end
            end
        end
        
        vm:set_data(vm_data)
        vm:set_param2_data(vm_p2_data)
        vm:write_to_map()
    end
end


local function onload(pos)
    local m = minetest.get_meta(pos)
    if not m:contains("initialized") then
        local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = vector.new(3,0,0)}
        m:set_string("_config", minetest.serialize(conf))
        m:set_string("config", minetest.serialize(conf))
        artifact.place_forcefield(pos, conf)
    end
end

artifact.register_node("forcefield_generator", {
    drawtype = "mesh",
    mesh = "artifact_forcefield_generator.gltf",
    tiles = {"artifact_forcefield_generator.png"},
    paramtype = "light",
    light_source = 10,
    paramtype2 = "facedir",
    groups = {whackable = 1},
    collision_box = {
        type = "fixed",
        fixed = {
            -1, -0.5, -0.5,
            1, 0.5, 0.5
        }
    },
    selection_box = {
        type = "fixed",
        fixed = {
            -1, -0.5, -0.5,
            1, 0.5, 0.5
        }
    },
    sounds = artifact.sounds.metal,
    on_construct = onload,
    on_load = onload,
    on_rightclick = artifact.debug and function(pos)
        local m = minetest.get_meta(pos)
        artifact.remove_forcefield(pos, minetest.deserialize(m:get("_config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = vector.new(3,0,0)})
        local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = 3}
        m:set_string("_config", minetest.serialize(conf))
        artifact.place_forcefield(pos, conf)
    end or nil,
    on_destruct = function(pos)
        local m = minetest.get_meta(pos)
        local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "cube", radius = 5}
        artifact.remove_forcefield(pos, conf)
        local node = minetest.get_node(pos)
        node.name = "forcefield_generator_off"
        minetest.after(0, function()
            minetest.set_node(pos, node)
        end)
        artifact.play_sound {
            name = "artifact_forcefield_generator_destruct",
            pos = pos,
        }
        minetest.add_particlespawner {
            pos = pos,
            vel = vector.zero(),
            acc = vector.new(0, -9.81, 0),
            drag = vector.new(3, 0, 3),
            radius = 0.1,
            attract = {
                kind = "point",
                origin = pos,
                strength = -50
            },
            texture = {
                name = "artifact_light_gold.png",
                alpha_tween = {1, 0}
            },
            size_tween = {
                {
                    min = 3,
                    max = 6
                },
                {
                    min = 1,
                    max = 2
                }
            },
            time = 0.3,
            amount = 100,
        }
    end
})

artifact.register_node("forcefield_generator_off", {
    drawtype = "mesh",
    mesh = "artifact_forcefield_generator.gltf",
    tiles = {"artifact_forcefield_generator_off.png"},
    paramtype = "light",
    light_source = 6,
    paramtype2 = "facedir",
    collision_box = {
        type = "fixed",
        fixed = {
            -1, -0.5, -0.5,
            1, 0.5, 0.5
        }
    },
    selection_box = {
        type = "fixed",
        fixed = {
            -1, -0.5, -0.5,
            1, 0.5, 0.5
        }
    },
    sounds = artifact.sounds.metal,
    on_impact = artifact.debug and function(pos)
        local node = minetest.get_node(pos)
        node.name = "forcefield_generator"
        minetest.set_node(pos, node)
    end or nil,
})
