-------------------
-- PaleoTest API --
-------------------
----- Ver 2.0 -----

local function find_feeder(self, feeder)
    local pos = self.object:get_pos()
    local pos1 = {x = pos.x + 32, y = pos.y + 32, z = pos.z + 32}
    local pos2 = {x = pos.x - 32, y = pos.y - 32, z = pos.z - 32}
    local area = minetest.find_nodes_in_area(pos1, pos2, feeder)
    if #area < 1 then return false end
    return true
end

local random = math.random

local creative = minetest.settings:get_bool("creative_mode")

function paleotest.particle_spawner(pos, texture, type)
	type = type or "float"
	if type == "float" then
		minetest.add_particlespawner({
			amount = 16,
			time = 0.25,
			minpos = {
				x = pos.x - 2,
				y = pos.y - 2,
				z = pos.z - 2,
			},
			maxpos = {
				x = pos.x + 2,
				y = pos.y + 2,
				z = pos.z + 2,
			},
			minacc = {x = 0, y = 0.25, z = 0},
			maxacc = {x = 0, y = -0.25, z = 0},
			minexptime = 0.75,
			maxexptime = 1,
			minsize = 4,
			maxsize = 4,
			texture = texture,
			glow = 1,
		})
	elseif type == "splash" then
		minetest.add_particlespawner({
			amount = 6,
			time = 0.25,
			minpos = {x = pos.x - 7/16, y = pos.y + 0.6, z = pos.z - 7/16},
			maxpos = {x = pos.x + 7/16, y = pos.y + 0.6, z = pos.z + 7/16},
			minvel = vector.new(-1, 2, -1),
			maxvel = vector.new(1, 5, 1),
			minacc = vector.new(0, -9.81, 0),
			maxacc = vector.new(0, -9.81, 0),
			minsize = 2,
			maxsize = 4,
			collisiondetection = true,
			texture = texture,
		})
	end
end

-----------------
-- On Activate --
-----------------

function paleotest.on_activate(self, staticdata, dtime_s)
	mob_core.on_activate(self, staticdata, dtime_s)
	self.is_in_shallow = false
	self.mood = mobkit.recall(self, "mood") or 75
	self.hunger = mobkit.recall(self, "hunger") or self.max_hunger/2
	self.sleep_timer = mobkit.recall(self, "sleep_timer") or 1
	self.order = mobkit.recall(self, "order") or "wander"
	self.attacks = mobkit.recall(self, "attacks") or "nothing"
	self.feeder_timer = mobkit.recall(self, "feeder_timer") or 32
	self.imprinter = mobkit.recall(self, "imprinter") or nil
	self.imprint_cooldown = mobkit.recall(self, "imprint_cooldown") or 1200
	self.imprint_exp_timer = mobkit.recall(self, "imprint_exp_timer") or 1200
	self.imprint_level = mobkit.recall(self, "imprint_level") or 0
	self.can_imprint = mobkit.recall(self, "can_imprint") or false
end


-------------
-- On Step --
-------------

