cow_los = {}

local SIGHT_LENGTH = 50
local MAX_RAYCAST_TRIES = 100

local LASER_RADIUS = 0.05
local LASER_TTL = 1
local LASER_MIN_ALPHA = 0x20
local LASER_MAX_ALPHA = 0xC0
local DEFAULT_LASER_LENGTH = 2
local TIME_BETWEEN_SHOTS = 0.2
local LASER_DISABLED_TIME = 3.0

local laser_disabled = false
local laser_disabled_for = 0

core.register_entity("cow_los:laser", {
	initial_properties = {
		visual = "cube",
		textures = {
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
			"cow_los_laser.png^[opacity:"..LASER_MAX_ALPHA,
		},
		use_texture_alpha = true,
		pointable = false,
		collisionbox = { -LASER_RADIUS, -(DEFAULT_LASER_LENGTH/2), -(DEFAULT_LASER_LENGTH/2), LASER_RADIUS, DEFAULT_LASER_LENGTH/2, DEFAULT_LASER_LENGTH/2 },
		visual_size = { x=LASER_RADIUS, y=DEFAULT_LASER_LENGTH, z=DEFAULT_LASER_LENGTH },
		physical = false,
		collide_with_objects = false,
		static_save = false,
	},
	_timer = 0,
	on_activate = function(self)
		self.object:set_armor_groups({immortal=1})
	end,
	on_step = function(self, dtime)
		self._timer = self._timer + dtime
		if self._timer >= LASER_TTL then
			self.object:remove()
			return
		end
		local alpha = (LASER_TTL-self._timer) / LASER_TTL
		alpha = math.ceil(math.max(LASER_MIN_ALPHA, math.min(LASER_MAX_ALPHA, alpha * LASER_MAX_ALPHA)))
		local textures = {
			"cow_los_laser.png^[opacity:"..alpha,
			"cow_los_laser.png^[opacity:"..alpha,
			"cow_los_laser.png^[opacity:"..alpha,
			"cow_los_laser.png^[opacity:"..alpha,
			"cow_los_laser.png^[opacity:"..alpha,
			"cow_los_laser.png^[opacity:"..alpha,
		}
		self.object:set_properties({textures=textures})
	end,
})

function cow_los.temp_disable_laser(player, time)
	laser_disabled = true
	laser_disabled_for = LASER_DISABLED_TIME
end

function cow_los.get_seen(player, origin, target)
	local raycast = core.raycast(origin, target, true)
	local pointed
	local tries = 0
	repeat
		pointed = raycast:next()
		tries = tries + 1
	until tries > MAX_RAYCAST_TRIES or pointed == nil or (pointed.type == "object" and pointed.ref ~= player) or (pointed.type == "node")
	return pointed
end

function cow_los.get_los_render_origin(player)
	local pos = player:get_pos()

	local yaw = player:get_look_horizontal()
	local offset = vector.new(0.12, 1.15, 0.85)
	offset = vector.rotate_around_axis(offset, vector.new(0, 1, 0), yaw)

	local origin = vector.add(pos, offset)
	return origin
end

function cow_los.get_los_origin(player)
	return cow_los.get_eye_pos(player)
end

function cow_los.get_eye_pos(player)
	local pos = player:get_pos()
	local _, eye_offset = player:get_eye_offset()

	local yaw = player:get_look_horizontal()
	eye_offset = vector.rotate_around_axis(eye_offset, vector.new(0, 1, 0), yaw)

	eye_offset = vector.multiply(eye_offset, 0.1)

	local eye_height = player:get_properties().eye_height
	local eye_pos = vector.add(pos, eye_offset)
	eye_pos = vector.offset(eye_pos, 0, eye_height, 0)

	return eye_pos
end

function cow_los.get_los_target(player)
	local origin = cow_los.get_los_origin(player)
	local look_dir = player:get_look_dir()
	look_dir = vector.multiply(look_dir, SIGHT_LENGTH)

	local look_target = vector.add(origin, look_dir)

	local seen = cow_los.get_seen(player, origin, look_target)
	local real_target
	if seen then
		real_target = seen.intersection_point
	else
		real_target = look_target
	end
	return real_target, seen
end

function cow_los.render_los(origin, target)
	local laser_pos = vector.new(
		(origin.x+target.x)/2,
		(origin.y+target.y)/2,
		(origin.z+target.z)/2
	)
	local obj = core.add_entity(laser_pos, "cow_los:laser")

	local dir = vector.direction(origin, target)
	local rot = vector.dir_to_rotation(dir)
	obj:set_rotation(rot)

	local length = vector.distance(origin, target)
	obj:set_properties({
		visual_size = { x=LASER_RADIUS, y=LASER_RADIUS, z=length },
	})

end

local timer = 0
core.register_globalstep(function(dtime)
	if cow_game.get_state() ~= cow_game.STATE_PLAY then
		return
	end
	timer = timer + dtime
	if timer < TIME_BETWEEN_SHOTS then
		return
	end
	local total_time = dtime + timer
	if laser_disabled then
		core.log("error", laser_disabled_for)
		laser_disabled_for = laser_disabled_for - total_time
		if laser_disabled_for > 0 then
			return
		else
			laser_disabled_for = 0
			laser_disabled = false
		end
	end
	local players = core.get_connected_players()
	for p=1, #players do
		local player = players[p]
			if not cow_game.is_protected(player) then
			-- intentionally no laser sound
			local target, seen = cow_los.get_los_target(player)
			if seen then
				if seen.type == "object" then
					cow_objects.deal_laser_damage(player, seen.ref, total_time, seen.intersection_point)
				end
			end
			local render_origin = cow_los.get_los_render_origin(player)
			cow_los.render_los(render_origin, target)
		end
	end
	timer = 0
end)
