--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]



--[[---------------------------------------------------------
   Duplicator module,
   to add new constraints or entity classes use...

   duplicator.RegisterConstraint( "name", funct, ... )
   duplicator.RegisterEntityClass( "class", funct, ... )

-----------------------------------------------------------]]

module( "duplicator", package.seeall )

--
-- When saving or loading all coordinates are saved relative to these
--
local LocalPos = Vector( 0, 0, 0 )
local LocalAng = Angle( 0, 0, 0 )

--
-- Should be set to the player that is creating/copying stuff. Can be nil.
--
local ActionPlayer = nil

--
-- The physics object Saver/Loader
--
local PhysicsObject =
{
	Save = function( data, phys )

		data.Pos = phys:GetPos()
		data.Angle = phys:GetAngles()
		data.Frozen = !phys:IsMoveable()
		if ( !phys:IsGravityEnabled() ) then data.NoGrav = true end
		if ( phys:IsAsleep() ) then data.Sleep = true end

		data.Pos, data.Angle = WorldToLocal( data.Pos, data.Angle, LocalPos, LocalAng )

	end,

	Load = function( data, phys )

		if ( isvector( data.Pos ) and isangle( data.Angle ) ) then

			local pos, ang = LocalToWorld( data.Pos, data.Angle, LocalPos, LocalAng )
			phys:SetPos( pos )
			phys:SetAngles( ang )

		end

		-- Let's not Wake or put anything to sleep for now
		--[[
		if ( data.Sleep ) then
			if ( IsValid( phys ) ) then phys:Sleep() end
		else
			phys:Wake()
		end
		]]

		if ( data.Frozen ) then

			phys:EnableMotion( false )

			-- If we're being created by a player then add these to their frozen list so they can unfreeze them all
			if ( IsValid( ActionPlayer ) ) then
				ActionPlayer:AddFrozenPhysicsObject( phys:GetEntity(), phys )
			end

		end

		if ( data.NoGrav ) then phys:EnableGravity( false ) end

	end,
}

--
-- Entity physics saver
--
local EntityPhysics =
{
	--
	-- Loop each bone, calling PhysicsObject.Save
	--
	Save = function( data, Entity )

		local num = Entity:GetPhysicsObjectCount()

		for objectid = 0, num-1 do

			local obj = Entity:GetPhysicsObjectNum( objectid )
			if ( !IsValid( obj ) ) then continue end

			data[ objectid ] = {}
			PhysicsObject.Save( data[ objectid ], obj )

		end

	end,

	--
	-- Loop each bone, calling PhysicsObject.Load
	--
	Load = function( data, Entity )

		if ( !istable( data ) ) then return end

		for objectid, objectdata in pairs( data ) do

			local Phys = Entity:GetPhysicsObjectNum( objectid )
			if ( !IsValid( Phys ) ) then continue end

			PhysicsObject.Load( objectdata, Phys )

		end

	end,
}

