local S = minetest.get_translator("testpathfinder")

-- Config parameters

-- Maximum direct distance between start and end
local MAX_DIRECT_DISTANCE = 64
-- Maximum search distance
local MAX_SEARCH_DISTANCE = 32
-- Maximum permitted jump height
local MAX_JUMP = 1
-- Maximum permitted drop height
local MAX_DROP = 5
-- If true, mod won't refuse to run pathfinder even at long distances
local IGNORE_MAX_DISTANCE_SAFEGUARD = false

-- End of config parameters

local timer = 0
local algorithms = {
	"A*_noprefetch",
	"A*",
	"Dijkstra",
}

local function find_path_for_player(player, itemstack)
	local meta = itemstack:get_meta()
	if not meta then
		return
	end
	local x = meta:get_int("pos_x")
	local y = meta:get_int("pos_y")
	local z = meta:get_int("pos_z")
	local algo = meta:get_int("algorithm")
	if x and y and z then
		local pos2 = {x=x, y=y, z=z}
		algo = algorithms[algo+1]
		local pos1 = vector.round(player:get_pos())
		-- Don't bother calling pathfinder for high distance to avoid freezing
		if (not IGNORE_MAX_DISTANCE_SAFEGUARD) and (vector.distance(pos1, pos2) > MAX_DIRECT_DISTANCE) then
			minetest.chat_send_player(player:get_player_name(), S("Destination too far away! Set a destination (via placing) within a distance of @1 and try again!", MAX_DIRECT_DISTANCE))
			return
		end
		local str = S("Path from @1 to @2:",
			minetest.pos_to_string(pos1),
			minetest.pos_to_string(pos2))

		minetest.chat_send_player(player:get_player_name(), str)
		local time_start = minetest.get_us_time()
		local path = minetest.find_path(pos1, pos2, MAX_SEARCH_DISTANCE, MAX_JUMP, MAX_DROP, algo)
		local time_end = minetest.get_us_time()
		local time_diff = time_end - time_start
		str = ""
		if not path then
			minetest.chat_send_player(player:get_player_name(), S("No path!"))
			minetest.chat_send_player(player:get_player_name(), S("Time: @1 ms", time_diff/1000))
			return
		end
		for s=1, #path do
			str = str .. minetest.pos_to_string(path[s]) .. "\n"
			local t
			if s == #path then
				t = "testpathfinder_waypoint_end.png"
			elseif s == 1 then
				t = "testpathfinder_waypoint_start.png"
			else
				local c = math.floor(((#path-s)/#path)*255)
				t = string.format("testpathfinder_waypoint.png^[multiply:#%02x%02x00", 0xFF-c, c)
			end
			minetest.add_particle({
				pos = path[s],
				expirationtime = 5 + 0.2 * s,
				playername = player:get_player_name(),
				glow = minetest.LIGHT_MAX,
				texture = t,
				size = 3,
			})
		end
		minetest.chat_send_player(player:get_player_name(), str)
		minetest.chat_send_player(player:get_player_name(), S("Path length: @1", #path))
		minetest.chat_send_player(player:get_player_name(), S("Time: @1 ms", time_diff/1000))
	end
end

local function set_destination(itemstack, user, pointed_thing)
	if not (user and user:is_player()) then
		return
	end
	local name = user:get_player_name()
	local obj
	local meta = itemstack:get_meta()
	if pointed_thing.type == "node" then
		local pos = pointed_thing.above
		meta:set_int("pos_x", pos.x)
		meta:set_int("pos_y", pos.y)
		meta:set_int("pos_z", pos.z)
		minetest.chat_send_player(user:get_player_name(), S("Destination set to @1", minetest.pos_to_string(pos)))
		return itemstack
	end
end

local function find_path_or_set_algorithm(itemstack, user, pointed_thing)
	if not (user and user:is_player()) then
		return
	end
	local ctrl = user:get_player_control()
	-- No sneak: Find path
	if not ctrl.sneak then
		find_path_for_player(user, itemstack)
	else
	-- Sneak: Set algorithm
		local meta = itemstack:get_meta()
		local algo = meta:get_int("algorithm")
		algo = (algo + 1) % #algorithms
		meta:set_int("algorithm", algo)
		minetest.chat_send_player(user:get_player_name(), S("Algorithm: @1", algorithms[algo+1]))
		return itemstack
	end
end

-- Punch: Find path
-- Sneak+punch: Select pathfinding algorithm
-- Place: Select destination node
minetest.register_tool("testpathfinder:testpathfinder", {
	description = S("Pathfinder Tester") .."\n"..
		S("Finds path between 2 points") .."\n"..
		S("Place on node: Select destination") .."\n"..
		S("Punch: Find path from here") .."\n"..
		S("Sneak+Punch: Change algorithm"),
	inventory_image = "testpathfinder_testpathfinder.png",
	groups = { testtool = 1, disable_repair = 1 },
	on_use = find_path_or_set_algorithm,
	on_secondary_use = set_destination,
	on_place = set_destination,
})