local modname = minetest.get_current_modname()

local function get_random_colormod()
	local r = math.random(256) - 1
	local g = math.random(256) - 1
	local b = math.random(256) - 1
	local hex = string.format("%.2X%.2X%.2X", r, g, b)
	return "^[multiply:#" .. hex
end

local function get_dragonfly_textures(colormod)
	return {
		"eg_bugs_dragonfly_thorax.png" .. colormod,
		"eg_bugs_dragonfly_wings.png",
		"eg_bugs_dragonfly_head.png" .. colormod,
		"eg_bugs_dragonfly_eye.png",
	}
end

local function get_firebreath_toolcaps(size)
	return {
		full_punch_interval = 10,
		damage_groups = {
			fire = math.ceil(size)
		}
	}
end

minetest.register_entity(modname .. ":firebreath", {
	initial_properties = {
		visual = "sprite",
		textures = {"eg_bugs_firebreath.png"},
		glow = 10, --BROKEN?
		collide_with_objects = true,
		physical = true,
		collisionbox = {-0.25, -0.35, -0.25, 0.25, 0.25, 0.25},
	},

	on_activate = function(self, staticdata)
		local data = minetest.deserialize(staticdata)
		-- Don't brick the world with crashes if no staticdata is passed for some reason.
		if not data then
			self.object:remove()
			return
		end
		self.object:set_velocity(data.vel)
		self.size = data.size
	end,

	get_staticdata = function(self)
		local data = {
			vel = self.object:get_velocity(),
			size = self.size,
		}
		if not vel then return end
		return minetest.serialize(data)
	end,

	on_step = function(self, dtime, moveresult)
		self.size = self.size - dtime
		local pos = self.object:get_pos()

		if self.size < 0.1 or 
				minetest.get_item_group(minetest.get_node(pos).name, "water") > 0 then
			self.object:remove()
			return
		end

		if moveresult.collides then
			-- Spawn a smoke puff
			minetest.add_particlespawner({
				amount = 10,
				time = 0.1,
				minpos = pos,
				maxpos = pos,
				glow = 15,
				minvel = vector.new(-0.3, 1, -0.3),
				maxvel = vector.new(0.3, 3, 0.3),
				minsize = 1,
				maxsize = 5,
				texture = "eg_bugs_smokepuff.png",
				minexptime = 0.3,
				maxexptime = 0.5,
			})
			-- Damage collided objects
			for i, v in ipairs(moveresult.collisions) do
				if v.type == "object" then
					v.object:punch(self.object, 10, get_firebreath_toolcaps(self.size))
				end
			end
			--disappear
			self.object:remove()
			return
		end

		local props = self.object:get_properties()
		local collbox_offset = self.size * 0.125
		props.collisionbox = {
			-collbox_offset,
			-collbox_offset,
			-collbox_offset,
			collbox_offset,
			collbox_offset,
			collbox_offset,
		}
		props.visual_size = vector.new(self.size, self.size, self.size)
		self.object:set_properties(props)
	end,
})

minetest.register_craftitem(modname .. ":firebreath_debug", {
	inventory_image = "eg_bugs_firebreath.png",
	on_use = function(itemstack, user)
		local dir = user:get_look_dir()
		local pos = user:get_pos()
		pos.y = pos.y + 1.4
		pos = vector.add(pos, dir)
		local data = {vel = vector.multiply(dir, 5), size = math.random(3)}
		minetest.add_entity(pos, modname .. ":firebreath", minetest.serialize(data))
	end
})

local function lq_hover_to_pos(self, pos, turn_speed, tolerance, speed_factor)
	speed_factor = speed_factor or 1
	tolerance = tolerance or 1
	turn_speed = turn_speed or 1
	local oldpos = pos
	local func = function()
		local selfpos = self.object:get_pos()
		-- Stop when there or stuck
		if vector.distance(selfpos, pos) < tolerance or
				vector.distance(selfpos, oldpos) < self.max_speed * speed_factor * self.dtime then
			return true
		end
		oldpos = selfpos
		local dir = vector.direction(selfpos, pos)
		mobkit.turn2yaw(self, minetest.dir_to_yaw(dir), turn_speed)
		self.object:set_velocity(vector.multiply(dir, self.max_speed * speed_factor))
	end
	mobkit.queue_low(self, func)
end

local function lq_turn2yaw_hovering(self, desired_yaw, rate)
	local func = function()
		self.object:set_velocity(vector.new(0, 0, 0)) -- TODO: maybe small random movement
		return mobkit.turn2yaw(self, desired_yaw, rate)
	end
	mobkit.queue_low(self, func)
end

local function lq_turn2pos_hovering(self, destpos, rate)
	local yaw = minetest.dir_to_yaw(vector.direction(self.object:get_pos(), destpos))
	lq_turn2yaw_hovering(self, yaw, rate)
end

local function flyable(selfpos, tgtpos)
	local raycast = Raycast(selfpos, tgtpos, true, true)
	raycast:next() --Get self out of list
	return not raycast:next()
end

local function attack(self, tgt)
	local data = {}
	local selfpos = self.object:get_pos()
	local tgtpos = tgt:get_pos()
	tgtpos.y = tgtpos.y + 1
	local dir = vector.direction(selfpos, tgtpos)
	local pos = vector.add(selfpos, dir)

	data.vel = vector.multiply(dir, 10)
	data.size = math.random(3)
	minetest.add_entity(pos, modname .. ":firebreath", minetest.serialize(data))
	mobkit.make_sound(self, "breathefire")
