
TOOL.Category		= "Construction"
TOOL.Name			= "#Duplicator"
TOOL.Command		= nil
TOOL.ConfigName		= ""

TOOL.ClientConVar[ "simple" ] = 0

cleanup.Register( "duplicates" )

//
// Converts to world so that the entities will be spawned in the correct positions
//
local function ConvertEntityPositionsToWorld( EntTable, Offset, HoldAngle )

	for k, Ent in pairs( EntTable ) do

		local NewPos, NewAngle = LocalToWorld( Ent.LocalPos, Ent.LocalAngle, Offset, HoldAngle )
		
		Ent.Pos = NewPos
		Ent.Angle = NewAngle
		
		// And for physics objects
		if ( Ent.PhysicsObjects ) then
			for Num, Object in pairs( Ent.PhysicsObjects ) do
	
				local NewPos, NewAngle = LocalToWorld( Object.LocalPos, Object.LocalAngle, Offset, HoldAngle )
			
				Object.Pos = NewPos
				Object.Angle = NewAngle
	
			end
		end
		
		
	end

end

//
// Move the world positions
//
local function ConvertConstraintPositionsToWorld( Constraints, Offset, HoldAngle )

	for k, Constraint in pairs( Constraints ) do
	
		if ( Constraint.Entity ) then
		
			for k, Entity in pairs( Constraint.Entity ) do
			
				if (Entity.World) then
				
					local NewPos, NewAngle = LocalToWorld( Entity.LPos, Angle(0,0,0), Offset, HoldAngle )
				
					Entity.LPosOld = Entity.LPos
					Entity.LPos = NewPos
				
				end
			
			end
		
		end
	
	end

end


//
// Resets the positions of all the entities in the table
//
local function ResetPositions( EntTable, Constraints )

	for k, Ent in pairs( EntTable ) do
	
		Ent.Pos = Ent.LocalPos * 1
		Ent.Angle = Ent.LocalAngle * 1
		
		// And for physics objects
		if ( Ent.PhysicsObjects ) then		
			for Num, Object in pairs( Ent.PhysicsObjects ) do
	
				Object.Pos = Object.LocalPos * 1
				Object.Angle = Object.LocalAngle * 1
	
			end
		end
		
	end
	
	for k, Constraint in pairs( Constraints ) do
	
		if ( Constraint.Entity ) then
		
			for k, Entity in pairs( Constraint.Entity ) do
			
				if (Entity.LPosOld) then
					Entity.LPos = Entity.LPosOld
					Entity.LPosOld = nil
				end
			
			end
		
		end
	
	end

end

//
// Converts the positions from world positions to positions local to Offset
//
local function ConvertPositionsToLocal( EntTable, Constraints, Offset, HoldAngle )

	for k, Ent in pairs( EntTable ) do
	
		Ent.Pos = Ent.Pos - Offset
		Ent.LocalPos = Ent.Pos * 1
		Ent.LocalAngle = Ent.Angle * 1
		
		if ( Ent.PhysicsObjects ) then
			for Num, Object in pairs(Ent.PhysicsObjects) do
			
				Object.Pos = Object.Pos - Offset
				Object.LocalPos = Object.Pos * 1
				Object.LocalAngle = Object.Angle * 1
				
			end
		end

	end
	
	// If the entity is constrained to the world we want to move the points to be
	// relative to where we're clicking
	for k, Constraint in pairs( Constraints ) do
	
		if ( Constraint.Entity ) then
		
			for k, Entity in pairs( Constraint.Entity ) do
			
				if (Entity.World) then
					Entity.LPos = Entity.LPos - Offset
				end
			
			end
		
		end
	
	end

end