function paleotest.on_step(self, dtime, moveresult)
	mobkit.stepfunc(self, dtime)
	self.moveresult = moveresult
	if not mobkit.is_alive(self) then return end
	mob_core.collision_detection(self)
	-- Vitals and other Functions
	if mobkit.timer(self, 1) then -- 1 Second Timer
		mob_core.vitals(self)
		mob_core.random_sound(self)
		mob_core.growth(self)
		paleotest.breed(self, self.live_birth)
		if self.sleep_timer > 0 then
			self.sleep_timer = mobkit.remember(self, "sleep_timer", self.sleep_timer-1)
		end
		if self.imprint_tame
		and not self.tamed
		or (self.growth_stage == 4
		and self.imprint_exp_timer > 0) then
			if self.imprint_cooldown >= 0 then
				self.imprint_cooldown = self.imprint_cooldown - 1
				self.can_imprint = false
			else
				self.imprint_exp_timer = self.imprint_exp_timer - 1
			end
			if self.imprint_cooldown <= 0 then
				self.can_imprint = true
			end
			if self.imprint_exp_timer <= 0 then
				self.can_imprint = false
				self.imprint_level = 0
			end
			mobkit.remember(self, "imprint_cooldown", self.imprint_cooldown)
			mobkit.remember(self, "imprint_exp_timer", self.imprint_exp_timer)
			mobkit.remember(self, "imprint_level", self.imprint_level)
			mobkit.remember(self, "can_imprint", self.can_imprint)
		end
	end
	-- Keep track of hunger and mood
	if self.needs_enrichment then
		if self.mood > 100 then
			self.mood = 100
		end
		if mobkit.timer(self, 60) then
			if self.status == "sleeping"
			and self.hunger > self.max_hunger/3
			and self.mood < 100 then
				self.mood = self.mood + 1
			end
		end
	end
	if self.hunger > self.max_hunger then
		self.hunger = self.hunger - 1
	end
	if mobkit.timer(self, 120) then
		self.hunger = self.hunger - 1
	end
	if mobkit.timer(self, 1) then
		if self.feeder_timer > 0 then
			self.feeder_timer = self.feeder_timer - 1
		elseif self.feeder_timer <= 0 then
			self.feeder_timer = mobkit.remember(self, "feeder_timer", random(16, 64))
		end
		if self.hunger < self.max_hunger/3 then
			if self.needs_enrichment then
				if self.mood > 0 then
					self.mood = self.mood - 1
				else
					self.mood = 0
				end
			end
			if self.hp >= self.max_hp/4 then
				mobkit.hurt(self, 1)
			end
		end
	end
	if mobkit.recall(self, "hunger") ~= self.hunger then
		mobkit.remember(self, "hunger", self.hunger)
	end
	if mobkit.recall(self, "mood") ~= self.mood then
		mobkit.remember(self, "mood", self.mood)
	end
	
	-- Check if in shallow water
	local pos = mobkit.get_stand_pos(self)
	local half_height = vector.new(pos.x, pos.y + self.height/2, pos.z)
	local height = vector.new(pos.x, pos.y + self.height, pos.z)
	if self.isinliquid
	and minetest.get_node(half_height) ~= self.isinliquid then
		self.is_in_shallow = true
		self.is_in_deep = true
	elseif self.isinliquid
	and minetest.get_node(height) == self.isinliquid then
		self.is_in_shallow = false
		self.is_in_deep = true
	else
		self.is_in_shallow = false
		self.is_in_deep = false
	end
end

local function hitbox(ent)
	if type(ent) == 'userdata' then
		return  ent:get_properties().collisionbox
	end
    return ent.object:get_properties().collisionbox
end

function paleotest.feed_tame(self, clicker, feed_count, tame, breed)
	local item = clicker:get_wielded_item()
	local pos = self.object:get_pos()
	local mob_name = mob_core.get_name_proper(self.name)
	if mob_core.follow_holding(self, clicker) then
		if creative == false then
			item:take_item()
			clicker:set_wielded_item(item)
		end
		mobkit.heal(self, self.max_hp/feed_count)
		if self.hp >= self.max_hp then
			self.hp = self.max_hp
		end
		if self.hunger < self.max_hunger then
			self.food = mobkit.remember(self, "food", self.food + 1)
			if self.hunger < self.max_hunger then
				self.hunger = mobkit.remember(self,"hunger",self.hunger+5)
			elseif self.hunger >= self.max_hunger then
				self.hunger = mobkit.remember(self,"hunger",self.max_hunger)
			end
			local minppos = vector.add(pos, hitbox(self)[4])
            local maxppos = vector.subtract(pos, hitbox(self)[4])
            local def = minetest.registered_items[item:get_name()]
            local texture = def.inventory_image
            if not texture or texture == "" then
				texture = def.wield_image
				if def.tiles then
					texture = def.tiles[1]
				end
            end
            minetest.add_particlespawner({
                amount = 12*self.height,
                time = 0.1,
                minpos = minppos,
                maxpos = maxppos,
                minvel = {x=-1, y=1, z=-1},
                maxvel = {x=1, y=2, z=1},
                minacc = {x=0, y=-5, z=0},
                maxacc = {x=0, y=-9, z=0},
                minexptime = 1,
                maxexptime = 1,
                minsize = 2*self.height,
                maxsize = 3*self.height,
                collisiondetection = true,
                vertical = false,
                texture = texture,
            })
			if self.food >= feed_count then
				self.food = mobkit.remember(self, "food", 0)
				if tame and not self.tamed then
					mob_core.set_owner(self, clicker:get_player_name())
					minetest.chat_send_player(clicker:get_player_name(), mob_name.." has been tamed!")
					mobkit.clear_queue_high(self)
					paleotest.particle_spawner(pos, "mob_core_green_particle.png", "float")
				end
				if breed then
					if self.child then return false end
					if self.breed_mode then return false end
					if self.breed_timer == 0 and self.breed_mode == false then
						self.breed_mode = true
						paleotest.particle_spawner(pos, "heart.png", "float")
					end
				end
			end
		end
	end
	return false
