-- Get how long a trigger waypoint is shown
local get_trigger_waypoint_timer = function()
	local timer = tonumber(minetest.settings:get("lzr_pirate_sense_duration"))
	if timer then
		timer = math.min(10, math.max(0.1, timer))
	else
		timer = 1.7
	end
	return timer
end

-- Interval in seconds for updating the trigger waypoints
-- when at least one of the triggers being shown is
-- located at the player (for carrying a triggerable
-- node in the inventory)
local TRIGGER_PLAYER_WAYPOINT_UPDATE_INTERVAL = 0.8

local trigger_waypoints = {}
local active_trigger_info

local remove_trigger_waypoints = function(player)
	for t=1, #trigger_waypoints do
		player:hud_remove(trigger_waypoints[t])
	end
	trigger_waypoints = {}
	active_trigger_info = nil
	lzr_gui.hide_pirate_sense_icon(player)
end
local add_trigger_waypoints = function(player, waypoints, is_permanent)
	local waypoint_ids = {}
	for w=1, #waypoints do
		local wp = waypoints[w]

		local player = wp.player
		local texture = wp.texture
		local wpos = wp.wpos
		local size = wp.size or 1
		local z_index_add = wp.z_index_add

		local id = player:hud_add({
			type = "image_waypoint",
			text = texture,
			offset = { x = 0, y = 0 },
			scale = { x = 8 * size, y = 8 * size },
			z_index = -290 + (z_index_add or 0),
			world_pos = wpos,
		})
		if not id then
			return
		end
		table.insert(trigger_waypoints, id)
	end
end

-- Show information in the HUD of the trigger
-- with the given trigger ID. This shows waypoints
-- of senders/receivers associated to the trigger
-- particle paths towards those associated triggers
-- and other icons.
-- If the trigger for the given ID does not exist,
-- this function does nothing.
-- `trigger_id`: Trigger ID
-- `player`: Player doing the trigger
-- `show_trigger_type`: If `true`, also show icons for the sender/receiver type (default: false)
-- `show_permanent`: If `true`, show the trigger info permanently. The triggers will be hidden again
-- `timer`: Don't use / set to nil. For internal use only
-- if they are hidden by a function call, or if temporary trigger info is shown and expires
lzr_laser.show_trigger_info = function(trigger_id, player, show_trigger_type, show_permanent, timer)
	remove_trigger_waypoints(player)

	local pending_trigger_waypoints = {}
	local add_pending_trigger_waypoint = function(player, texture, wpos, size, z_index_add, is_permanent)
		table.insert(pending_trigger_waypoints, {
			player = player,
			texture = texture,
			wpos = wpos,
			size = size,
			z_index_add = z_index_add,
			is_permanent = is_permanent,
		})
	end

	local trigger = lzr_triggers.get_trigger(trigger_id)
	if not trigger then
		return
	end

	local tpos
	if type(trigger.location) == "table" then
		tpos = trigger.location
	elseif trigger.location == "player" then
		tpos = player:get_pos()
		tpos = vector.offset(tpos, 0, 1, 0)
	else
		return
	end
	active_trigger_info = {
		trigger_id = trigger_id,
		player = player,
		show_trigger_type = show_trigger_type,
		show_permanent = show_permanent,
	}
	if trigger.location == "player" then
		active_trigger_info.trigger_at_player = true
	else
		active_trigger_info.trigger_at_player = false
	end
	if not show_permanent then
		if not timer then
			timer = get_trigger_waypoint_timer()
		end
		active_trigger_info.timer = timer
	end

	-- The dotted lines between signal and receiver count as
	-- a 'signal line', so may can be disabled by a game setting
	local show_signal_lines = minetest.settings:get_bool("lzr_particles_signals", true)

	local receivers = lzr_triggers.get_receivers(trigger_id)
	local senders = lzr_triggers.get_senders(trigger_id)
	local added_waypoint = false
	if #receivers > 0 and #senders > 0 then
		add_pending_trigger_waypoint(player, "lzr_triggers_icon_sender_receiver.png", tpos, nil, nil)
		added_waypoint = true
	end
	if #receivers > 0 then
		if #senders == 0 then
			add_pending_trigger_waypoint(player, "lzr_triggers_icon_sender.png", tpos, nil, nil)
			added_waypoint = true
		end
		for r=1, #receivers do
			local rtrig = lzr_triggers.get_trigger(receivers[r])
			if not rtrig then
				local rpos = minetest.string_to_pos(receivers[r])
				if rpos then
					add_pending_trigger_waypoint(player, "lzr_laser_particle_unknown.png^[opacity:127", rpos, nil, nil)
					added_waypoint = true
				end
				minetest.log("error", "[lzr_laser] Receiver '"..receivers[r].." for trigger '"..trigger_id.."' does not exist!")
			else
				local rloc = rtrig.location
				if rloc == "player" then
					rloc = player:get_pos()
					rloc = vector.offset(rloc, 0, 1, 0)
					active_trigger_info.trigger_at_player = true
				end
				add_pending_trigger_waypoint(player, "lzr_triggers_icon_receiver.png^[opacity:127", rloc, nil, nil)
				added_waypoint = true
				if show_trigger_type then
					-- Small icon for receiver type
					local rloc_receiver_type = vector.offset(rloc, 0.125, 0.125, 0.125)
					add_pending_trigger_waypoint(player, lzr_triggers.RECEIVER_TYPE_ICONS[rtrig.receiver_type].."^[opacity:127", rloc_receiver_type, 0.4, 1)
				end
				if show_signal_lines then
					-- Dotted line between sender and receiver
					lzr_laser.particle_line(tpos, rloc, "lzr_laser_particle_signal_marker.png", 1, 0, 0, nil, nil, timer, timer)
				end
			end
		end
		if show_trigger_type then
			-- Small icon for sender type
			local tpos_sender_type = vector.offset(tpos, -0.125, -0.125, -0.125)
			add_pending_trigger_waypoint(player, lzr_triggers.SENDER_TYPE_ICONS[trigger.sender_type], tpos_sender_type, 0.4, 1)
		end
	end
	if #senders > 0 then
		if #receivers == 0 then
			add_pending_trigger_waypoint(player, "lzr_triggers_icon_receiver.png", tpos, nil, nil)
			added_waypoint = true
		end
		for s=1, #senders do
			local strig = lzr_triggers.get_trigger(senders[s])
			if not strig then
				local spos = minetest.string_to_pos(senders[s])
				if spos then
					add_pending_trigger_waypoint(player, "lzr_laser_particle_unknown.png^[opacity:127", spos, nil, nil)
					added_waypoint = true
				end
				minetest.log("error", "[lzr_laser] Sender '"..senders[s].." for trigger '"..trigger_id.."' does not exist!")
			else
				local sloc = strig.location
				if sloc == "player" then
					sloc = player:get_pos()
					sloc = vector.offset(sloc, 0, 1, 0)
					active_trigger_info.trigger_at_player = true
				end
				add_pending_trigger_waypoint(player, "lzr_triggers_icon_sender.png^[opacity:127", sloc, nil, nil)
				added_waypoint = true
				if show_trigger_type then
					-- Small icon for sender type
					local sloc_sender_type = vector.offset(sloc, -0.125, -0.125, -0.125)
					add_pending_trigger_waypoint(player, lzr_triggers.SENDER_TYPE_ICONS[strig.sender_type].."^[opacity:127", sloc_sender_type, 0.4, 1)
				end
				if show_signal_lines then
					-- Dotted line between sender and receiver
					lzr_laser.particle_line(tpos, sloc, "lzr_laser_particle_signal_marker.png", 1, 0, 0, nil, nil, timer, timer)
				end
			end
		end
		if show_trigger_type then
			-- Small icon for receiver type
			local tpos_receiver_type = vector.offset(tpos, 0.125, 0.125, 0.125)
			add_pending_trigger_waypoint(player, lzr_triggers.RECEIVER_TYPE_ICONS[trigger.receiver_type], tpos_receiver_type, 0.4, 1)
		end
	end
	if #pending_trigger_waypoints > 0 then
		add_trigger_waypoints(player, pending_trigger_waypoints, show_permanent)
	end
	if added_waypoint then
		lzr_gui.show_pirate_sense_icon(player, show_permanent)
	end
