local FISH = {}

FISH.Model = Model("models/props/de_inferno/goldfish.mdl")
FISH.ClassName = "base_swimming"
FISH.Forward = Vector(1, 0, 0)
FISH.BuoyancyRatio = 0
FISH.PositionDamping = 0
FISH.AnglesDamping = 0
FISH.IdleZMult = 1

FISH.BaitDamage = 10

FISH.MaxSpawned = 30
FISH.Rareness = 0

function FISH:SetupDataTables()
	self:DTVar("Bool", 0, "dead")
	if self.OnSetupDataTables then
		self:OnSetupDataTables()
	end
end

if CLIENT then
	function FISH:Initialize()
		self:PostInit()
	end

	function FISH:Draw()
		if self:WaterLevel() > 0 and not self.dt.dead then
			local ent = self

			-- ummm
			local hook = self:GetParent()
			if hook:IsValid() and hook:GetModel() == "models/props_junk/meathook001a.mdl" then
				ent = self:GetParent()
			end

			local ang = ent:GetVelocity():AngleEx(self.Forward)
			ang.r = 0
			ang.p = self.DrawPitch or ang.p

			if self:GetVelocity():Length() > 50 then
				self:SetRenderAngles(ang)
			end
		else
			self:SetRenderAngles()
		end

		self:DrawModel()
	end
end

if SERVER then

	function FISH:OnDeath() self:DelayRemove(10) end
	function FISH:OnRevive() end
	function FISH:OnEnterWater() end
	function FISH:OnExitWater() end
	function FISH:PreMove(phys) end
	function FISH:OnThink() end
	function FISH:PostInit() end

	function FISH:Initialize()
		fishing.RegisteredFish["base"].Initialize(self)

		self:StartMotionController()
		self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE_DEBRIS)
		self:Revive()

		self:SetSize(self.rand_size)
		self.BaitDamage = 10 * self.rand_size
	end

	do -- behavior
		function FISH:SetMoveMode(type, ...)
			self.behavior = {func = "Move" .. type, args = {...}}
		end

		function FISH:CalcMoveModes()
			local phys = self:GetPhysicsObject()

			if self:PreMove(phys) == false then return end

			if not self.behavior then
				self:SetMoveMode("Idle")
			end

			local hook = self:GetParent()
			
			if hook.IsFishingHook and hook:WaterLevel() ~= 0 then
				self:MoveHook(hook:GetPhysicsObject(), hook.rod.Owner)
			elseif self[self.behavior.func] then
				if self[self.behavior.func](self, phys, unpack(self.behavior.args)) == true then
					self:SetMoveMode("Idle")
				end
			end
		end

		-- default
		do
			function FISH:MoveIdle(phys)
				local t = CurTime()

				if (self.last_idle_swim or 0) < t then
					self.idle_swim_dir = VectorRand() * Vector(0,0,self.IdleZMult)
					self.last_idle_swim = t + (math.random() * 3)
				end

				self.smooth_dir = self.smooth_dir or Vector()
				self.smooth_dir = self.smooth_dir + ((self.idle_swim_dir - self.smooth_dir) * FrameTime() * 50)

				phys:AddVelocity(self.smooth_dir)
				phys:AddVelocity(phys:GetVelocity() * -0.01)

				for key, ent in pairs(ents.FindInSphere(self:GetPos(), 200)) do

					if ent.IsFishingBait then
						local chance = not ent.fishing_rand and 100 or (ent.fishing_rand + self.fishing_rand)
						chance = chance + ent:GetVelocity():Length() / 100

						if chance > 1 then
							self:SetMoveMode("AttemptBite", ent)
							return
						end
					end
				end
			end

			function FISH:MoveAttemptBite(phys, bait)
				if not bait:IsValid() then return true end

				local dir = bait:GetPos() - self:GetPos()

				phys:AddVelocity(dir:GetNormalized() * phys:GetMass() * (self:WaterLevel() > 0 and 1 or 0.01) * 0.1)
				if self:WaterLevel() > 0 then phys:AddVelocity(phys:GetVelocity() * -0.1) end

				if dir:Length() < bait:BoundingRadius() * 1.25 then
					
					bait:DrainHealth(self)
					
					return true
				end
			end

			function FISH:MoveFlee(phys)

			end

			function FISH:MoveHook(phys, ply)
				local eye = ply:EyePos()
				local pos = phys:GetPos()

				eye.z = Lerp(0.1, pos.z, eye.z)

				local t = CurTime()
				if (self.last_idle_swim or 0) < t then
					self.idle_swim_dir = Vector(math.Rand(-1,1), math.Rand(-1,1), math.Rand(-2, -1) * self.IdleZMult)
					self.last_idle_swim = t + math.random()
				end

				self.smooth_dir = self.smooth_dir or Vector()
				self.smooth_dir = self.smooth_dir + (((self.idle_swim_dir + (phys:GetPos() - ply:EyePos())) - self.smooth_dir) * FrameTime() * 50)

				phys:AddVelocity(self.smooth_dir:GetNormalized() * self:GetPhysicsObject():GetMass() * self:GetSize() * 1.25)
			end
		end
	end

	function FISH:OnEnterWater()
		local data = EffectData()
			data:SetOrigin(self:GetPos())
			data:SetScale(self:GetSize())
		util.Effect("WaterRipple", data)
	end

	function FISH:Think()
		self:PhysWake()
		return self:OnThink()
	end

	function FISH:PhysicsSimulate(phys)
	
		if self.dt.dead then
			if self:WaterLevel() == 0 then
				phys:AddVelocity(physenv.GetGravity():GetNormalized() *-0.1)
				phys:SetBuoyancyRatio(1)

				phys:AddVelocity(phys:GetVelocity() * - 0.1)
				phys:AddAngleVelocity(phys:GetAngleVelocity() * - 0.1)
			end

			phys:EnableGravity(true)
		else
			self:CalcMoveModes()

			if self:WaterLevel() == 0 then
				if self.last_inwater then
					self:OnExitWater()
					self.last_inwater = false
				end

				phys:EnableGravity(true)
			else
				if not self.last_inwater then
					self:OnEnterWater()
					self.last_inwater = true
				end

				phys:EnableGravity(false)
			end

			phys:SetBuoyancyRatio(self.BuoyancyRatio)

			phys:AddVelocity(phys:GetVelocity() * -self.PositionDamping)
			phys:AddAngleVelocity(phys:GetAngleVelocity() * -self.AnglesDamping)
		end
	end

	function FISH:OnTakeDamage(dmg)
		self:TakePhysicsDamage(dmg)

		self:Kill()
	end

	function FISH:Revive()
		if self.dt.dead then
			self:OnRevive()
			self.dt.dead = false
		end
	end

	function FISH:Kill()
		if not self.dt.dead then
			self:OnDeath()
			self.dt.dead = true
		end
	end
end

fishing.RegisterFish(FISH)