end

------------
-- Basics --
------------

function paleotest.can_find_ball(self)
	local objects = minetest.get_objects_inside_radius(self.object:get_pos(), self.view_range)
	for _, object in ipairs(objects) do
		if object:get_luaentity() then
			local ent = object:get_luaentity()
			if ent.name
			and ent.name == "hades_paleotest:pursuit_ball_ent" then
				return true
			end
		end
	end
	return false
end

function paleotest.can_find_post(self)
	local pos = self.object:get_pos()
	local pos1 = vector.new(pos.x - self.view_range, pos.y - self.view_range, pos.z - self.view_range)
	local pos2 = vector.new(pos.x + self.view_range, pos.y + self.view_range, pos.z + self.view_range)
	local nodes = minetest.find_nodes_in_area(pos1, pos2, "hades_paleotest:scratching_post")
	if #nodes >= 1 then return true end
	return false
end

function paleotest.on_punch(self)
	if self.mood > 0 then
		self.mood = self.mood - 5
	elseif self.mood <= 0 then
		self.mood = 0
	end
	mobkit.remember(self, "mood", self.mood)
	if self.status == "sleeping" then
		self.status = mobkit.remember(self, "status", "")
		self.sleep_timer = mobkit.remember(self, "sleep_timer", self.sleep_timer+30)
	end
end

-----------------------
-- Tamed Mob Control --
-----------------------

local function set_order(self,player,order)
	if order == "stand" then
		if self.isinliquid then return end
		if self.status == "sleeping" then
			self.object:settexturemod("")
		end
		mobkit.clear_queue_high(self)
		mobkit.clear_queue_low(self)
		self.object:set_velocity({ x = 0, y = 0, z = 0 })
		self.object:set_acceleration({ x = 0, y = 0, z = 0 })
		self.status = mobkit.remember(self, "status", "stand")
		self.order = mobkit.remember(self, "order", "stand")
		mobkit.animate(self,"stand")
	end
	if order == "wander" then
		mobkit.clear_queue_high(self)
		mobkit.clear_queue_low(self)
		self.status = mobkit.remember(self, "status", "")
		self.order = mobkit.remember(self, "order", "wander")
	end
	if order == "follow" then
		if self.status == "sleeping" then
			self.object:settexturemod("")
		end
		mobkit.clear_queue_low(self)
		self.status = mobkit.remember(self, "status", "following")
		self.order = mobkit.remember(self, "order", "follow")
		if self.aerial_follow then
			paleotest.hq_aerial_follow(self, 20, player)
		else
			mobkit.hq_follow(self, 20, player)
		end
	end
end

function paleotest.set_order(self,player)
	local name = player:get_player_name()
	local mob_name = mob_core.get_name_proper(self.name)
	if self.owner and self.owner == name and player:get_player_control().sneak == true then
		if self.order == "follow" then
			set_order(self,player,"stand")
			minetest.chat_send_player(name, (""..mob_name.." is standing."))
		elseif self.order == "stand" then
			set_order(self,player,"wander")
			minetest.chat_send_player(name, (""..mob_name.." is wandering."))
		elseif self.order == "wander" then
			set_order(self,player,"follow")
			minetest.chat_send_player(name, (""..mob_name.." is following."))
		end
	end
end

function paleotest.set_attack(self, player)
	local name = player:get_player_name()
	if self.owner and self.owner == name then
		if self.attacks == "nothing" then
			self.attacks = mobkit.remember(self,"attacks","mobs")
			minetest.chat_send_player(name, ("Attacks: Mobs"))
		elseif self.attacks == "mobs" then
			self.attacks = mobkit.remember(self,"attacks","players")
			minetest.chat_send_player(name, ("Attacks: Players"))
		elseif self.attacks == "players" then
		    self.attacks = mobkit.remember(self,"attacks","all")
			minetest.chat_send_player(name, ("Attacks: All"))
		else
			self.attacks = mobkit.remember(self,"attacks","nothing")
			minetest.chat_send_player(name, ("Attacks: Nothing"))
		end
	end
