struct LinkedFocusClueData
{
	persistent var clueGroupID : CName;
	persistent var ownerID : EntityID;
	persistent var clueIndex : Int32;
	persistent var clueRecord : TweakDBID;
	persistent var extendedClueRecords : array< ClueRecordData >;
	persistent var isScanned : Bool;
	persistent var wasInspected : Bool;
	persistent var isEnabled : Bool;
	persistent var psData : PSOwnerData;
}

class linkedClueUpdateEvent extends Event
{
	var linkedCluekData : LinkedFocusClueData;
	var requesterID : EntityID;
	var updatePS : Bool;
	default updatePS = true;
}

class linkedClueTagEvent extends Event
{
	var tag : Bool;
	var requesterID : EntityID;
}

class TagLinkedCluekRequest extends ScriptableSystemRequest
{
	var tag : Bool;
	var linkedCluekData : LinkedFocusClueData;
}

class RegisterLinkedCluekRequest extends ScriptableSystemRequest
{
	var linkedCluekData : LinkedFocusClueData;
	var forceUpdate : Bool;
}

class UnregisterLinkedCluekRequest extends ScriptableSystemRequest
{
	var linkedCluekData : LinkedFocusClueData;
}

class RegisterActiveClueOwnerkRequest extends ScriptableSystemRequest
{
	var ownerID : EntityID;
}

class UpdateLinkedClueskRequest extends ScriptableSystemRequest
{
	var linkedCluekData : LinkedFocusClueData;
}

class FocusCluesSystem extends ScriptableSystem
{
	private persistent var m_linkedClues : array< LinkedFocusClueData >;
	private persistent var m_disabledGroupes : array< CName >;
	private var m_activeLinkedClue : LinkedFocusClueData;

	private function EnableGroup( groupID : CName )
	{
		m_disabledGroupes.Remove( groupID );
	}

	private function DisableGroup( groupID : CName )
	{
		if( !( m_disabledGroupes.Contains( groupID ) ) )
		{
			m_disabledGroupes.PushBack( groupID );
		}
	}

	private function AddLinkedClue( const clue : ref< LinkedFocusClueData > )
	{
		m_linkedClues.PushBack( clue );
	}

	private function RemoveLinkedClue( const clue : ref< LinkedFocusClueData > )
	{
		var i : Int32;
		for( i = m_linkedClues.Size() - 1; i >= 0; i -= 1 )
		{
			if( ( clue.ownerID == m_linkedClues[ i ].ownerID ) && clue.clueGroupID == m_linkedClues[ i ].clueGroupID )
			{
				m_linkedClues.Erase( i );
				i -= 1;
			}
		}
	}

	private function RemoveLinkedClueByIndex( clueID : Int32 )
	{
		if( ( clueID >= m_linkedClues.Size() ) || ( clueID < 0 ) )
		{
			return;
		}
		m_linkedClues.Erase( clueID );
	}

