
TOOL.Category = "Construction"
TOOL.Name = "#tool.thruster.name"

TOOL.ClientConVar[ "force" ] = "1500"
TOOL.ClientConVar[ "model" ] = "models/props_phx2/garbage_metalcan001a.mdl"
TOOL.ClientConVar[ "keygroup" ] = "45"
TOOL.ClientConVar[ "keygroup_back" ] = "42"
TOOL.ClientConVar[ "toggle" ] = "0"
TOOL.ClientConVar[ "collision" ] = "0"
TOOL.ClientConVar[ "effect" ] = "fire"
TOOL.ClientConVar[ "damageable" ] = "0"
TOOL.ClientConVar[ "soundname" ] = "PhysicsCannister.ThrusterLoop"

TOOL.Information = { { name = "left" } }

cleanup.Register( "thrusters" )

local function IsValidThrusterModel( model )
	for mdl, _ in pairs( list.Get( "ThrusterModels" ) ) do
		if ( mdl:lower() == model:lower() ) then return true end
	end
	return false
end

function TOOL:LeftClick( trace )

	if ( trace.Entity && trace.Entity:IsPlayer() ) then return false end

	-- If there's no physics object then we can't constraint it!
	if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end

	if ( CLIENT ) then return true end

	local ply = self:GetOwner()

	local force = math.Clamp( self:GetClientNumber( "force" ), 0, 1E10 )
	local model = self:GetClientInfo( "model" )
	local key = self:GetClientNumber( "keygroup" )
	local key_bk = self:GetClientNumber( "keygroup_back" )
	local toggle = self:GetClientNumber( "toggle" )
	local collision = self:GetClientNumber( "collision" ) == 0
	local effect = self:GetClientInfo( "effect" )
	local damageable = self:GetClientNumber( "damageable" )
	local soundname = self:GetClientInfo( "soundname" )

	-- If we shot a thruster change its force
	if ( IsValid( trace.Entity ) && trace.Entity:GetClass() == "gmod_thruster" && trace.Entity.pl == ply ) then

		trace.Entity:SetForce( force )
		trace.Entity:SetEffect( effect )
		trace.Entity:SetToggle( toggle == 1 )
		trace.Entity.ActivateOnDamage = damageable == 1
		trace.Entity:SetSound( soundname )

		numpad.Remove( trace.Entity.NumDown )
		numpad.Remove( trace.Entity.NumUp )
		numpad.Remove( trace.Entity.NumBackDown )
		numpad.Remove( trace.Entity.NumBackUp )

		trace.Entity.NumDown = numpad.OnDown( ply, key, "Thruster_On", trace.Entity, 1 )
		trace.Entity.NumUp = numpad.OnUp( ply, key, "Thruster_Off", trace.Entity, 1 )

		trace.Entity.NumBackDown = numpad.OnDown( ply, key_bk, "Thruster_On", trace.Entity, -1 )
		trace.Entity.NumBackUp = numpad.OnUp( ply, key_bk, "Thruster_Off", trace.Entity, -1 )

		trace.Entity.key = key
		trace.Entity.key_bk = key_bk
		trace.Entity.force = force
		trace.Entity.toggle = toggle
		trace.Entity.effect = effect
		trace.Entity.damageable = damageable

		return true
	end

	if ( !util.IsValidModel( model ) || !util.IsValidProp( model ) || !IsValidThrusterModel( model ) ) then return false end
	if ( !self:GetWeapon():CheckLimit( "thrusters" ) ) then return false end

	local Ang = trace.HitNormal:Angle()
	Ang.pitch = Ang.pitch + 90

	local thruster = MakeThruster( ply, model, Ang, trace.HitPos, key, key_bk, force, toggle, effect, damageable, soundname )
	if ( !IsValid( thruster ) ) then return false end

	local min = thruster:OBBMins()
	thruster:SetPos( trace.HitPos - trace.HitNormal * min.z )

	undo.Create( "gmod_thruster" )
		undo.AddEntity( thruster )

		-- Don't weld to world
		if ( IsValid( trace.Entity ) ) then

			local weld = constraint.Weld( thruster, trace.Entity, 0, trace.PhysicsBone, 0, collision, true )
			if ( IsValid( weld ) ) then
				ply:AddCleanup( "thrusters", weld )
				undo.AddEntity( weld )
			end

			-- Don't disable collision if it's not attached to anything
			if ( collision ) then

				if ( IsValid( thruster:GetPhysicsObject() ) ) then thruster:GetPhysicsObject():EnableCollisions( false ) end
				thruster:SetCollisionGroup( COLLISION_GROUP_WORLD )
				thruster.nocollide = true

			end

		end

		undo.SetPlayer( ply )
	undo.Finish()

	return true

