local S, PS = minetest.get_translator("lzr_tools")

local instadig = { times = { [1] = 0, [2] = 0, [3] = 0}, uses=0, maxlevel=255}

lzr_tools = {}

-- Cheat tools and weapons


-- Digs (almost) any block
minetest.register_tool("lzr_tools:ultra_pickaxe", {
	--~ Tool that digs blocks
	description = S("Ultra Pickaxe"),
	_tt_help = S("Removes blocks except liquids"),
	inventory_image = "lzr_tools_ultra_pickaxe.png",
	groups = { pickaxe = 1, editor_tool = 1 },

	-- CAN point the normally non-pointable
	-- level barriers
	pointabilities = {
		nodes = {
			["group:barrier"] = true,
			["group:skull"] = true,
		},
	},

	range = 20,
	tool_capabilities = {
		groupcaps = {
			choppy = instadig,
			cracky = instadig,
			snappy = instadig,
			crumbly = instadig,
			oddly_breakable_by_hand = instadig,
			dig_immediate = instadig,
			takable = instadig,
			breakable = instadig,
		},
	},
})


-- Digs liquids
minetest.register_tool("lzr_tools:ultra_bucket", {
	--~ Tool that digs liquids
	description = S("Ultra Bucket"),
	_tt_help = S("Removes liquids"),
	inventory_image = "lzr_tools_ultra_bucket.png",
	groups = { bucket = 1, editor_tool = 1 },

	range = 20,
	liquids_pointable = true,
	tool_capabilities = {
		groupcaps = {
			liquid = instadig,
		},
	},
})


-- Add laser debug tools (requires a hidden setting)
if minetest.settings:get_bool("lzr_debug", false) then

	-- This tool digs lasers. Usually this is only useful when lasers
	-- are frozen because otherwise they instantly regenerate.
	minetest.register_tool("lzr_tools:laser_absorber", {
		--~ Hidden tool that digs lasers
		description = S("Laser Absorber"),
		_tt_help = S("Removes lasers"),
		inventory_image = "lzr_tools_laser_absorber.png",
		groups = { dev_tool = 1, not_in_creative_inventory = 1 },

		-- Can point and dig lasers
		pointabilities = {
			nodes = {
				["group:laser"] = true,
			},
		},

		range = 20,
		tool_capabilities = {
			groupcaps = {
				laser = instadig,
			},
		},
	})


	local ls_max_iterations = 0

	local function emit_lasers_raw(max_iterations)
		local minpos, maxpos = lzr_world.get_level_pos(), vector.add(lzr_world.get_level_pos(), lzr_world.get_level_size())
		local vmanip = minetest.get_voxel_manip(minpos, maxpos)
		local vpos1, vpos2 = vmanip:get_emerged_area()
		local varea = VoxelArea:new({MinEdge = vpos1, MaxEdge = vpos2})
		local vdata = vmanip:get_data()
		local vdata_p2 = vmanip:get_param2_data()
		local emit_state = {
			detections = {},
			out_of_bounds = {},
			crystal_outgoing = {},
			burning_cache = {},
			destroy_cache = {},
		}
		lzr_laser.emit_lasers_in_area(minpos, maxpos, varea, vdata, vdata_p2, emit_state, max_iterations)

		-- Write laser changes to map
		vmanip:set_data(vdata)
		vmanip:set_param2_data(vdata_p2)
		vmanip:write_to_map()
	end

	local clear_lasers = function(itemstack, user)
		local minpos, maxpos = lzr_world.get_level_pos(), vector.add(lzr_world.get_level_pos(), lzr_world.get_level_size())
		local vmanip = minetest.get_voxel_manip(minpos, maxpos)
		local vpos1, vpos2 = vmanip:get_emerged_area()
		local varea = VoxelArea:new({MinEdge = vpos1, MaxEdge = vpos2})
		local vdata = vmanip:get_data()

		lzr_laser.clear_lasers_in_area(minpos, maxpos, varea, vdata)

		-- Write laser changes to map
		vmanip:set_data(vdata)
		vmanip:write_to_map()

		return itemstack
	end

	local step_msg = function(user, iter)
		--~ Message shown when the 'laser stepper' debug tool was used to simulate the laser travel algorithm with a set number of iterations
		minetest.chat_send_player(user:get_player_name(), PS("Emitted lasers with @1 iteration.", "Emitted lasers with @1 iterations.", iter, iter))
	end

	-- Debug tool to test the laser travel algorithm.
	-- Simulates the laser travelling up to a certain maximum number of iterations.
	minetest.register_tool("lzr_tools:laser_stepper", {
		--~ Hidden debug tool to test the laser travel algorithm.
		description = S("Laser Stepper"),
		_tt_help = S("Simulates the laser travel algorithm up to a given number of iterations").."\n"..
			S("Punch: Increase laser iterations by 1").."\n"..
			S("Place: Decrease laser iterations by 1").."\n"..
			S("Place/Punch + Sneak: Multiply iterations change by 10"),
		inventory_image = "lzr_tools_laser_stepper.png",
		groups = { dev_tool = 1, not_in_creative_inventory = 1 },
		on_use = function(itemstack, user)
			clear_lasers(itemstack, user)
			if user:get_player_control().sneak then
				ls_max_iterations = ls_max_iterations + 10
			else
				ls_max_iterations = ls_max_iterations + 1
			end
			emit_lasers_raw(ls_max_iterations)
			step_msg(user, ls_max_iterations)
			return itemstack
		end,
		on_place = function(itemstack, user)
			clear_lasers(itemstack, user)
			if user:get_player_control().sneak then
				ls_max_iterations = ls_max_iterations - 10
			else
				ls_max_iterations = ls_max_iterations - 1
			end
			ls_max_iterations = math.max(0, ls_max_iterations)
			emit_lasers_raw(ls_max_iterations)
			step_msg(user, ls_max_iterations)
			return itemstack
		end,
		on_secondary_use = function(itemstack, user)
			clear_lasers(itemstack, user)
			if user:get_player_control().sneak then
				ls_max_iterations = ls_max_iterations - 10
			else
				ls_max_iterations = ls_max_iterations - 1
			end
			ls_max_iterations = math.max(0, ls_max_iterations)
			emit_lasers_raw(ls_max_iterations)
			step_msg(user, ls_max_iterations)
			return itemstack
		end,
	})

