struct EntityAttachementData
{
	instanceeditable persistent var slotName : CName;
	instanceeditable persistent var slotComponentName : CName;
	instanceeditable persistent var nodeRef : NodeRef;
	instanceeditable persistent var attachementComponentName : CName;
	default attachementComponentName = 'EntityAttachementComponent';
	persistent var ownerID : EntityID;
}

class EntityAttachementRequestEvent extends Event
{
	var attachementData : EntityAttachementData;
}

class EntityAttachementComponentPS extends GameComponentPS
{
	private persistent var m_pendingChildAttachements : array< EntityAttachementData >;

	private const function GetMyEntityID() : EntityID
	{
		return PersistentID.ExtractEntityID( GetID() );
	}

	private const function GetOwnerEntityWeak() : weak< Entity >
	{
		return GameInstance.FindEntityByID( GetGameInstance(), GetMyEntityID() );
	}

	public const function GetPendingChildAttachementsData() : array< EntityAttachementData >
	{
		return m_pendingChildAttachements;
	}

	private export function OnChildAttachementRequest( evt : EntityAttachementRequestEvent ) : EntityNotificationType
	{
		if( GetOwnerEntityWeak() == NULL )
		{
			AddPendingChildAttachementRequest( evt.attachementData );
			return EntityNotificationType.DoNotNotifyEntity;
		}
		else
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
	}

	private function AddPendingChildAttachementRequest( const data : ref< EntityAttachementData > )
	{
		if( !( HasPendingChildAttachementRequest( data ) ) )
		{
			m_pendingChildAttachements.PushBack( data );
		}
	}

	private function HasPendingChildAttachementRequest( const data : ref< EntityAttachementData > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_pendingChildAttachements.Size(); i += 1 )
		{
			if( data.ownerID == m_pendingChildAttachements[ i ].ownerID )
			{
				return true;
			}
		}
		return false;
	}

	public function ClearPendingChildAttachementRequests()
	{
		m_pendingChildAttachements.Clear();
	}

}

class EntityAttachementComponent extends ScriptableComponent
{
	instanceeditable var m_parentAttachementData : EntityAttachementData;

	protected export function OnGameAttach()
	{
		RestoreAttachements();
	}

	private function GetMyPS() : EntityAttachementComponentPS
	{
		return ( ( EntityAttachementComponentPS )( GetPS() ) );
	}

	private function RestoreAttachements()
	{
		AttachToParent( GetParentAttachementData() );
		RestoreChildAttachements();
	}

	private function RestoreChildAttachements()
	{
		var i : Int32;
		var pendingChildAttachements : array< EntityAttachementData >;
		pendingChildAttachements = GetMyPS().GetPendingChildAttachementsData();
		for( i = 0; i < pendingChildAttachements.Size(); i += 1 )
		{
			AttachChild( pendingChildAttachements[ i ] );
		}
		if( pendingChildAttachements.Size() > 0 )
		{
			GetMyPS().ClearPendingChildAttachementRequests();
		}
	}

	public const function GetParentAttachementData() : EntityAttachementData
	{
		return m_parentAttachementData;
	}

	private function AttachToParent( data : EntityAttachementData )
	{
		var parentID : PersistentID;
		var globalRef : GlobalNodeRef;
		var evt : EntityAttachementRequestEvent;
		if( !( IsNameValid( data.slotComponentName ) ) && !( IsNameValid( data.slotName ) ) )
		{
			return;
		}
		globalRef = ResolveNodeRefWithEntityID( data.nodeRef, GetOwner().GetEntityID() );
		parentID = CreatePersistentID( ( ( EntityID )( globalRef ) ), data.attachementComponentName );
		if( PersistentID.IsDefined( parentID ) )
		{
			evt = new EntityAttachementRequestEvent;
			data.ownerID = GetOwner().GetEntityID();
			evt.attachementData = data;
			GameInstance.GetPersistencySystem( GetOwner().GetGame() ).QueuePSEvent( parentID, 'EntityAttachementComponentPS', evt );
		}
	}

	private function AttachChild( const data : ref< EntityAttachementData > )
	{
		var childEntity : weak< GameObject >;
		if( EntityID.IsDefined( data.ownerID ) )
		{
			childEntity = ( ( GameObject )( GameInstance.FindEntityByID( GetOwner().GetGame(), data.ownerID ) ) );
		}
		if( childEntity != NULL )
		{
			EntityGameInterface.BindToComponent( childEntity.GetEntity(), GetOwner().GetEntity(), data.slotComponentName, data.slotName, true );
		}
	}

	protected event OnChildAttachementRequest( evt : EntityAttachementRequestEvent )
	{
		AttachChild( evt.attachementData );
	}

}