end

if ( SERVER ) then

	function MakeThruster( ply, model, ang, pos, key, key_bck, force, toggle, effect, damageable, soundname, nocollide, Data )

		if ( IsValid( ply ) && !ply:CheckLimit( "thrusters" ) ) then return NULL end
		if ( !IsValidThrusterModel( model ) ) then return NULL end

		local thruster = ents.Create( "gmod_thruster" )
		if ( !IsValid( thruster ) ) then return NULL end

		duplicator.DoGeneric( thruster, Data )
		thruster:SetModel( model ) -- Backwards compatible for addons directly calling this function
		thruster:SetAngles( ang )
		thruster:SetPos( pos )
		thruster:Spawn()

		DoPropSpawnedEffect( thruster )

		duplicator.DoGenericPhysics( thruster, ply, Data )

		force = math.Clamp( force, 0, 1E10 )

		thruster:SetEffect( effect )
		thruster:SetForce( force )
		thruster:SetToggle( toggle == 1 )
		thruster.ActivateOnDamage = ( damageable == 1 )
		thruster:SetPlayer( ply )
		thruster:SetSound( soundname )

		thruster.NumDown = numpad.OnDown( ply, key, "Thruster_On", thruster, 1 )
		thruster.NumUp = numpad.OnUp( ply, key, "Thruster_Off", thruster, 1 )

		thruster.NumBackDown = numpad.OnDown( ply, key_bck, "Thruster_On", thruster, -1 )
		thruster.NumBackUp = numpad.OnUp( ply, key_bck, "Thruster_Off", thruster, -1 )

		if ( nocollide == true ) then
			if ( IsValid( thruster:GetPhysicsObject() ) ) then thruster:GetPhysicsObject():EnableCollisions( false ) end
			thruster:SetCollisionGroup( COLLISION_GROUP_WORLD )
		end

		table.Merge( thruster:GetTable(), {
			key = key,
			key_bck = key_bck,
			force = force,
			toggle = toggle,
			pl = ply,
			effect = effect,
			nocollide = nocollide,
			damageable = damageable,
			soundname = soundname
		} )

		if ( IsValid( ply ) ) then
			ply:AddCount( "thrusters", thruster )
			ply:AddCleanup( "thrusters", thruster )
		end


		return thruster

	end

	duplicator.RegisterEntityClass( "gmod_thruster", MakeThruster, "Model", "Ang", "Pos", "key", "key_bck", "force", "toggle", "effect", "damageable", "soundname", "nocollide", "Data" )

end

function TOOL:UpdateGhostThruster( ent, ply )

	if ( !IsValid( ent ) ) then return end

	local trace = ply:GetEyeTrace()
	if ( !trace.Hit || IsValid( trace.Entity ) && ( trace.Entity:GetClass() == "gmod_thruster" || trace.Entity:IsPlayer() ) ) then

		ent:SetNoDraw( true )
		return

	end

	local ang = trace.HitNormal:Angle()
	ang.pitch = ang.pitch + 90

	local min = ent:OBBMins()
	ent:SetPos( trace.HitPos - trace.HitNormal * min.z )
	ent:SetAngles( ang )

	ent:SetNoDraw( false )

end

function TOOL:Think()

	local mdl = self:GetClientInfo( "model" )
	if ( !IsValidThrusterModel( mdl ) ) then self:ReleaseGhostEntity() return end

	if ( !IsValid( self.GhostEntity ) || self.GhostEntity:GetModel() != mdl ) then
		self:MakeGhostEntity( mdl, vector_origin, angle_zero )
	end

	self:UpdateGhostThruster( self.GhostEntity, self:GetOwner() )

end

local ConVarsDefault = TOOL:BuildConVarList()