//
// PASTE
//
function TOOL:LeftClick( trace )

	if ( CLIENT ) then	return true	end
	if (!self.Entities) then return end
	
	local angle  = self:GetOwner():GetAngles()
	angle.pitch = 0
	angle.roll = 0	
	
	
	// Create the entities at the clicked position at the angle we're facing right now	
	ConvertEntityPositionsToWorld( self.Entities, trace.HitPos, angle - self.HoldAngle )
	ConvertConstraintPositionsToWorld( self.Constraints, trace.HitPos, angle - self.HoldAngle )
	
		local Ents, Constraints = duplicator.Paste( self:GetOwner(), self.Entities, self.Constraints )
	
	ResetPositions( self.Entities, self.Constraints )
		

	// Add all of the created entities
	//  to the undo system under one undo.
	undo.Create( "Duplicator" )
	
		for k, ent in pairs( Ents ) do
			undo.AddEntity( ent )
		end
		
		for k, ent in pairs( Ents )	do 
			self:GetOwner():AddCleanup( "duplicates", ent ) 
		end
		
		undo.SetPlayer( self:GetOwner() )
		
	undo.Finish()

	return true
	
end

//
// Copy
//
function TOOL:RightClick( trace )

	if (!trace.Entity ||
		!trace.Entity:IsValid() ||
		trace.Entity:IsPlayer() )
	then
	
		self.Entities = nil
		self:ReleaseGhostEntity()
		self.GhostEntities = {}
		
		return false
	
	end

	local StartPos = trace.Entity:GetPos()

	self:ReleaseGhostEntity()

	if ( CLIENT ) then return true end
	
	// Get the distance from the floor
	local tr = {}
	tr.start = StartPos
	tr.endpos = StartPos + Vector(0,0,-1024)
	tr.mask = MASK_NPCSOLID_BRUSHONLY
	local tr_floor = util.TraceLine( tr )
	if (tr_floor.Hit) then 

		StartPos = StartPos  + Vector(0,0,-1) * tr_floor.Fraction * 1024
	
	end
	
	// Copy the entities
	local Entities, Constraints = duplicator.Copy( trace.Entity )
	
	local angle  = self:GetOwner():GetAngles()
	angle.pitch = 0
	angle.roll = 0
	
	// Convert the positions to local
	ConvertPositionsToLocal( Entities, Constraints, StartPos, angle )

	// Store stuff for pasting/ghosting
	self.HeadEntityIdx	= trace.Entity:EntIndex()
	self.HoldAngle 		= angle
	self.HoldPos 		= StartPos
	self.Entities		= Entities
	self.Constraints	= Constraints
	
	self:StartGhostEntities( Entities )
	
	return true
	
end

//
// Builds the control panel in the spawn menu
//
function TOOL.BuildCPanel( CPanel )

	CPanel:AddControl( "Header", { Text = "#Tool_duplicator_name", Description	= "#Tool_duplicator_desc" }  )
	//CPanel:AddControl( "Checkbox", { Label = "#Simple Mode", Command = "duplicator_simple" } )

end

//
// Think
//
function TOOL:Think()

	self:UpdateGhostEntities()
	
end

//
// Make the ghost entities
//
function TOOL:MakeGhostFromTable( EntTable, pParent )

	local GhostEntity = nil
	
	if ( EntTable.Model:sub( 1, 1 ) == "*" ) then
		GhostEntity = ents.Create( "func_physbox" )
	else
	
	//if ( EntTable.Class == "prop_ragdoll" ) then
	//	GhostEntity = ents.Create( "prop_ragdoll" )
	//else
		GhostEntity = ents.Create( "prop_physics" )
	//end
	
	end
	
	// If there are too many entities we might not spawn..
	if ( !GhostEntity || GhostEntity == NULL ) then return end
	
	// If we're a ragdoll set our model  as a watermelon
	if ( EntTable.Class == "prop_ragdoll" ) then
		GhostEntity:SetModel( "models/props_junk/watermelon01.mdl" )
	else
		GhostEntity:SetModel( EntTable.Model )
	end
	
	GhostEntity:SetPos( EntTable.Pos )
	GhostEntity:SetAngles( EntTable.Angle )
	GhostEntity:Spawn()
	
	GhostEntity:DrawShadow( false )
	GhostEntity:SetMoveType( MOVETYPE_NONE )
	GhostEntity:SetSolid( SOLID_VPHYSICS );
	GhostEntity:SetNotSolid( true )
	GhostEntity:SetRenderMode( RENDERMODE_TRANSALPHA )
	GhostEntity:SetColor( 255, 255, 255, 150 )
	
	GhostEntity.Pos 	= EntTable.Pos
	GhostEntity.Angle 	= EntTable.Angle - self.HoldAngle
	
	if ( pParent ) then
		GhostEntity:SetParent( pParent )
	end
	
	return GhostEntity
	
