local S = minetest.get_translator("lzr_levers")

lzr_levers = {}

local registered_after_pulled_levers = {}
-- Register a function that is called when the player pulls a lever.
-- callback(pos, new_node, player):
-- * pos: Lever position
-- * new_node: Node table of *new* lever node
-- * player: Player who pulled the lever
-- Note: The callback function MUST NOT manipulate the map!
lzr_levers.register_after_pulled_lever = function(callback)
	table.insert(registered_after_pulled_levers, callback)
end
local call_pull_lever_callbacks = function(pos, new_node, player)
	for c=1, #registered_after_pulled_levers do
		local callback = registered_after_pulled_levers[c]
		callback(pos, new_node, player)
	end
end

local activate_stone_lever = function(pos, node, player, return_actions)
	local actions = {}
	local new_node = { name = "lzr_levers:lever_stone_on", param2 = node.param2 }
	if return_actions then
		table.insert(actions, { type = "swap_node", node = new_node })
	else
		minetest.swap_node(pos, new_node)
	end
	if player then
		call_pull_lever_callbacks(pos, new_node, player)
	end
	if return_actions then
		table.insert(actions, {type = "sound_play", sound = {name="lzr_levers_lever_activate", gain=0.7}})
		return actions
	else
		minetest.sound_play({name="lzr_levers_lever_activate", gain=0.7}, {pos=pos}, true)
		-- Update lasers and communicate the new lever state
		lzr_laser.full_laser_update(nil, nil, {lever_pos_activate = pos})
	end
end
local deactivate_stone_lever = function(pos, node, player, return_actions)
	local actions = {}
	local new_node = { name = "lzr_levers:lever_stone_off", param2 = node.param2 }
	if return_actions then
		table.insert(actions, { type = "swap_node", node = new_node })
	else
		minetest.swap_node(pos, new_node)
	end
	if player then
		call_pull_lever_callbacks(pos, new_node, player)
	end
	if return_actions then
		table.insert(actions, {type = "sound_play", sound = {name="lzr_levers_lever_deactivate", gain=0.7}})
		return actions
	else
		minetest.sound_play({name="lzr_levers_lever_deactivate", gain=0.7}, {pos=pos}, true)
		-- Update lasers and communicate the new lever state
		lzr_laser.full_laser_update(nil, nil, {lever_pos_deactivate = pos})
	end
end

function lzr_levers.pull_lever(pos, player)
	local node = minetest.get_node(pos)
	if node.name == "lzr_levers:lever_stone_on" then
		deactivate_stone_lever(pos, node, player, false)
		return true
	elseif node.name == "lzr_levers:lever_stone_off" then
		activate_stone_lever(pos, node, player, false)
		return true
	end
	return false
end

local was_front_punched = function(param2, pointed_thing)
	if pointed_thing.type ~= "node" then
		return false
	end
	local ptdir = vector.subtract(pointed_thing.under, pointed_thing.above)
	local p2dir = minetest.wallmounted_to_dir(param2)
	if vector.equals(ptdir, p2dir) then
		return true
	else
		return false
	end
end

minetest.register_node("lzr_levers:lever_stone_off", {
	description = S("Lever (off)"),
	_tt_help = S("Can be punched to send a signal to receivers"),
	drawtype = "normal",
	paramtype2 = "wallmounted",
	wallmounted_rotate_vertical = true,
	inventory_image = "lzr_levers_stone_lever_inv_off.png",
	tiles = {
		"lzr_levers_stone_lever_front_off.png",
		"lzr_levers_stone_lever_sides.png",
	},
	groups = { breakable = 1, rotatable = 3, sender = 1, lever = 1 },
	sounds = lzr_sounds.node_sound_stone_defaults(),
	on_punch = function(pos, node, puncher, pointed_thing)
		local gs = lzr_gamestate.get_state()
		if gs ~= lzr_gamestate.LEVEL and gs ~= lzr_gamestate.LEVEL_TEST and gs ~= lzr_gamestate.EDITOR then
			return
		end
		if not was_front_punched(node.param2, pointed_thing) then
			return
		end

		-- Activate lever
		minetest.log("action", "[lzr_levers] "..puncher:get_player_name().." pulls lever '"..node.name.."' on at "..minetest.pos_to_string(pos))
		activate_stone_lever(pos, node, puncher, false)
	end,

	_lzr_on_toggle = function(pos, node)
		return activate_stone_lever(pos, node, nil, true)
	end,

	after_place_node = lzr_laser.trigger_after_place_node,
	after_dig_node = lzr_laser.trigger_after_dig_node,

	_lzr_element_group = "lever",
})

minetest.register_node("lzr_levers:lever_stone_on", {
	description = S("Lever (on)"),
	_tt_help = S("Can be punched to send a signal to receivers"),
	drawtype = "normal",
	paramtype2 = "wallmounted",
	wallmounted_rotate_vertical = true,
	inventory_image = "lzr_levers_stone_lever_inv_on.png",
	tiles = {
		"lzr_levers_stone_lever_front_on.png",
		"lzr_levers_stone_lever_sides.png",
	},
	groups = { breakable = 1, rotatable = 3, sender = 1, lever = 2 },
	sounds = lzr_sounds.node_sound_stone_defaults(),
	on_punch = function(pos, node, puncher, pointed_thing)
		local gs = lzr_gamestate.get_state()
		if gs ~= lzr_gamestate.LEVEL and gs ~= lzr_gamestate.LEVEL_TEST and gs ~= lzr_gamestate.EDITOR then
			return
		end
		if not was_front_punched(node.param2, pointed_thing) then
			return
		end

		-- Deactivate lever
		minetest.log("action", "[lzr_levers] "..puncher:get_player_name().." pulls lever '"..node.name.."' off at "..minetest.pos_to_string(pos))
		deactivate_stone_lever(pos, node, puncher, false)
	end,

	after_place_node = lzr_laser.trigger_after_place_node,
	after_dig_node = lzr_laser.trigger_after_dig_node,

	_lzr_on_toggle = function(pos, node)
		return deactivate_stone_lever(pos, node, nil, true)
	end,

	_lzr_element_group = "lever",
})