function TOOL.BuildCPanel( CPanel )

	CPanel:Help( "#tool.thruster.desc" )
	CPanel:ToolPresets( "thruster", ConVarsDefault )

	CPanel:KeyBinder( "#tool.thruster.forward", "thruster_keygroup", "#tool.thruster.back", "thruster_keygroup_back" )

	CPanel:NumSlider( "#tool.thruster.force", "thruster_force", 1, 10000 )

	local combo = CPanel:ComboBoxMulti( "#tool.thruster.effect" )
	for k, v in pairs( list.Get( "ThrusterEffects" ) ) do
		combo:AddOption( k, { thruster_effect = v.thruster_effect } )
	end

	CPanel:ComboBoxMulti( "#tool.thruster.sound", list.Get( "ThrusterSounds" ) )

	CPanel:CheckBox( "#tool.thruster.toggle", "thruster_toggle" )
	CPanel:CheckBox( "#tool.thruster.collision", "thruster_collision" )
	CPanel:CheckBox( "#tool.thruster.damagable", "thruster_damageable" )

	CPanel:PropSelect( "#tool.thruster.model", "thruster_model", list.Get( "ThrusterModels" ), 0 )

end

list.Set( "ThrusterSounds", "#thrustersounds.none", { thruster_soundname = "" } )
list.Set( "ThrusterSounds", "#thrustersounds.steam", { thruster_soundname = "PhysicsCannister.ThrusterLoop" } )
list.Set( "ThrusterSounds", "#thrustersounds.zap", { thruster_soundname = "WeaponDissolve.Charge" } )
list.Set( "ThrusterSounds", "#thrustersounds.beam", { thruster_soundname = "WeaponDissolve.Beam" } )
list.Set( "ThrusterSounds", "#thrustersounds.elevator", { thruster_soundname = "eli_lab.elevator_move" } )
list.Set( "ThrusterSounds", "#thrustersounds.energy", { thruster_soundname = "combine.sheild_loop" } )
list.Set( "ThrusterSounds", "#thrustersounds.ring", { thruster_soundname = "k_lab.ringsrotating" } )
list.Set( "ThrusterSounds", "#thrustersounds.resonance", { thruster_soundname = "k_lab.teleport_rings_high" } )
list.Set( "ThrusterSounds", "#thrustersounds.dropship", { thruster_soundname = "k_lab2.DropshipRotorLoop" } )
list.Set( "ThrusterSounds", "#thrustersounds.machine", { thruster_soundname = "Town.d1_town_01_spin_loop" } )

list.Set( "ThrusterModels", "models/dav0r/thruster.mdl", {} )
list.Set( "ThrusterModels", "models/MaxOfS2D/thruster_projector.mdl", {} )
list.Set( "ThrusterModels", "models/MaxOfS2D/thruster_propeller.mdl", {} )
list.Set( "ThrusterModels", "models/thrusters/jetpack.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/plasticbucket001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/PropaneCanister001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/propane_tank001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/PopCan01a.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/MetalBucket01a.mdl", {} )
list.Set( "ThrusterModels", "models/props_lab/jar01a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/lampShade001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/canister_propane01a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/canister01a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/canister02a.mdl", {} )
list.Set( "ThrusterModels", "models/props_trainstation/trainstation_ornament002.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/TrafficCone001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/clock01.mdl", {} )
list.Set( "ThrusterModels", "models/props_junk/terracotta01.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/TrapPropeller_Engine.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/FurnitureSink001a.mdl", {} )
list.Set( "ThrusterModels", "models/props_trainstation/trainstation_ornament001.mdl", {} )
list.Set( "ThrusterModels", "models/props_trainstation/trashcan_indoor001b.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/pottery02a.mdl", {} )
list.Set( "ThrusterModels", "models/props_c17/pottery03a.mdl", {} )

--PHX
list.Set( "ThrusterModels", "models/props_phx2/garbage_metalcan001a.mdl", {} )

--Tile Model Pack Thrusters
list.Set( "ThrusterModels", "models/hunter/plates/plate.mdl", {} )
list.Set( "ThrusterModels", "models/hunter/blocks/cube025x025x025.mdl", {} )

--XQM Model Pack Thrusters
list.Set( "ThrusterModels", "models/XQM/AfterBurner1.mdl", {} )
list.Set( "ThrusterModels", "models/XQM/AfterBurner1Medium.mdl", {} )
list.Set( "ThrusterModels", "models/XQM/AfterBurner1Big.mdl", {} )
list.Set( "ThrusterModels", "models/XQM/AfterBurner1Huge.mdl", {} )
list.Set( "ThrusterModels", "models/XQM/AfterBurner1Large.mdl", {} )