	private function HasLinkedClue( const clue : ref< LinkedFocusClueData > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( ( clue.ownerID == m_linkedClues[ i ].ownerID ) && clue.clueGroupID == m_linkedClues[ i ].clueGroupID )
			{
				return true;
			}
		}
		return false;
	}

	private function GetLinkedClueGroupData( groupID : CName, clue : ref< LinkedFocusClueData > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( groupID == m_linkedClues[ i ].clueGroupID )
			{
				clue = m_linkedClues[ i ];
				return true;
			}
		}
		return false;
	}

	public const function IsRegistered( ownerID : EntityID, groupID : CName ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( ( m_linkedClues[ i ].ownerID == ownerID ) && groupID == m_linkedClues[ i ].clueGroupID )
			{
				return true;
			}
		}
		return false;
	}

	public const function IsGroupped( ownerID : EntityID, out groupID : CName ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( m_linkedClues[ i ].ownerID == ownerID )
			{
				groupID = m_linkedClues[ i ].clueGroupID;
				return true;
			}
		}
		return false;
	}

	public const function IsGroupDisabled( groupID : CName ) : Bool
	{
		return m_disabledGroupes.Contains( groupID );
	}

	public const function GetClueGroupData( groupID : CName, clue : ref< FocusClueDefinition > ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( groupID == m_linkedClues[ i ].clueGroupID )
			{
				clue.isEnabled = m_linkedClues[ i ].isEnabled;
				clue.wasInspected = m_linkedClues[ i ].wasInspected;
				return true;
			}
		}
		return false;
	}

	private function IsGroupTagged( const clue : ref< LinkedFocusClueData > ) : Bool
	{
		var i : Int32;
		var clueObject : GameObject;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( clue.clueGroupID == m_linkedClues[ i ].clueGroupID )
			{
				clueObject = ( ( GameObject )( GameInstance.FindEntityByID( GetGameInstance(), m_linkedClues[ i ].ownerID ) ) );
				if( IsTagged( clueObject ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	private const function IsTagged( owner : GameObject ) : Bool
	{
		if( owner != NULL )
		{
			return GameInstance.GetVisionModeSystem( owner.GetGame() ).GetScanningController().IsTagged( owner );
		}
		else
		{
			return false;
		}
	}

	private function UpdateLinkedClues( const clue : ref< LinkedFocusClueData > )
	{
		var i : Int32;
		var currentClue : LinkedFocusClueData;
		var disableGroup : Bool;
		m_activeLinkedClue = clue;
		for( i = m_linkedClues.Size() - 1; i >= 0; i -= 1 )
		{
			currentClue = m_linkedClues[ i ];
			if( clue.clueGroupID == currentClue.clueGroupID )
			{
				currentClue.wasInspected = clue.wasInspected;
				currentClue.isScanned = clue.isScanned;
				currentClue.isEnabled = clue.isEnabled;
				if( m_linkedClues[ i ].ownerID != clue.ownerID )
				{
					if( ( m_linkedClues[ i ].wasInspected != currentClue.wasInspected || m_linkedClues[ i ].isScanned != currentClue.isScanned ) || m_linkedClues[ i ].isEnabled != currentClue.isEnabled )
					{
						SendlinkedClueUpdateEvent( currentClue, clue.ownerID );
					}
				}
				m_linkedClues[ i ] = currentClue;
				if( !( m_linkedClues[ i ].isEnabled ) && m_linkedClues[ i ].wasInspected )
				{
					m_linkedClues.Erase( i );
					disableGroup = true;
				}
				else
				{
					disableGroup = false;
				}
			}
		}
		if( disableGroup )
		{
			DisableGroup( clue.clueGroupID );
		}
		else
		{
			EnableGroup( clue.clueGroupID );
		}
	}

	private function UpdateSingleLinkedClue( clue : LinkedFocusClueData )
	{
		var currentClue : LinkedFocusClueData;
		if( GetLinkedClueGroupData( clue.clueGroupID, currentClue ) )
		{
			if( ( clue.wasInspected != currentClue.wasInspected || clue.isScanned != currentClue.isScanned ) || clue.isEnabled != currentClue.isEnabled )
			{
				clue.wasInspected = currentClue.wasInspected;
				clue.isScanned = currentClue.isScanned;
				clue.isEnabled = currentClue.isEnabled;
				SendlinkedClueUpdateEvent( clue, clue.ownerID );
			}
		}
		else
		{
			EnableGroup( clue.clueGroupID );
		}
	}

	private function SendlinkedClueUpdateEvent( const linkedClue : ref< LinkedFocusClueData >, requester : EntityID )
	{
		var clueEvent : linkedClueUpdateEvent;
		if( !( EntityID.IsDefined( linkedClue.ownerID ) ) )
		{
			return;
		}
		clueEvent = new linkedClueUpdateEvent;
		clueEvent.linkedCluekData = linkedClue;
		clueEvent.requesterID = requester;
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueuePSEvent( linkedClue.psData.id, linkedClue.psData.className, clueEvent );
	}

	private function ResolveLinkedCluesTagging( const clue : ref< LinkedFocusClueData >, tag : Bool )
	{
		var i : Int32;
		for( i = 0; i < m_linkedClues.Size(); i += 1 )
		{
			if( clue.clueGroupID == m_linkedClues[ i ].clueGroupID )
			{
				if( m_linkedClues[ i ].ownerID != clue.ownerID )
				{
					SendlinkedClueTagEvent( m_linkedClues[ i ], clue.ownerID, tag );
				}
			}
		}
	}

	private function SendlinkedClueTagEvent( const linkedClue : ref< LinkedFocusClueData >, requester : EntityID, tag : Bool )
	{
		var clueEvent : linkedClueTagEvent;
		clueEvent = new linkedClueTagEvent;
		clueEvent.requesterID = requester;
		clueEvent.tag = tag;
		GameInstance.GetPersistencySystem( GetGameInstance() ).QueueEntityEvent( linkedClue.ownerID, clueEvent );
	}

	private function OnTagLinkedClueRequest( request : TagLinkedCluekRequest )
	{
		ResolveLinkedCluesTagging( request.linkedCluekData, request.tag );
	}

	private export function OnRegisterLinkedClueRequest( request : RegisterLinkedCluekRequest )
	{
		if( !( HasLinkedClue( request.linkedCluekData ) ) )
		{
			AddLinkedClue( request.linkedCluekData );
			UpdateSingleLinkedClue( request.linkedCluekData );
			if( IsGroupTagged( request.linkedCluekData ) )
			{
				SendlinkedClueTagEvent( request.linkedCluekData, request.linkedCluekData.ownerID, true );
			}
		}
	}

	private function OnUnregisterLinkedClueRequest( request : UnregisterLinkedCluekRequest )
	{
		RemoveLinkedClue( request.linkedCluekData );
	}

	private function OnUpdateLinkedCluesRequest( request : UpdateLinkedClueskRequest )
	{
		UpdateLinkedClues( request.linkedCluekData );
	}

	public const function GetActiveLinkedClue() : LinkedFocusClueData
	{
		return m_activeLinkedClue;
	}

	public const function GetActiveLinkedClueScannableData() : array< ScanningTooltipElementDef >
	{
		var objectData : ScanningTooltipElementDef;
		var arr : array< ScanningTooltipElementDef >;
		var i : Int32;
		for( i = 0; i < m_activeLinkedClue.extendedClueRecords.Size(); i += 1 )
		{
			objectData.recordID = m_activeLinkedClue.extendedClueRecords[ i ].clueRecord;
			objectData.timePct = m_activeLinkedClue.extendedClueRecords[ i ].percentage;
			if( TDBID.IsValid( objectData.recordID ) )
			{
				arr.PushBack( objectData );
			}
		}
		if( m_activeLinkedClue.isScanned && TDBID.IsValid( m_activeLinkedClue.clueRecord ) )
		{
			objectData.recordID = m_activeLinkedClue.clueRecord;
			objectData.timePct = 0.0;
			arr.PushBack( objectData );
		}
		return arr;
	}

}