--
-- Entity saver
--
local EntitySaver =
{
	--
	-- Called on each entity when saving
	--
	Save = function( data, ent )

		--
		-- Merge the entities actual table with the table we're saving
		-- this is terrible behaviour - but it's what we've always done.
		--
		if ( ent.PreEntityCopy ) then ent:PreEntityCopy() end
		table.Merge( data, ent:GetTable() )
		if ( ent.PostEntityCopy ) then ent:PostEntityCopy() end

		--
		-- Set so me generic variables that pretty much all entities
		-- would like to save.
		--
		data.Pos				= ent:GetPos()
		data.Angle				= ent:GetAngles()
		data.Class				= ent:GetClass()
		data.Model				= ent:GetModel()
		data.Skin				= ent:GetSkin()
		data.Mins, data.Maxs	= ent:GetCollisionBounds()
		data.ColGroup			= ent:GetCollisionGroup()
		data.Name				= ent:GetName()
		data.WorkshopID			= ent:GetWorkshopID()
		data.CurHealth			= ent:Health()
		data.MaxHealth			= ent:GetMaxHealth()
		data.Persistent			= ent:GetPersistent()

		data.Pos, data.Angle	= WorldToLocal( data.Pos, data.Angle, LocalPos, LocalAng )

		data.ModelScale			= ent:GetModelScale()
		if ( data.ModelScale == 1 ) then data.ModelScale = nil end

		-- This is useful for addons to determine if the entity was map spawned or not
		if ( ent:CreatedByMap() ) then
			data.MapCreationID = ent:MapCreationID()
		end

		-- Allow the entity to override the class
		-- (this is a hack for the jeep, since it's real class is different from the one it reports as)
		if ( ent.ClassOverride ) then data.Class = ent.ClassOverride end

		-- Save the physics
		data.PhysicsObjects = data.PhysicsObjects or {}
		EntityPhysics.Save( data.PhysicsObjects, ent )


		-- Flexes
		data.FlexScale = ent:GetFlexScale()
		for i = 0, ent:GetFlexNum() do

			local w = ent:GetFlexWeight( i )
			if ( w != 0 ) then
				data.Flex = data.Flex or {}
				data.Flex[ i ] = w
			end

		end

		-- Body Groups
		local bg = ent:GetBodyGroups()
		if ( bg ) then

			for k, v in pairs( bg ) do

				--
				-- If it has a non default setting, save it.
				--
				if ( ent:GetBodygroup( v.id ) > 0 ) then

					data.BodyG = data.BodyG or {}
					data.BodyG[ v.id ] = ent:GetBodygroup( v.id )

				end

			end

		end

		-- Non Sandbox tool set color and materials
		if ( ent:GetColor() != color_white ) then data._DuplicatedColor = ent:GetColor() end
		if ( ent:GetMaterial() != "" ) then data._DuplicatedMaterial = ent:GetMaterial() end

		-- Sub materials
		local subMaterials = {}

		for i = 0, 31 do

			local mat = ent:GetSubMaterial( i )

			if ( mat:len() > 0 ) then
				subMaterials[ i ] = mat
			end

		end

		if ( !table.IsEmpty( subMaterials ) ) then data._DuplicatedSubMaterials = subMaterials end

		-- Bone Manipulations
		if ( ent:HasBoneManipulations() ) then

			data.BoneManip = {}

			for i = 0, ent:GetBoneCount() do

				local t = {}

				local s = ent:GetManipulateBoneScale( i )
				local a = ent:GetManipulateBoneAngles( i )
				local p = ent:GetManipulateBonePosition( i )

				if ( s != Vector( 1, 1, 1 ) ) then t[ 's' ] = s end -- scale
				if ( a != angle_zero ) then t[ 'a' ] = a end -- angle
				if ( p != vector_origin ) then t[ 'p' ] = p end -- position

				if ( !table.IsEmpty( t ) ) then
					data.BoneManip[ i ] = t
				end

			end

		end

		--
		-- Store networks vars/DT vars (assigned using SetupDataTables)
		--
		if ( ent.GetNetworkVars ) then
			data.DT = ent:GetNetworkVars()
		end

		-- Make this function on your SENT if you want to modify the
		-- returned table specifically for your entity.
		if ( ent.OnEntityCopyTableFinish ) then
			ent:OnEntityCopyTableFinish( data )
		end

		--
		-- Exclude this crap
		--
		for k, v in pairs( data ) do

			if ( isfunction( v ) ) then
				data[k] = nil
			end

		end

		data.OnDieFunctions = nil
		data.AutomaticFrameAdvance = nil
		data.BaseClass = nil

	end,

	--
	-- Fill in the data!
	--
	Load = function( data, ent )

		if ( !data ) then return end

		-- We do the second check for models because apparently setting the model on an NPC causes some position changes
		-- And to prevent NPCs going into T-pose briefly upon duplicating
		if ( data.Model and data.Model != ent:GetModel() ) then ent:SetModel( data.Model ) end
		if ( data.Angle ) then ent:SetAngles( data.Angle ) end
		if ( data.Pos ) then ent:SetPos( data.Pos ) end
		if ( data.Skin ) then ent:SetSkin( data.Skin ) end
		if ( data.Flex ) then DoFlex( ent, data.Flex, data.FlexScale ) end
		if ( data.BoneManip ) then DoBoneManipulator( ent, data.BoneManip ) end
		if ( data.ModelScale ) then ent:SetModelScale( data.ModelScale, 0 ) end
		if ( data.ColGroup ) then ent:SetCollisionGroup( data.ColGroup ) end
		if ( data.Name ) then ent:SetName( data.Name ) end
		if ( data.Persistent ) then ent:SetPersistent( data.Persistent ) end
		if ( data._DuplicatedColor ) then ent:SetColor( data._DuplicatedColor ) end
		if ( data._DuplicatedMaterial ) then ent:SetMaterial( data._DuplicatedMaterial ) end

		-- Sub materials
		if ( data._DuplicatedSubMaterials ) then

			for id, mat in pairs( data._DuplicatedSubMaterials ) do

				ent:SetSubMaterial( id, mat )

			end

		end

		-- Body Groups
		if ( data.BodyG ) then
			for k, v in pairs( data.BodyG ) do
				ent:SetBodygroup( k, v )
			end
		end

		--
		-- Restore NetworkVars/DataTable variables (the SetupDataTables values)
		--
		if ( ent.RestoreNetworkVars ) then
			ent:RestoreNetworkVars( data.DT )
		end

	end,
}

local DuplicateAllowed = {}

--
-- Allow this entity to be duplicated
--
function Allow( classname )

	DuplicateAllowed[ classname ] = true

end

--
-- Disallow this entity to be duplicated
--
function Disallow( classname )

	DuplicateAllowed[ classname ] = false

end

--
-- Returns true if we can copy/paste this entity
--
function IsAllowed( classname )

	return DuplicateAllowed[ classname ]

end

ConstraintType 	= ConstraintType or {}

--
-- When a copy is copied it will be translated according to these
-- If you set them - make sure to set them back to 0 0 0!
--
function SetLocalPos( v ) LocalPos = v * 1 end
function SetLocalAng( v ) LocalAng = v * 1 end

--[[---------------------------------------------------------
	Register a constraint to be duplicated
-----------------------------------------------------------]]
function RegisterConstraint( _name_, _function_, ... )

	ConstraintType[ _name_ ] = {}

	ConstraintType[ _name_ ].Func = _function_
	ConstraintType[ _name_ ].Args = { ... }

end

EntityClasses = EntityClasses or {}

--[[---------------------------------------------------------
	Register an entity's class, to allow it to be duplicated
-----------------------------------------------------------]]
function RegisterEntityClass( _name_, _function_, ... )

	EntityClasses[ _name_ ] = {}

	EntityClasses[ _name_ ].Func = _function_
	EntityClasses[ _name_ ].Args = {...}

	Allow( _name_ )

end

--[[---------------------------------------------------------
   Returns an entity class factory
-----------------------------------------------------------]]
function FindEntityClass( _name_ )

	if ( !_name_ ) then return end
	return EntityClasses[ _name_ ]

end

BoneModifiers = BoneModifiers or {}
EntityModifiers = EntityModifiers or {}

function RegisterBoneModifier( _name_, _function_ ) BoneModifiers[ _name_ ] = _function_ end
function RegisterEntityModifier( _name_, _function_ ) EntityModifiers[ _name_ ] = _function_ end

--
-- Try to work out which workshop addons are used by this dupe. This is far from perfect.
--
function FigureOutRequiredAddons( Dupe )

	local addons = {}
	for _, ent in pairs( Dupe.Entities ) do
		for id, addon in pairs( engine.GetAddons() ) do
			-- Model
			if ( ent.Model and file.Exists( ent.Model, addon.title ) ) then
				addons[ addon.wsid ] = true
			end

			-- Material override
			if ( ent._DuplicatedMaterial and file.Exists( "materials/" .. ent._DuplicatedMaterial .. ".vmt", addon.title ) ) then
				addons[ addon.wsid ] = true
			end
		end
	end
	Dupe.RequiredAddons = table.GetKeys( addons )

end

if ( CLIENT ) then return end

--[[---------------------------------------------------------
   Restore's the flex data
-----------------------------------------------------------]]
function DoFlex( ent, Flex, Scale )

	if ( !Flex ) then return end
	if ( !IsValid( ent ) ) then return end

	for k, v in pairs( Flex ) do
		ent:SetFlexWeight( k, v )
	end

	if ( Scale ) then
		ent:SetFlexScale( Scale )
	end

end

--[[---------------------------------------------------------
   Restore's the bone's data
-----------------------------------------------------------]]
function DoBoneManipulator( ent, Bones )

	if ( !Bones ) then return end
	if ( !IsValid( ent ) ) then return end

	for k, v in pairs( Bones ) do

		if ( v.s ) then ent:ManipulateBoneScale( k, v.s ) end
		if ( v.a ) then ent:ManipulateBoneAngles( k, v.a ) end
		if ( v.p ) then ent:ManipulateBonePosition( k, v.p ) end

	end

end

--[[---------------------------------------------------------
   Generic function for duplicating stuff
-----------------------------------------------------------]]
function GenericDuplicatorFunction( Player, data )

	if ( !IsAllowed( data.Class ) ) then
		-- MsgN( "duplicator: ", data.Class, " isn't allowed to be duplicated!" )
		return
	end

	--
	-- Is this entity 'admin only'?
	--
	if ( IsValid( Player ) and !Player:IsAdmin() ) then

		if ( !scripted_ents.GetMember( data.Class, "Spawnable" ) ) then return end
		if ( scripted_ents.GetMember( data.Class, "AdminOnly" ) ) then return end

	end

	local Entity = ents.Create( data.Class )
	if ( !IsValid( Entity ) ) then return end

	-- TODO: Entity not found - maybe spawn a prop_physics with their model?

	DoGeneric( Entity, data )

	Entity:Spawn()
	Entity:Activate()

	EntityPhysics.Load( data.PhysicsObjects, Entity )

	table.Merge( Entity:GetTable(), data )

	return Entity

end

--[[---------------------------------------------------------
	Automates the process of adding crap the EntityMods table
-----------------------------------------------------------]]
function StoreEntityModifier( Entity, Type, Data )

	if ( !IsValid( Entity ) ) then return end

	Entity.EntityMods = Entity.EntityMods or {}

	-- Copy the data
	local NewData = Entity.EntityMods[ Type ] or {}
	table.Merge( NewData, Data )

	Entity.EntityMods[ Type ] = NewData

end

--[[---------------------------------------------------------
	Clear entity modification
-----------------------------------------------------------]]
function ClearEntityModifier( Entity, Type )

	if ( !IsValid( Entity ) ) then return end

	Entity.EntityMods = Entity.EntityMods or {}
	Entity.EntityMods[ Type ] = nil

end

--[[---------------------------------------------------------
	Automates the process of adding crap the BoneMods table
-----------------------------------------------------------]]
function StoreBoneModifier( Entity, BoneID, Type, Data )

	if ( !IsValid( Entity ) ) then return end

	-- Copy the data
	NewData = {}
	table.Merge( NewData, Data )

	-- Add it to the entity
	Entity.BoneMods = Entity.BoneMods or {}
	Entity.BoneMods[ BoneID ] = Entity.BoneMods[ BoneID ] or {}

	Entity.BoneMods[ BoneID ][ Type ] = NewData

end

--[[---------------------------------------------------------
	Returns a copy of the passed entity's table
-----------------------------------------------------------]]
function CopyEntTable( Ent )

	local output = {}
	EntitySaver.Save( output, Ent )
	return output

end

--
-- Work out the AABB size
--
function WorkoutSize( Ents )

	local mins = Vector( -1, -1, -1 )
	local maxs = Vector( 1, 1, 1 )


	for k, v in pairs( Ents ) do

		if ( !v.Mins or !v.Maxs ) then continue end
		if ( !v.Angle or !v.Pos ) then continue end

		--
		-- Rotate according to the entity!
		--
		local mi = v.Mins
		local ma = v.Maxs

		-- There has to be a better way
		local t1 = LocalToWorld( Vector( mi.x, mi.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local t2 = LocalToWorld( Vector( ma.x, mi.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local t3 = LocalToWorld( Vector( mi.x, ma.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local t4 = LocalToWorld( Vector( ma.x, ma.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )

		local b1 = LocalToWorld( Vector( mi.x, mi.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local b2 = LocalToWorld( Vector( ma.x, mi.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local b3 = LocalToWorld( Vector( mi.x, ma.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
		local b4 = LocalToWorld( Vector( ma.x, ma.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )

		mins.x = math.min( mins.x, t1.x, t2.x, t3.x, t4.x, b1.x, b2.x, b3.x, b4.x )
		mins.y = math.min( mins.y, t1.y, t2.y, t3.y, t4.y, b1.y, b2.y, b3.y, b4.y )
		mins.z = math.min( mins.z, t1.z, t2.z, t3.z, t4.z, b1.z, b2.z, b3.z, b4.z )

		maxs.x = math.max( maxs.x, t1.x, t2.x, t3.x, t4.x, b1.x, b2.x, b3.x, b4.x )
		maxs.y = math.max( maxs.y, t1.y, t2.y, t3.y, t4.y, b1.y, b2.y, b3.y, b4.y )
		maxs.z = math.max( maxs.z, t1.z, t2.z, t3.z, t4.z, b1.z, b2.z, b3.z, b4.z )

	end

	return mins, maxs

end

--[[---------------------------------------------------------
   Copy this entity, and all of its constraints and entities
   and put them in a table.
-----------------------------------------------------------]]
function Copy( Ent, AddToTable )

	local Ents = {}
	local Constraints = {}

	GetAllConstrainedEntitiesAndConstraints( Ent, Ents, Constraints )

	local EntTables = {}
	if ( AddToTable != nil ) then EntTables = AddToTable.Entities or {} end

	for k, v in pairs( Ents ) do
		EntTables[ k ] = CopyEntTable( v )
	end

	local ConstraintTables = {}
	if ( AddToTable != nil ) then ConstraintTables = AddToTable.Constraints or {} end

	for k, v in pairs( Constraints ) do
		ConstraintTables[ k ] = v
	end

	local mins, maxs = WorkoutSize( EntTables )

	return {
		Entities = EntTables,
		Constraints = ConstraintTables,
		Mins = mins,
		Maxs = maxs
	}

end

function CopyEnts( Ents )

	local Ret = { Entities = {}, Constraints = {} }

	for k, v in pairs( Ents ) do

		Ret = Copy( v, Ret )

	end

	return Ret

end

--[[---------------------------------------------------------
   Create an entity from a table.
-----------------------------------------------------------]]
function CreateEntityFromTable( Player, EntTable )

	-- Get rid of stored outputs, they are being abused
	-- Do it here, so that entities can store new ones on creation
	EntTable.m_tOutputs = nil

	--
	-- Convert position/angle to `local`
	--
	if ( EntTable.Pos and EntTable.Angle ) then

		EntTable.Pos, EntTable.Angle = LocalToWorld( EntTable.Pos, EntTable.Angle, LocalPos, LocalAng )

	end

	local EntityClass = FindEntityClass( EntTable.Class )

	-- This class is unregistered. Instead of failing try using a generic
	-- Duplication function to make a new copy..
	if ( !EntityClass ) then

		return GenericDuplicatorFunction( Player, EntTable )

	end

	-- Build the argument list
	local ArgList = {}

	for iNumber, Key in pairs( EntityClass.Args ) do

		local Arg = nil

		-- Translate keys from old system
		if ( Key == "pos" or Key == "position" ) then Key = "Pos" end
		if ( Key == "ang" or Key == "Ang" or Key == "angle" ) then Key = "Angle" end
		if ( Key == "model" ) then Key = "Model" end

		Arg = EntTable[ Key ]

		-- Special keys
		if ( Key == "Data" ) then Arg = EntTable end

		-- If there's a missing argument then unpack will stop sending at that argument so send it as `false`
		if ( Arg == nil ) then Arg = false end

		ArgList[ iNumber ] = Arg

	end

	-- Create and return the entity
	return EntityClass.Func( Player, unpack( ArgList ) )

end


--[[---------------------------------------------------------
  Make a constraint from a constraint table
-----------------------------------------------------------]]
function CreateConstraintFromTable( Constraint, EntityList, Player )

	local Factory = ConstraintType[ Constraint.Type ]
	if ( !Factory ) then return end

	local Args = {}
	for k, Key in pairs( Factory.Args ) do

		local Val = Constraint[ Key ]

		for i = 1, 6 do

			if ( Constraint.Entity[ i ] ) then

				if ( Key == "Ent" .. i ) then
					Val = EntityList[ Constraint.Entity[ i ].Index ]
					if ( Constraint.Entity[ i ].World ) then
						Val = game.GetWorld()
					end
				end

				if ( Key == "Bone" .. i ) then Val = Constraint.Entity[ i ].Bone or 0 end
				if ( Key == "LPos" .. i ) then Val = Constraint.Entity[ i ].LPos end
				if ( Key == "WPos" .. i ) then Val = Constraint.Entity[ i ].WPos end
				if ( Key == "Length" .. i ) then Val = Constraint.Entity[ i ].Length or 0 end

			end
		end

		-- A little hack to give the duped constraints the correct player object
		if ( Key:lower() == "pl" or Key:lower() == "ply" or Key:lower() == "player" ) then Val = Player end

		-- If there's a missing argument then unpack will stop sending at that argument
		if ( Val == nil ) then Val = false end

		table.insert( Args, Val )

	end

	return Factory.Func( unpack( Args ) )

end

--[[---------------------------------------------------------
   Given entity list and constranit list, create all entities
   and return their tables
-----------------------------------------------------------]]
function Paste( Player, entityList, constraintList )

	--
	-- Store the player
	--
	local oldplayer = ActionPlayer
	ActionPlayer = Player

	--
	-- Copy the table - because we're gonna be changing some stuff on it.
	--
	local EntityList = table.Copy( entityList )
	local ConstraintList = table.Copy( constraintList )

	local CreatedEntities = {}

	--
	-- Create the Entities
	--
	for k, v in pairs( EntityList ) do

		local e = nil
		local b = ProtectedCall( function() e = CreateEntityFromTable( Player, v ) end )
		if ( !b ) then continue end

		if ( IsValid( e ) ) then

			--
			-- Call this here ( as well as before :Spawn) because Spawn/Init might have stomped the values
			--
			if ( e.RestoreNetworkVars ) then
				e:RestoreNetworkVars( v.DT )
			end

			if ( e.OnDuplicated ) then
				e:OnDuplicated( v )
			end

		end

		CreatedEntities[ k ] = e

		if ( CreatedEntities[ k ] ) then

			CreatedEntities[ k ].BoneMods = table.Copy( v.BoneMods )
			CreatedEntities[ k ].EntityMods = table.Copy( v.EntityMods )
			CreatedEntities[ k ].PhysicsObjects = table.Copy( v.PhysicsObjects )

		else

			CreatedEntities[ k ] = nil

		end

	end

	--
	-- Apply modifiers to the created entities
	--
	for EntID, Ent in pairs( CreatedEntities ) do

		ApplyEntityModifiers( Player, Ent )
		ApplyBoneModifiers( Player, Ent )

		if ( Ent.PostEntityPaste ) then
			Ent:PostEntityPaste( Player or NULL, Ent, CreatedEntities )
		end

	end

	local CreatedConstraints = {}

	--
	-- Create constraints
	--
	for k, Constraint in pairs( ConstraintList ) do

		local Entity = nil
		ProtectedCall( function() Entity = CreateConstraintFromTable( Constraint, CreatedEntities, Player ) end )

		if ( IsValid( Entity ) ) then
			table.insert( CreatedConstraints, Entity )
		end

	end

	ActionPlayer = oldplayer

	return CreatedEntities, CreatedConstraints

end


--[[---------------------------------------------------------
  Applies entity modifiers
-----------------------------------------------------------]]
function ApplyEntityModifiers( Player, Ent )

	if ( !Ent ) then return end
	if ( !Ent.EntityMods ) then return end

	for Type, ModFunction in pairs( EntityModifiers ) do

		if ( Ent.EntityMods[ Type ] ) then

			ModFunction( Player, Ent, Ent.EntityMods[ Type ] )

		end
	end

end


--[[---------------------------------------------------------
  Applies Bone Modifiers
-----------------------------------------------------------]]
function ApplyBoneModifiers( Player, Ent )

	if ( !Ent ) then return end
	if ( !Ent.PhysicsObjects ) then return end
	if ( !Ent.BoneMods ) then return end

	--
	-- Loop every Bone on the entity
	--
	for Bone, Types in pairs( Ent.BoneMods ) do

		-- The physics object isn't valid, skip it.
		if ( !Ent.PhysicsObjects[ Bone ] ) then continue end

		-- Loop through each modifier on this bone
		for Type, Data in pairs( Types ) do

			-- Find and all the function
			local ModFunction = BoneModifiers[ Type ]
			if ( ModFunction ) then
				ModFunction( Player, Ent, Bone, Ent:GetPhysicsObjectNum( Bone ), Data )
			end

		end

	end

end


--
-- Returns all constrained Entities and constraints
-- This is kind of in the wrong place.
--
-- This function will accept the world entity to save constrains, but will not actually save the world entity itself
--
function GetAllConstrainedEntitiesAndConstraints( ent, EntTable, ConstraintTable )

	if ( !IsValid( ent ) and !ent:IsWorld() ) then return end

	-- Translate the class name
	local classname = ent:GetClass()
	if ( ent.ClassOverride ) then classname = ent.ClassOverride end

	-- Is the entity in the dupe whitelist?
	if ( !IsAllowed( classname ) and !ent:IsWorld() ) then
		-- MsgN( "duplicator: ", classname, " isn't allowed to be duplicated!" )
		return
	end

	-- Entity doesn't want to be duplicated.
	if ( ent.DoNotDuplicate ) then return end

	if ( !ent:IsWorld() ) then EntTable[ ent:EntIndex() ] = ent end

	if ( !constraint.HasConstraints( ent ) ) then return end

	local ConTable = constraint.GetTable( ent )

	for key, constr in pairs( ConTable ) do

		local index = constr.Constraint:GetCreationID()

		if ( !ConstraintTable[ index ] ) then

			-- Add constraint to the constraints table
			ConstraintTable[ index ] = constr

			-- Run the Function for any ents attached to this constraint
			for _, ConstrainedEnt in pairs( constr.Entity ) do

				if ( !ConstrainedEnt.Entity:IsWorld() ) then

					GetAllConstrainedEntitiesAndConstraints( ConstrainedEnt.Entity, EntTable, ConstraintTable )

				end

			end

		end
	end

	return EntTable, ConstraintTable

end


--
-- Return true if this entity should be removed when RemoveMapCreatedEntities is called
-- We don't want to remove all entities.
--
local function ShouldMapEntityBeRemoved( ent, classname )

	if ( classname == "prop_physics" ) then return true end
	if ( classname == "prop_physics_multiplayer" ) then return true end
	if ( classname == "prop_ragdoll" ) then return true end
	if ( ent:IsNPC() ) then return true end
	if ( IsAllowed( classname ) ) then return true end

	return false

end

--
-- Help to remove certain map created entities before creating the saved entities
-- This is obviously so we don't get duplicate props everywhere. It should be called
-- before calling Paste.
--
function RemoveMapCreatedEntities()

	for k, v in ipairs( ents.GetAll() ) do

		if ( v:CreatedByMap() and ShouldMapEntityBeRemoved( v, v:GetClass() ) ) then
			v:Remove()
		end

	end

end

--
-- BACKWARDS COMPATIBILITY - PHASE OUT, RENAME?
--
function DoGenericPhysics( Entity, Player, data )

	if ( !data or !data.PhysicsObjects ) then return end

	EntityPhysics.Load( data.PhysicsObjects, Entity )

end

function DoGeneric( ent, data )

	EntitySaver.Load( data, ent )

end
