--[[---------------------------------------------------------
   Name: scripted_ents
   Desc: Scripted Entity factory
-----------------------------------------------------------]]
module( "scripted_ents", package.seeall )

local Aliases = {}
local SEntList = {}

local BaseClasses = {}
	BaseClasses['anim'] 	= 'base_anim'
	BaseClasses['point'] 	= 'base_point'
	BaseClasses['brush'] 	= 'base_brush'

--[[---------------------------------------------------------
   Name: TableInherit( t, base )
   Desc: Copies any missing data from base to t
-----------------------------------------------------------]]
local function TableInherit( t, base )

	for k, v in pairs( base ) do 
		
		if ( t[k] == nil ) then	
			t[k] = v 
		elseif ( istable(t[k]) ) then
			TableInherit( t[k], v )
		end
		
	end
	
	t[ "BaseClass" ] = base
	
	return t

end


--[[---------------------------------------------------------
   Name: Register( table, string, bool )
-----------------------------------------------------------]]
function Register( t, name )

	local Base = t.Base	
	if ( !Base ) then Base = BaseClasses[ t.Type ] end
	
	local old = SEntList[ name ]
	local tab = {}
	
	tab.type 		= t.Type
	tab.t 			= t
	tab.isBaseType 	= true
	tab.Base 		= Base
	tab.t.ClassName = name
	
	if ( !Base ) then
		Msg( "WARNING: Scripted entity "..name.." has an invalid base entity!\n" )
	end

	SEntList[ name ] = tab


	-- Allow all SENTS to be duplicated, unless specified
	if ( !t.DisableDuplicator ) then
		duplicator.Allow( name )
	end
	
	--
	-- If we're reloading this entity class
	-- then refresh all the existing entities.
	--
	if ( old != nil ) then

		--
		-- Foreach entity using this class
		--
		table.ForEach( ents.FindByClass( name ), function( _, entity ) 
		
			--
			-- Replace the contents with this entity table
			--
			table.Merge( entity, tab.t )

			--
			-- Call OnReloaded hook (if it has one)
			--
			if ( entity.OnReloaded ) then
				entity:OnReloaded()
			end
		
		end )

	end

	if ( !t.Spawnable ) then return end

	list.Set( "SpawnableEntities", name, { 	
											-- Required information
											PrintName		= t.PrintName, 
											ClassName		= name,
											Category		= t.Category,

											-- Optional information
											NormalOffset	= t.NormalOffset,
											DropToFloor		= t.DropToFloor,
											Author			= t.Author,
											AdminOnly		= t.AdminOnly,
											Information		= t.Information
										} )
	
end

--
-- All scripts have been loaded...
--
function OnLoaded()

	--
	-- Once all the scripts are loaded we can set up the baseclass
	-- - we have to wait until they're all setup because load order
	-- could cause some entities to load before their bases!
	--
	table.ForEach( SEntList, function( k, v ) 

		baseclass.Set( k, Get( k ) )

	end )

end


--[[---------------------------------------------------------
   Name: Get( string )
-----------------------------------------------------------]]
function Get( name )
	
	-- Do we have an alias?
	if ( Aliases[ name ] ) then
		name = Aliases[ name ]
	end

	if ( SEntList[ name ] == nil ) then return nil end


	-- Create/copy a new table
	local retval = {}
	for k, v in pairs( SEntList[ name ].t ) do 
		retval[k] = v
	end
	
	-- Derive from base class
	if ( name != SEntList[ name ].Base ) then
	
		if (!Get( SEntList[ name ].Base )) then
		
			Msg("ERROR: Trying to derive entity "..tostring(name).." from non existant entity "..tostring(SEntList[ name ].Base).."!\n" )
		
		else
	
			retval = TableInherit( retval, Get( SEntList[ name ].Base ) )
		
		end
		
	end
	
	return retval

end


--[[---------------------------------------------------------
   Name: GetType( string )
-----------------------------------------------------------]]
function GetType( name )

	for k, v in pairs( BaseClasses ) do 
		if ( name == v ) then return k end
	end
	
	local ent = SEntList[ name ]
	if ( ent == nil ) then return nil end
	
	if ( ent.type ) then
		return ent.type
	end
	
	if ( ent.Base ) then
		return GetType( ent.Base )
	end
	
	return nil
	
	
end

--[[---------------------------------------------------------
   Name: GetStored( string )
   Desc: Gets the REAL sent table, not a copy
-----------------------------------------------------------]]
function GetStored( name )
	return SEntList[ name ]
end

--[[---------------------------------------------------------
   Name: GetList( string )
   Desc: Get a list of all the registered SENTs
-----------------------------------------------------------]]
function GetList()
	local result = {}
	
	for k,v in pairs(SEntList) do
		result[ k ] = v
	end
	
	return result
end

--[[---------------------------------------------------------
   Name: GetSpawnable
-----------------------------------------------------------]]
function GetSpawnable()

	local result = {}
	
	for k, v in pairs( SEntList ) do
		
		local tab = v.t
		
		if ( tab.Spawnable ) then
			table.insert( result, tab )
		end
		
	end
	
	return result
	
end


--[[---------------------------------------------------------
   Name: Alias
-----------------------------------------------------------]]
function Alias( From, To )

	Aliases[ From ] = To
	
end


--[[---------------------------------------------------------
   Name: GetMember
-----------------------------------------------------------]]
function GetMember( entity_name, membername )

	if ( !entity_name ) then return end

	local ent = SEntList[ entity_name ];

	if ( !ent ) then return end

	local member = ent.t[ membername ]
	if ( member != nil ) then return member end

	-- If our base is the same as us - don't infinite loop!
	if ( entity_name == ent.Base ) then return end

	return GetMember( ent.Base, membername )
	
end
