AddCSLuaFile()

ENT.Type = "anim"

ENT.ExplosionEffect = "lvs_explosion_bomb"

ENT.lvsProjectile = true
ENT.VJ_ID_Danger = true

function ENT:SetupDataTables()
	self:NetworkVar( "Bool", 0, "Active" )
	self:NetworkVar( "Bool", 1, "MaskSolid" )

	self:NetworkVar( "Vector", 0, "Speed" )
end

if SERVER then
	util.AddNetworkString( "lvs_bomb_hud" )

	function ENT:SetEntityFilter( filter )
		if not istable( filter ) then return end

		self._FilterEnts = {}

		for _, ent in pairs( filter ) do
			self._FilterEnts[ ent ] = true
		end
	end
	function ENT:GetEntityFilter()
		return self._FilterEnts or {}
	end
	function ENT:SetDamage( num ) self._dmg = num end
	function ENT:SetForce( num ) self._force = num end
	function ENT:SetThrust( num ) self._thrust = num end
	function ENT:SetRadius( num ) self._radius = num end
	function ENT:SetAttacker( ent )
		self._attacker = ent

		if not IsValid( ent ) or not ent:IsPlayer() then return end

		net.Start( "lvs_bomb_hud", true )
			net.WriteEntity( self )
		net.Send( ent )
	end

	function ENT:GetAttacker() return self._attacker or NULL end
	function ENT:GetDamage() return (self._dmg or 2000) end
	function ENT:GetForce() return (self._force or 8000) end
	function ENT:GetRadius() return (self._radius or 400) end

	function ENT:Initialize()
		self:SetModel( "models/props_phx/ww2bomb.mdl" )
		self:SetMoveType( MOVETYPE_NONE )
	end

	function ENT:OnTakeDamage( dmginfo )
	end

	function ENT:Enable()
		if self.IsEnabled then return end

		local Parent = self:GetParent()

		if IsValid( Parent ) then
			self:SetOwner( Parent )
			self:SetParent( NULL )
		end

		self:PhysicsInit( SOLID_VPHYSICS )
		self:SetMoveType( MOVETYPE_VPHYSICS )
		self:SetSolid( SOLID_VPHYSICS )
		self:SetCollisionGroup( COLLISION_GROUP_WORLD )
		self:PhysWake()

		timer.Simple(1, function()
			if not IsValid( self ) then return end

			self:SetCollisionGroup( COLLISION_GROUP_NONE )
		end )

		self.IsEnabled = true

		local pObj = self:GetPhysicsObject()
		
		if not IsValid( pObj ) then
			self:Remove()

			print("LVS: missing model. Missile terminated.")

			return
		end

		pObj:SetMass( 500 ) 
		pObj:EnableGravity( false ) 
		pObj:EnableMotion( true )
		pObj:EnableDrag( false )
		pObj:SetVelocityInstantaneous( self:GetSpeed() )

		self:SetTrigger( true )

		self:StartMotionController()

		self:PhysWake()

		self.SpawnTime = CurTime()

		self:SetActive( true )
	end

	function ENT:PhysicsSimulate( phys, deltatime )
		phys:Wake()

		local ForceLinear = physenv.GetGravity()

		local Pos = self:GetPos()
		local TargetPos = Pos + self:GetVelocity()

		local AngForce = -self:WorldToLocalAngles( (TargetPos - Pos):Angle() )

		local ForceAngle = (Vector(AngForce.r,-AngForce.p,-AngForce.y) * 10 - phys:GetAngleVelocity() * 5 ) * 250 * deltatime

		return ForceAngle, ForceLinear, SIM_GLOBAL_ACCELERATION
	end

	function ENT:Think()	
		local T = CurTime()

		self:NextThink( T )

		self:UpdateTrajectory()

		if not self.SpawnTime then return true end

		if (self.SpawnTime + 12) < T then
			self:Detonate()
		end

		return true
	end

	function ENT:UpdateTrajectory()
		local base = self:GetParent()

		if not IsValid( base ) then return end

		self:SetSpeed( base:GetVelocity() )
	end

	ENT.IgnoreCollisionGroup = {
		[COLLISION_GROUP_NONE] = true,
		[COLLISION_GROUP_WORLD] =  true,
		[COLLISION_GROUP_IN_VEHICLE] = true
	}

	function ENT:StartTouch( entity )
		if entity == self:GetAttacker() then return end

		if istable( self._FilterEnts ) and self._FilterEnts[ entity ] then return end

		if entity.GetCollisionGroup and self.IgnoreCollisionGroup[ entity:GetCollisionGroup() ] then return end

		if entity.lvsProjectile then return end

		self:Detonate( entity )
	end

	function ENT:EndTouch( entity )
	end

	function ENT:Touch( entity )
	end

	function ENT:PhysicsCollide( data )
		if istable( self._FilterEnts ) and self._FilterEnts[ data.HitEntity ] then return end

		self:Detonate( data.HitEntity )
	end

	function ENT:OnTakeDamage( dmginfo )	
	end

	function ENT:Detonate( target )
		if not self.IsEnabled or self.IsDetonated then return end

		self.IsDetonated = true

		local Pos =  self:GetPos() 

		local effectdata = EffectData()
			effectdata:SetOrigin( Pos )
		util.Effect( self.ExplosionEffect, effectdata, true, true )

		local attacker = self:GetAttacker()

		LVS:BlastDamage( Pos, self:GetForward(), IsValid( attacker ) and attacker or game.GetWorld(), self, self:GetDamage(), DMG_BLAST, self:GetRadius(), self:GetForce() )

		SafeRemoveEntityDelayed( self, FrameTime() )
	end

	return