end

-- Hide all currently displayed trigger information,
-- as shown by show_trigger_info to the player,
-- in the HUD immediately.
lzr_laser.hide_trigger_info = function(player)
	remove_trigger_waypoints(player)
end

-- Hide trigger info on all game state changes
lzr_gamestate.register_on_enter_state(function()
	local players = minetest.get_connected_players()
	for p=1, #players do
		lzr_laser.hide_trigger_info(players[p])
	end
end)

local function update_trigger_info()
	lzr_laser.show_trigger_info(active_trigger_info.trigger_id, active_trigger_info.player, active_trigger_info.show_trigger_type, active_trigger_info.show_permanent, active_trigger_info.timer)
end

lzr_laser.register_after_laser_update(function()
	if active_trigger_info then
		if active_trigger_info.show_permanent or (active_trigger_info.timer and active_trigger_info.timer > 0) then
			update_trigger_info()
		end
	end
end)

local player_trigger_update_timer = 0
minetest.register_globalstep(function(dtime)
	if active_trigger_info then
		-- Remove trigger info on timeout
		if active_trigger_info.timer then
			active_trigger_info.timer = active_trigger_info.timer - dtime
			if active_trigger_info.timer <= 0 then
				remove_trigger_waypoints(active_trigger_info.player)
				return
			end
		end
		-- If any of the triggers being shown is located at the player, periodically
		-- re-send the trigger locations to account for the change in player position
		if active_trigger_info.trigger_at_player then
			player_trigger_update_timer = player_trigger_update_timer + dtime
			-- NOTE: Low update intervals may be quite resource-hungry
			if player_trigger_update_timer >= TRIGGER_PLAYER_WAYPOINT_UPDATE_INTERVAL then
				update_trigger_info()
				player_trigger_update_timer = 0
			end
		end
	end
end)