end

--------------
-- Breeding --
--------------

local breeding = minetest.settings:get_bool("breeding")

function paleotest.breed(self, live) -- Breeding
	if not breeding then return end
    if self.breed_timer > 0 then
		self.breed_timer = mobkit.remember(self,"breed_timer",self.breed_timer - 1)
    else
        self.breed_timer = mobkit.remember(self,"breed_timer",0)
	end
	local name = self.name:split(":")[2]
	if self.gender == "female" then
		local pos = self.object:get_pos()
		local objs = minetest.get_objects_inside_radius(pos, self.collisionbox[4]*4)
		for _,obj in ipairs(objs) do
			local luaent = obj:get_luaentity()
			if (obj and obj ~= self.object)
			and (luaent and luaent.name == self.name)
			and (self.breed_timer and luaent.breed_timer)
			and (self.breed_timer <= 0 and luaent.breed_timer <= 0)
			and (self.owner == luaent.owner) then
				self.breed_timer = mobkit.remember(self,"breed_timer",math.random(600,1200))
				luaent.breed_timer = mobkit.remember(luaent,"breed_timer",math.random(600,1200))
				minetest.after(2.5,function()
					if live then
						mob_core.spawn_child(pos,self.name)
					else
						minetest.add_entity(pos,"hades_paleotest:egg_"..name.."_ent")
					end
					paleotest.particle_spawner(self.object:get_pos(), "heart.png", "float")
				end)
			end
		end
	end
end

function paleotest.imprint_tame(self, clicker)
	if not self.imprinter then
		self.imprinter = mobkit.remember(self, "imprinter", clicker:get_player_name())
	end
	local name = clicker:get_player_name()
	if name ~= self.imprinter
	or self.tamed
	or not self.can_imprint
	or (self.growth_stage == 4
	and not self.can_imprint) then
		return
	end
	local mob_name = mob_core.get_name_proper(self.name)
	if self.can_imprint then
		paleotest.particle_spawner(self.object:get_pos(), "heart.png", "float")
		local level = self.imprint_level
		if not level then
			self.imprint_level = 1
		else
			self.imprint_level = self.imprint_level + 1
		end
		if level == 4 then
			minetest.chat_send_player(name, mob_name .. " has been tamed")
			mob_core.set_owner(self, clicker:get_player_name())
		else
			minetest.chat_send_player(name, "Imprinting has been raised, come back in 20 minutes.")
		end
		self.can_imprint = false
		self.imprint_cooldown = 1200
		self.imprint_exp_timer = 1200
	end
	mobkit.remember(self, "imprint_level", self.imprint_level)
	mobkit.remember(self, "can_imprint", self.can_imprint)
end

--------------------
-- Block Breaking --
--------------------

local block_breaking = minetest.settings:get_bool("block_breaking")

local function can_break(pos)
	local node = minetest.get_node_or_nil(pos)
	if node
	and not minetest.registered_nodes[node.name].groups.stone
	and not minetest.registered_nodes[node.name].groups.level
	and not minetest.registered_nodes[node.name].groups.unbreakable
	and not minetest.registered_nodes[node.name].groups.liquid then
		return true
	end
end

function paleotest.block_breaking(self)
	if not block_breaking then
		return
	end
	local width = self.object:get_properties().collisionbox[4] + 0.25
	local height = self.height + 0.25
	local pos = mobkit.get_stand_pos(self)
	local pos1 = {x = pos.x + width, y = pos.y + height, z = pos.z + width}
	local pos2 = {x = pos.x - width, y = pos.y, z = pos.z - width}
	local area = minetest.find_nodes_in_area(pos1, pos2, paleotest.global_walkable)
	if #area < 1 then return end
	for i = #area, 1, -1 do
		local name = minetest.get_node(area[i]).name
		if minetest.registered_nodes[name].groups.soil
		and area[i].y < pos.y + 1.1 then -- This helps prevent terrain destruction
			table.remove(area, i)
		else
			local yaw = self.object:get_yaw()
			local yaw_to_node = minetest.dir_to_yaw(vector.direction(pos, area[i]))
			if math.abs(yaw - yaw_to_node) <= 1
			and can_break(area[i])
			and not minetest.is_protected(area[i], "") then
				if random(1, 4) == 1 then
					minetest.dig_node(area[i])
				else
					minetest.dig_node(area[i])
				end
			end
		end
	end
end