end

local function lq_strafe(self, tgtpos)
	local selfpos = self.object:get_pos()
	local dir = vector.direction(selfpos, tgtpos)
	local dest
	for i = 1, 4 do
		dest = vector.add(selfpos, vector.multiply(vector.rotate_around_axis(
					vector.new(0, 0, 1),
					dir,
					math.random() * 2 * math.pi),
				math.random() * 2 + 1))
		if flyable(selfpos, dest) then
			break
		end
	end
	lq_hover_to_pos(self, dest, 6)
end

minetest.register_on_joinplayer(function(player)
	player:set_armor_groups({fire = 100})
end)

local function hq_dragonfly_roam(self, prty)
	local func = function()
		if mobkit.timer(self, 0.5) then
			if not mobkit.is_queue_empty_low(self) then
				return
			end
			local rand = math.random(10)
			if rand == 1 then
				lq_turn2yaw_hovering(self, math.random() * 2 * math.pi, 4)
			elseif rand > 1 then
				--find position
				local rand_vec = vector.normalize(vector.new(math.random() - 0.5, math.random() - (not self.isonground and 0.55 or 0), math.random() - 0.5))
				if vector.length(rand_vec) == 0 then
					return
				end
				rand_vec = vector.multiply(rand_vec, math.random(self.view_range))
				local pos = self.object:get_pos()
				local dest = vector.add(pos, rand_vec)
				--fly to position if possible
				if flyable(pos, dest) then
					lq_hover_to_pos(self, dest, 6)
				end
			end
		end
	end
	mobkit.queue_high(self, func, prty)
end

local function hq_dragonfly_hunt(self, tgtobj, prty)
	local tgt = tgt or mobkit.get_nearby_player(self)
	local func = function()
		if not mobkit.timer(self, 0.5) then
			return
		end
		if not mobkit.exists(tgt) then
			--TODO: test if gives up dead player
			return true
		end
		mobkit.clear_queue_low(self)

		local selfpos = self.object:get_pos()
		local tgtpos = tgt:get_pos()
		tgtpos.y = tgtpos.y + 1 --TODO: un-hardcode
		local dist = vector.distance(selfpos, tgtpos)

		if dist > 16 then
			local cpos = vector.divide(vector.add(tgtpos, selfpos), 2)
			if not flyable(selfpos, cpos) then
				cpos = vector.add(selfpos, vector.new(math.random() * 2 - 1,
						math.random() * 2 - 1,
						math.random() * 2 - 1))
			end
			lq_hover_to_pos(self, cpos, 6)
		elseif dist < 5 then
			local dir = vector.direction(tgtpos, selfpos)
			local cpos = vector.add(tgtpos, vector.multiply(dir, 6))
			for i = 1, 5 do
				if flyable(selfpos, cpos) then
					break
				end
				cpos = vector.add(selfpos, vector.new(math.random() * 8 - 4,
						math.random() * 8 - 3,
						math.random() * 8 - 4))
			end
			lq_hover_to_pos(self, cpos, 6)
		elseif math.random(2) == 1 then
			lq_turn2pos_hovering(self, tgtpos, 6)
			attack(self, tgt)
		else
			lq_strafe(self, tgtpos)
		end
	end
	mobkit.queue_high(self, func, prty)
end


minetest.register_entity(modname .. ":dragonfly",
	{
		initial_properties =
		{
			visual = "mesh",
			mesh = "eg_bugs_dragonfly.b3d",
			physical = true,
			collisionbox = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25},
			glow = 5,
			backface_culling = false,
		},
		timeout = 50,
		buoyancy = 0.2,
		lung_capacity = 10,
		max_hp = 10,
		sounds = {
			breathefire = 
				"eg_bugs_dragonfly_firebreath",
		},
		on_step = mobkit.stepfunc,
		on_activate = function(self, staticdata, dtime_s)
			mobkit.actfunc(self, staticdata, dtime_s)
			local props = self.object:get_properties()

			-- set texture
			local colormod = mobkit.recall(self, "colormod")
			if not colormod then
				colormod = get_random_colormod()
				mobkit.remember(self, "colormod", colormod)
			end
			props.textures = get_dragonfly_textures(colormod)
			
			-- set size
			local size = mobkit.recall(self, "size")
			if not size then
				size = math.random()
				-- hypercube size so small bugs are more common
				size = (size * size * size * size * 3) + 1
				mobkit.remember(self, "size", size)
			end
			props.visual_size = vector.new(size, size, size)
			for i, v in ipairs(props.collisionbox) do
				props.collisionbox[i] = v * size
			end
			self.object:set_properties(props)
			hq_dragonfly_roam(self, 1)
		end,
		get_staticdata = mobkit.statfunc,
		logic = function(self)
			if mobkit.is_queue_empty_low(self) then
				self.object:set_acceleration({x = 0, y = -0.2, z = 0})
			end
			if mobkit.get_queue_priority(self) < 5 then
				local p = mobkit.get_nearby_player(self)
				if p then
					hq_dragonfly_hunt(self, p, 10)
				end
			end
		end,
		max_speed = 5,
		jump_height = 1,
		view_range = 16,
		attack = {range = 1, damage_groups = {fleshy = 2}},
		armor_groups = {fleshy = 3}
	})