end

function ENT:Enable()
	if self.IsEnabled then return end

	self.IsEnabled = true

	self.snd = CreateSound(self, "lvs/weapons/bomb_whistle_loop.wav")
	self.snd:SetSoundLevel( 110 )
	self.snd:PlayEx(0,150)
end

function ENT:CalcDoppler()
	local Ent = LocalPlayer()

	local ViewEnt = Ent:GetViewEntity()

	if Ent:lvsGetVehicle() == self then
		if ViewEnt == Ent then
			Ent = self
		else
			Ent = ViewEnt
		end
	else
		Ent = ViewEnt
	end

	local sVel = self:GetVelocity()
	local oVel = Ent:GetVelocity()

	local SubVel = oVel - sVel
	local SubPos = self:GetPos() - Ent:GetPos()

	local DirPos = SubPos:GetNormalized()
	local DirVel = SubVel:GetNormalized()

	local A = math.acos( math.Clamp( DirVel:Dot( DirPos ) ,-1,1) )

	return (1 + math.cos( A ) * SubVel:Length() / 13503.9)
end

function ENT:Think()
	if self.snd then
		self.snd:ChangePitch( 100 * self:CalcDoppler(), 1 )
		self.snd:ChangeVolume(math.Clamp(-(self:GetVelocity().z + 1000) / 3000,0,1), 2)
	end

	if self.IsEnabled then return end

	if self:GetActive() then
		self:Enable()
	end
end

function ENT:Draw()
	local T = CurTime()

	if not self:GetActive() then
		self._PreventDrawTime = T + 0.1
		return
	end

	if (self._PreventDrawTime or 0) > T then return end

	self:DrawModel()
end

function ENT:SoundStop()
	if self.snd then
		self.snd:Stop()
	end
end

function ENT:OnRemove()
	self:SoundStop()
end

local color_red = Color(255,0,0,255)
local color_red_blocked = Color(100,0,0,255)
local HudTargets = {}
hook.Add( "HUDPaint", "!!!!lvs_bomb_hud", function()
	for ID, _ in pairs( HudTargets ) do
		local Missile = Entity( ID )

		if not IsValid( Missile ) or Missile:GetActive() then
			HudTargets[ ID ] = nil

			continue
		end

		local Grav = physenv.GetGravity()
		local FT = 0.05
		local MissilePos = Missile:GetPos()
		local Pos = MissilePos
		local Vel = Missile:GetSpeed()

		local LastColor = color_red
		local Mask = Missile.GetMaskSolid and (Missile:GetMaskSolid() and MASK_SOLID or MASK_SOLID_BRUSHONLY) or MASK_SOLID_BRUSHONLY

		cam.Start3D()
		local Iteration = 0
		while Iteration < 1000 do
			Iteration = Iteration + 1

			Vel = Vel + Grav * FT

			local StartPos = Pos
			local EndPos = Pos + Vel * FT

			local trace = util.TraceLine( {
				start = StartPos,
				endpos = EndPos,
				mask = Mask,
			} )

			local traceVisible = util.TraceLine( {
				start = MissilePos,
				endpos = StartPos,
				mask = Mask,
			} )

			LastColor = traceVisible.Hit and color_red_blocked or color_red

			render.DrawLine( StartPos, EndPos, LastColor )

			Pos = EndPos

			if trace.Hit then
				break
			end
		end
		cam.End3D()

		local TargetPos = Pos:ToScreen()

		if not TargetPos.visible then continue end

		surface.DrawCircle( TargetPos.x, TargetPos.y, 20, LastColor )
	end
end )

net.Receive( "lvs_bomb_hud", function( len )
	local ent = net.ReadEntity()

	if not IsValid( ent ) then return end

	HudTargets[ ent:EntIndex() ] = true
end )