end

local variant_cycles = {
	next_nodes = {},
	prev_nodes = {},
}
lzr_tools.register_variant_cycle = function(cycle)
	for c=1, #cycle do
		local cn = c + 1
		if cn > #cycle then
			cn = 1
		end
		local cp = c - 1
		if cp < 1 then
			cp = #cycle
		end
		local node_self = cycle[c]
		local node_next = cycle[cn]
		local node_prev = cycle[cp]

		if variant_cycles.next_nodes[node_self] or variant_cycles.prev_nodes[node_self] then
			minetest.log("warning", "[lzr_tools] register_variant_cycle: Node '"..node_self.."' already was part of a variant cycle. Overwriting ...")
		end
		variant_cycles.next_nodes[node_self] = node_next
		variant_cycles.prev_nodes[node_self] = node_prev
	end
end

-- Add a new node `new_node` into an existing variant cycle after `node_after`.
-- `new_node` must not be already part of a variant cycle and `node_after`
-- must be part of a variant cycle.
lzr_tools.extend_variant_cycle = function(node_after, new_node)
	local added = false
	if variant_cycles.next_nodes[new_node] or variant_cycles.prev_nodes[new_node] then
		-- Can't extend cycle if new_node is already part of a cycle
		return false
	elseif variant_cycles.next_nodes[node_after] and variant_cycles.prev_nodes[node_after] then
		local old_next = variant_cycles.next_nodes[node_after]
		local old_prev = variant_cycles.prev_nodes[node_after]
		variant_cycles.next_nodes[node_after] = new_node
		variant_cycles.prev_nodes[old_next] = new_node
		variant_cycles.next_nodes[new_node] = old_next
		variant_cycles.prev_nodes[new_node] = old_prev
		return true
	else
		return false
	end
end

local use_variant_changer = function(itemstack, user, pointed_thing, forwards)
	if pointed_thing.type ~= "node" then
		return itemstack
	end
	local state = lzr_gamestate.get_state()
	if state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		minetest.chat_send_player(user:get_player_name(), S("This tool only works in the level editor or development mode."))
		return itemstack
	end
	local pos = pointed_thing.under
	if minetest.is_protected(pos, user:get_player_name()) then
		minetest.record_protection_violation(pos, user:get_player_name())
		return itemstack
	end
	local node = minetest.get_node(pos)
	local new_node
	if forwards then
		new_node = variant_cycles.next_nodes[node.name]
	else
		new_node = variant_cycles.prev_nodes[node.name]
	end
	local deactivated = false
	if not new_node then
		local def = minetest.registered_nodes[node.name]
		-- If node is not part of a cycle, try the
		-- inactive state instead (if one exists). That way, variants will
		-- also work for nodes with a laser in it, e.g. iron bars with laser.
		if def and def._lzr_inactive then
			if forwards then
				new_node = variant_cycles.next_nodes[def._lzr_inactive]
			else
				new_node = variant_cycles.prev_nodes[def._lzr_inactive]
			end
			deactivated = true
		end
	end
	if new_node then
		node.name = new_node
		minetest.swap_node(pos, node)
		local def = minetest.registered_nodes[node.name]
		if def and def.sounds and def.sounds.place then
			minetest.sound_play(def.sounds.place, {pos=pos}, true)
		end
		if deactivated then
			if state == lzr_gamestate.EDITOR then
				lzr_laser.full_laser_update()
			end
		end
	end
	return itemstack
end

minetest.register_tool("lzr_tools:variant_changer", {
	description = S("Block Variant Changer"),
	_tt_help = S("Changes a block to different variant"),
	inventory_image = "lzr_tools_variant_changer.png",
	groups = { editor_tool = 1 },
	on_use = function(itemstack, user, pointed_thing)
		return use_variant_changer(itemstack, user, pointed_thing, true)
	end,
	on_place = function(itemstack, user, pointed_thing)
		return use_variant_changer(itemstack, user, pointed_thing, false)
	end,
})

-- Register node variants
dofile(minetest.get_modpath("lzr_tools").."/variants.lua")