end


/*---------------------------------------------------------
   Starts up the ghost entities
---------------------------------------------------------*/
function TOOL:Deploy()

	if ( self.Entities ) then
		self:StartGhostEntities( self.Entities )
	end

end


/*---------------------------------------------------------
   Starts up the ghost entities
---------------------------------------------------------*/
function TOOL:StartGhostEntities( EntityTable )

	self:ReleaseGhostEntity()
	self.GhostEntities = {}
	
	self.GhostEntities[ self.HeadEntityIdx ] = self:MakeGhostFromTable( EntityTable[ self.HeadEntityIdx ], self.GhostEntities[ self.HeadEntityIdx ] )
	
	
	self.Weapon:SetNetworkedEntity( "GhostEntity", self.GhostEntities[ self.HeadEntityIdx ] )
	self.Weapon:SetNetworkedVector( "HeadPos", self.GhostEntities[ self.HeadEntityIdx ].Pos )
	self.Weapon:SetNetworkedAngle( 	"HeadAngle", self.GhostEntities[ self.HeadEntityIdx ].Angle )	
	
	if ( !self.GhostEntities[ self.HeadEntityIdx ] || !self.GhostEntities[ self.HeadEntityIdx ]:IsValid()  ) then
	
		self.GhostEntities = nil
		return
	
	end
	
	for k, entTable in pairs( EntityTable ) do
		
		if ( !self.GhostEntities[ k ] ) then
			self.GhostEntities[ k ] = self:MakeGhostFromTable( entTable, self.GhostEntities[ self.HeadEntityIdx ] )
		end
	
	end

end

/*---------------------------------------------------------
   Update the ghost entity positions
---------------------------------------------------------*/
function TOOL:UpdateGhostEntities()

	if (SERVER && !self.GhostEntities) then return end

	local tr = utilx.GetPlayerTrace( self:GetOwner(), self:GetOwner():GetCursorAimVector() )
	local trace = util.TraceLine( tr )
	if (!trace.Hit) then return end

	local GhostEnt = nil
	
	if ( SERVER ) then
		GhostEnt = self.GhostEntities[ self.HeadEntityIdx ]
	else
		GhostEnt = self.Weapon:GetNetworkedEntity( "GhostEntity", nil )
		GhostEnt.Pos = self.Weapon:GetNetworkedVector( "HeadPos", Vector(0,0,0) )
		GhostEnt.Angle = self.Weapon:GetNetworkedAngle( "HeadAngle", Angle(0,0,0) )		
	end
	
	if (!GhostEnt || !GhostEnt:IsValid()) then 
		// HACKKKK - this should be done in ReleaseGhostEntity
		self.GhostEntities = nil
	return end
	
	GhostEnt:SetMoveType( MOVETYPE_VPHYSICS )
	GhostEnt:SetNotSolid( true )
	
	local angle  = self:GetOwner():GetAngles()
	angle.pitch = 0
	angle.roll = 0

	local PhysObj = GhostEnt:GetPhysicsObject()
	if ( PhysObj && PhysObj:IsValid() ) then
	
		PhysObj:EnableMotion( false )
		PhysObj:SetPos( GhostEnt.Pos + trace.HitPos )
		PhysObj:SetAngle( GhostEnt.Angle + angle )
		PhysObj:Wake()
		
	else
	
		if ( CLIENT ) then
			GhostEnt:PhysicsInit( SOLID_VPHYSICS )
		end
	
	end
		
end
