-- param2 rotation table from:
-- https://forum.minetest.net/viewtopic.php?p=73195&sid=1d2d2e4e76ce2ef9c84646481a4b84bc#p73195
-- Axis * 4 + Rotation = Facedir
-- First in pair is axis
-- Second in pair is rotation
local raw_facedir = {
	x = {
		{{3, 0}, {3, 1}, {3, 2}, {3, 3}},
		{{4, 0}, {4, 3}, {4, 2}, {4, 1}},
		{{0, 0}, {1, 0}, {5, 2}, {2, 0}},
		{{0, 1}, {1, 1}, {5, 3}, {2, 1}},
		{{0, 2}, {1, 2}, {5, 0}, {2, 2}},
		{{0, 3}, {1, 3}, {5, 1}, {2, 3}}
	},
	y = {
		{{0, 0}, {0, 1}, {0, 2}, {0, 3}},
		{{5, 0}, {5, 3}, {5, 2}, {5, 1}},
		{{1, 0}, {3, 1}, {2, 2}, {4, 3}},
		{{2, 0}, {4, 1}, {1, 2}, {3, 3}},
		{{3, 0}, {2, 1}, {4, 2}, {1, 3}},
		{{4, 0}, {1, 1}, {3, 2}, {2, 3}}
	},
	z = {
		{{1, 0}, {1, 1}, {1, 2}, {1, 3}},
		{{2, 0}, {2, 3}, {2, 2}, {2, 1}},
		{{0, 0}, {4, 0}, {5, 0}, {3, 0}},
		{{0, 1}, {4, 1}, {5, 1}, {3, 1}},
		{{0, 2}, {4, 2}, {5, 2}, {3, 2}},
		{{0, 3}, {4, 3}, {5, 3}, {3, 3}}
	}
}

local facedir_rot = {}

local function pair_to_param2(pair)
	return pair[1] * 4 + pair[2]
end

for axis, raw_rot in pairs(raw_facedir) do
	facedir_rot[axis] = {}
	for j, pair_list in pairs(raw_rot) do
		for i = 1, 4 do
			local back_index = i - 1
			if back_index == 0 then
				back_index = 4
			end
			local next_index = i + 1
			if next_index == 5 then
				next_index = 1
			end
			local back = pair_to_param2(pair_list[back_index])
			local next = pair_to_param2(pair_list[next_index])
			local current = pair_to_param2(pair_list[i])
			facedir_rot[axis][current] = {next, back}
		end
	end
end

local raw_wallmounted = {
	x = {4, 0, 5, 1},
	y = {2, 4, 3, 5},
	z = {3, 0, 2, 1},
}

local wallmounted_rot = {}

for axis, rots in pairs(raw_wallmounted) do
	wallmounted_rot[axis] = {}
	local param2s_unused = {
		[0] = true, [1] = true, [2] = true,
		[3] = true, [4] = true, [5] = true}
	for i = 1, 4 do
		local back_index = i - 1
		if back_index == 0 then
			back_index = 4
		end
		local next_index = i + 1
		if next_index == 5 then
			next_index = 1
		end
		local current = rots[i]
		local back = rots[back_index]
		local next = rots[next_index]
		wallmounted_rot[axis][current] = {back, next}
		param2s_unused[current] = nil
	end

	for param2, bool in pairs(param2s_unused) do
		wallmounted_rot[axis][param2] = {param2, param2}
	end
end

function edit.rotate_param2(node, rot_vect)
	local def = minetest.registered_items[node.name]
	if not node.param2 or not def then return end
	local paramtype2 = def.paramtype2
	local is_wallmounted = paramtype2 == "wallmounted" or paramtype2 == "colorwallmounted"
	local is_facedir = paramtype2 == "facedir" or paramtype2 == "colorfacedir"
	local rot_table = is_facedir and facedir_rot or wallmounted_rot
	if is_facedir or is_wallmounted then
		local param2_rot = node.param2 % 32 -- Get first 5 bits
		if is_wallmounted then
			param2_rot = node.param2 % 8 -- Get first 3 bits
		end
		local param2_other = node.param2 - param2_rot
		for axis, target_rot in pairs(rot_vect) do
			if target_rot ~= 0 then
				local direction = math.sign(target_rot)
				for rot = direction, target_rot / (math.pi / 2), direction do
					if target_rot > 0 then
						param2_rot = rot_table[axis][param2_rot][1]
					else
						param2_rot = rot_table[axis][param2_rot][2]
					end
				end
			end
		end
		node.param2 = param2_other + param2_rot
	elseif paramtype2 == "degrotate" or paramtype2 == "colordegrotate" then
		local param2_rot
		local deg_per_unit
		if paramtype2 == "degrotate" then
			param2_rot = node.param2
			deg_per_unit = 1.5
		else
			param2_rot = node.param2 % 32 -- Get first 5 bits
			deg_per_unit = 15
		end
		local param2_other = node.param2 - param2_rot
		local rot = param2_rot * deg_per_unit / 180 * math.pi
	
		rot = rot + rot_vect.y
		rot = rot % (math.pi * 2)
		if rot < 0 then
			rot = rot + math.pi * 2
		end

		param2_rot = math.round(rot / math.pi * 180 / deg_per_unit)
		node.param2 = param2_other + param2_rot
	end
end

local function screwdriver_run(player, pointed_thing, rotate_y)
	if not edit.on_place_checks(player) then return end
	if pointed_thing.type ~= "node" then return end
	local pos = pointed_thing.under
	local node = minetest.get_node(pos)
	local def = minetest.registered_items[node.name]
	if not def then return end
	local rot = vector.new(0, 0, 0)
	local paramtype2 = def.paramtype2
	if paramtype2 == "degrotate" or paramtype2 == "colordegrotate" then
		if rotate_y then
			local deg = paramtype2 == "degrotate" and 1.5 or 15
			rot.y = deg / 180 * math.pi
			if player:get_player_control().aux1 then
				rot.y = rot.y * 4
			end
		end
	else
		if rotate_y then
			rot = vector.new(0, math.pi / 2, 0)
		else
			local player_pos = player:get_pos()
			local diff = vector.subtract(player_pos, pos)
			local abs_diff = vector.apply(diff, math.abs)
			if abs_diff.x > abs_diff.z then
				local sign = (diff.x > 0) and 1 or -1
				rot = vector.new(0, 0, math.pi / 2 * sign)
			else
				local sign = (diff.z < 0) and 1 or -1
				rot = vector.new(math.pi / 2 * sign, 0, 0)
			end
		end
	end
	local old_node = table.copy(node)
	edit.rotate_param2(node, rot)
	if def.on_rotate then
		def.on_rotate(pos, old_node, player, 1, node.param2)
	end
	minetest.swap_node(pos, node)
end

minetest.register_tool("edit:screwdriver", {
	description = "Edit Screwdriver",
	inventory_image = "edit_screwdriver.png",
	groups = {not_in_creative_inventory = 1},
	on_use = function(itemstack, user, pointed_thing)
		screwdriver_run(user, pointed_thing, true)
		return itemstack
	end,
	on_place = function(itemstack, user, pointed_thing)
		screwdriver_run(user, pointed_thing, false)
		return itemstack
	end,
})
