-- LUALOCALS < ---------------------------------------------------------
local klots, minetest, pairs, vector
    = klots, minetest, pairs, vector
-- LUALOCALS > ---------------------------------------------------------

if klots.editmode then return end

local modname = minetest.get_current_modname()

local actuate_opacity = 96
local huddot_opacity = 128

local function playercast(player)
	local pos = player:get_pos()
	pos.y = pos.y + player:get_properties().eye_height
	local look = player:get_look_dir()
	local target = vector.add(pos, vector.multiply(look, 4))
	for pt in minetest.raycast(pos, target, false, false) do
		if pt.type == "node" then
			return pt
		end
	end
end

local function updatehud(player, huds, key, pos, ppos, img, scale)
	local diff = vector.subtract(ppos, pos)
	scale = scale / vector.length(diff)
	local myhud = huds[key]
	if not myhud then
		huds[key] = {
			id = player:hud_add({
					hud_elem_type = "image_waypoint",
					world_pos = pos,
					text = img,
					scale = {x = scale, y = scale},
					precision = 0,
					number = 0xffffff,
					z_index = -250,
					alignment = {x = 0, y = 0}
				}),
			pos = pos,
			scale = scale
		}
	else
		if not vector.equals(myhud.pos, pos) then
			player:hud_change(myhud.id, "world_pos", pos)
			myhud.pos = pos
		end
		local sdiff = myhud.scale / scale
		if sdiff < 0.99 or sdiff > 1.01 then
			player:hud_change(myhud.id, "scale", {x = scale, y = scale})
			myhud.scale = scale
		end
	end
end

local all_axes = {
	{x = 0.45, y = 0, z = 0},
	{x = 0, y = 0.45, z = 0},
	{x = 0, y = 0, z = 0.45}
}
local function settip(player, data, pt)
	if not pt then
		if not data.huds then return end
		for _, v in pairs(data.huds) do
			player:hud_remove(v.id)
		end
		data.huds = nil
		return
	end
	local huds = data.huds
	if not huds then
		huds = {}
		data.huds = huds
	end

	local norm = vector.subtract(pt.above, pt.under)
	local axes = {}
	for i = 1, #all_axes do
		if vector.dot(norm, all_axes[i]) == 0 then
			axes[#axes + 1] = all_axes[i]
		end
	end
	local pos = vector.multiply(vector.add(pt.under, pt.above), 0.5)
	local ppos = player:get_pos()
	ppos.y = ppos.y + player:get_properties().eye_height
	updatehud(player, huds, "hand", pos, ppos,
		modname .. "_actuate.png^[opacity:" .. actuate_opacity,
		1.25)

	local function lines(vx, vy, min, max, lbl)
		local top = vector.add(pos, vy)
		local bot = vector.subtract(pos, vy)
		for nx = min, max do
			local dx = vector.multiply(vx, nx / 2)
			local tpos = vector.add(top, dx)
			updatehud(player, huds, "t" .. lbl .. nx, tpos, ppos,
				modname .. "_huddot.png^[opacity:" .. huddot_opacity, 1)
			local bpos = vector.add(bot, dx)
			updatehud(player, huds, "b" .. lbl .. nx, bpos, ppos,
				modname .. "_huddot.png^[opacity:" .. huddot_opacity, 1)
		end
	end
	lines(axes[1], axes[2], -2, 2, "a")
	lines(axes[2], axes[1], -1, 1, "b")
end

klots.register_playerstep(function(player, data)
		if not minetest.check_player_privs(player, "interact") then
			return settip(player, data)
		end

		local pt = playercast(player)
		if not pt then return settip(player, data) end

		if klots.actuate_cooldown(pt.under) then
			return settip(player, data)
		end

		local node = minetest.get_node(pt.under)
		local def = minetest.registered_nodes[node.name]
		if def and def.on_actuate then
			return settip(player, data, pt)
		end

		return settip(player, data)
	end)
