class TrackedQuestPhaseUpdateRequest extends Event
{
}

class QuestTrackerGameController extends inkHUDGameController
{
	private editable var m_questTrackerContainer : inkWidgetRef;
	private editable var m_QuestTitle : inkTextRef;
	private editable var m_ObjectiveContainer : inkCompoundRef;
	private editable var m_TrackedMappinTitle : inkTextRef;
	private editable var m_TrackedMappinContainer : inkWidgetRef;
	private editable var m_TrackedMappinObjectiveContainer : inkCompoundRef;
	private var m_player : weak< GameObject >;
	protected var m_mappinSystem : weak< MappinSystem >;
	protected var m_journalManager : weak< JournalManager >;
	protected var m_bufferedEntry : weak< JournalQuestObjective >;
	protected var m_bufferedPhase : weak< JournalQuestPhase >;
	protected var m_bufferedQuest : weak< JournalQuest >;
	private var m_root : weak< inkWidget >;
	private var blackboard : weak< IBlackboard >;
	private var uiSystemBB : UI_SystemDef;
	private var uiSystemId : CallbackHandle;
	private var trackedMappinId : CallbackHandle;
	private var m_trackedMappinSpawnRequest : weak< inkAsyncSpawnRequest >;
	private var m_currentMappin : weak< IMappin >;

	protected event OnInitialize()
	{
		m_root = GetRootWidget();
		m_player = ( ( GameObject )( GetPlayerControlledObject() ) );
		m_player.RegisterInputListener( this, 'VisionPush' );
		m_player.RegisterInputListener( this, 'UI_DPadDown' );
		m_mappinSystem = GameInstance.GetMappinSystem( m_player.GetGame() );
		m_journalManager = GameInstance.GetJournalManager( m_player.GetGame() );
		inkCompoundRef.RemoveAllChildren( m_ObjectiveContainer );
		UpdateTrackerData();
		m_journalManager.RegisterScriptCallback( this, 'OnStateChanges', gameJournalListenerType.State );
		m_journalManager.RegisterScriptCallback( this, 'OnTrackedEntryChanges', gameJournalListenerType.Tracked );
		m_journalManager.RegisterScriptCallback( this, 'OnCounterChanged', gameJournalListenerType.Counter );
		m_journalManager.RegisterScriptCallback( this, 'OnObjectiveIsOptionalChanged', gameJournalListenerType.ObjectiveOptional );
		blackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_System );
		uiSystemBB = GetAllBlackboardDefs().UI_System;
		uiSystemId = blackboard.RegisterListenerBool( uiSystemBB.IsInMenu, this, 'OnMenuUpdate' );
		trackedMappinId = blackboard.RegisterListenerVariant( uiSystemBB.TrackedMappin, this, 'OnTrackedMappinUpdated' );
		blackboard.SignalBool( uiSystemBB.IsInMenu );
		blackboard.SignalVariant( uiSystemBB.TrackedMappin );
	}

	protected event OnUninitialize() {}

	protected event OnStateChanges( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		var j : Int32;
		var objectiveController : weak< QuestTrackerObjectiveLogicController >;
		var state : gameJournalEntryState;
		if( className != 'gameJournalInternetPage' )
		{
			state = m_journalManager.GetEntryState( m_journalManager.GetEntry( hash ) );
			for( j = 0; j < inkCompoundRef.GetNumChildren( m_ObjectiveContainer ); j += 1 )
			{
				objectiveController = ( ( QuestTrackerObjectiveLogicController )( inkCompoundRef.GetWidgetByIndex( m_ObjectiveContainer, j ).GetController() ) );
				if( QuestTrackerObjectiveLogicController.IsObjectiveEntry() )
				{
					if( m_journalManager.GetEntry( hash ) == objectiveController.GetObjectiveEntry() )
					{
						if( state != gameJournalEntryState.Succeeded && state != gameJournalEntryState.Failed )
						{
							inkCompoundRef.RemoveChildByIndex( m_ObjectiveContainer, j );
							j -= 1;
						}
					}
				}
			}
			UpdateTrackerData();
			state = m_journalManager.GetEntryState( m_journalManager.GetEntry( hash ) );
			for( j = 0; j < inkCompoundRef.GetNumChildren( m_ObjectiveContainer ); j += 1 )
			{
				objectiveController = ( ( QuestTrackerObjectiveLogicController )( inkCompoundRef.GetWidgetByIndex( m_ObjectiveContainer, j ).GetController() ) );
				if( QuestTrackerObjectiveLogicController.IsObjectiveEntry() )
				{
					if( objectiveController.IsReadyToRemove() )
					{
						inkCompoundRef.RemoveChildByIndex( m_ObjectiveContainer, j );
						j -= 1;
						continue;
					}
					if( m_journalManager.GetEntry( hash ) == objectiveController.GetObjectiveEntry() )
					{
						if( state == gameJournalEntryState.Succeeded )
						{
							objectiveController.SetFinished();
						}
						if( state == gameJournalEntryState.Failed )
						{
							objectiveController.SetFailed();
						}
					}
				}
			}
		}
	}

	protected event OnTrackedEntryChanges( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		var j : Int32;
		var objectiveController : weak< QuestTrackerObjectiveLogicController >;
		var state : gameJournalEntryState;
		state = m_journalManager.GetEntryState( m_journalManager.GetEntry( hash ) );
		for( j = 0; j < inkCompoundRef.GetNumChildren( m_ObjectiveContainer ); j += 1 )
		{
			objectiveController = ( ( QuestTrackerObjectiveLogicController )( inkCompoundRef.GetWidgetByIndex( m_ObjectiveContainer, j ).GetController() ) );
			if( QuestTrackerObjectiveLogicController.IsObjectiveEntry() )
			{
				if( m_journalManager.GetEntry( hash ) == objectiveController.GetObjectiveEntry() )
				{
					if( state != gameJournalEntryState.Succeeded && state != gameJournalEntryState.Failed )
					{
						inkCompoundRef.RemoveChildByIndex( m_ObjectiveContainer, j );
						j -= 1;
					}
				}
			}
		}
		UpdateTrackerData();
		state = m_journalManager.GetEntryState( m_journalManager.GetEntry( hash ) );
		for( j = 0; j < inkCompoundRef.GetNumChildren( m_ObjectiveContainer ); j += 1 )
		{
			objectiveController = ( ( QuestTrackerObjectiveLogicController )( inkCompoundRef.GetWidgetByIndex( m_ObjectiveContainer, j ).GetController() ) );
			if( QuestTrackerObjectiveLogicController.IsObjectiveEntry() )
			{
				if( objectiveController.IsReadyToRemove() )
				{
					inkCompoundRef.RemoveChildByIndex( m_ObjectiveContainer, j );
					j -= 1;
					continue;
				}
				if( m_journalManager.GetEntry( hash ) == objectiveController.GetObjectiveEntry() )
				{
					if( state == gameJournalEntryState.Succeeded )
					{
						objectiveController.SetFinished();
					}
					if( state == gameJournalEntryState.Failed )
					{
						objectiveController.SetFailed();
					}
				}
			}
		}
	}

	protected event OnCounterChanged( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		UpdateTrackerData();
	}

	protected event OnObjectiveIsOptionalChanged( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		UpdateTrackerData();
	}

	protected event OnMenuUpdate( value : Bool )
	{
		UpdateTrackerData();
	}

	protected event OnTrackedMappinUpdated( value : Variant )
	{
		m_currentMappin = ( ( weak< weak< IMappin > > )( ( ( IScriptable )value ) ) );
		inkCompoundRef.RemoveAllChildren( m_TrackedMappinObjectiveContainer );
		inkWidgetRef.SetVisible( m_TrackedMappinContainer, ( ( Bool )( m_currentMappin ) ) );
		if( m_trackedMappinSpawnRequest )
		{
			m_trackedMappinSpawnRequest.Cancel();
		}
		if( m_currentMappin )
		{
			m_trackedMappinSpawnRequest = AsyncSpawnFromLocal( inkCompoundRef.Get( m_TrackedMappinObjectiveContainer ), 'Objective', this, 'OnTrackedMappinSpawned' );
		}
	}

	protected event OnTrackedMappinSpawned( newItem : inkWidget, userData : IScriptable )
	{
		var objectiveController : QuestTrackerObjectiveLogicController;
		var mappinText, objectiveText : String;
		mappinText = NameToString( MappinUIUtils.MappinToString( m_currentMappin.GetVariant() ) );
		objectiveText = NameToString( MappinUIUtils.MappinToObjectiveString( m_currentMappin.GetVariant() ) );
		objectiveController = ( ( QuestTrackerObjectiveLogicController )( newItem.GetController() ) );
		objectiveController.SetData( objectiveText, true, false, 0, 0, NULL, true );
		objectiveController.SetState( 'world' );
		inkTextRef.SetText( m_TrackedMappinTitle, mappinText );
		PlayLibraryAnimation( 'TracketMappinIntro' );
	}

	private function SortObjectiveListByTimestamp( sortedObjectives : ref< array< weak< JournalEntry > > > )
	{
		var tempVar : weak< JournalEntry >;
		var i, j : Int32;
		for( i = 0; i < sortedObjectives.Size(); i += 1 )
		{
			for( j = 0; j < ( sortedObjectives.Size() - 1 ); j += 1 )
			{
				if( m_journalManager.GetEntryTimestamp( sortedObjectives[ i ] ) < m_journalManager.GetEntryTimestamp( sortedObjectives[ j + 1 ] ) )
				{
					tempVar = sortedObjectives[ j + 1 ];
					sortedObjectives[ j + 1 ] = sortedObjectives[ i ];
					sortedObjectives[ i ] = tempVar;
				}
			}
		}
	}

	private function UpdateTrackerData()
	{
		var m_trackedEntry : weak< JournalQuestObjective >;
		var m_trackedPhase : weak< JournalQuestPhase >;
		var m_trackedQuest : weak< JournalQuest >;
		var allObjectives : array< weak< JournalEntry > >;
		var allPhases : array< weak< JournalEntry > >;
		var entryWidget : weak< inkWidget >;
		var filter : JournalRequestStateFilter;
		var count : Int32;
		var i, j : Int32;
		var z : Int32;
		var m_objectiveEntry : weak< JournalQuestObjective >;
		var objectiveController : weak< QuestTrackerObjectiveLogicController >;
		var ignoreIntroAnimation : Bool;
		var createNewEntry : Bool;
		var isQuestType : Bool;
		var questType : gameJournalQuestType;
		filter.active = true;
		inkWidgetRef.SetVisible( m_questTrackerContainer, false );
		inkTextRef.SetText( m_QuestTitle, "" );
		m_trackedEntry = ( ( JournalQuestObjective )( m_journalManager.GetTrackedEntry() ) );
		if( m_trackedEntry != NULL )
		{
			m_trackedPhase = ( ( JournalQuestPhase )( m_journalManager.GetParentEntry( m_trackedEntry ) ) );
			if( m_trackedPhase != NULL )
			{
				m_trackedQuest = ( ( JournalQuest )( m_journalManager.GetParentEntry( m_trackedPhase ) ) );
				if( m_trackedQuest != NULL )
				{
					ignoreIntroAnimation = false;
					if( ( m_bufferedQuest != m_trackedQuest ) || ( m_bufferedPhase != m_trackedPhase ) )
					{
						inkCompoundRef.RemoveAllChildren( m_ObjectiveContainer );
						ignoreIntroAnimation = true;
					}
					m_journalManager.GetChildren( m_trackedQuest, filter, allPhases );
					inkWidgetRef.SetVisible( m_questTrackerContainer, m_trackedQuest.GetTitle( m_journalManager ) != "" );
					inkTextRef.SetText( m_QuestTitle, m_trackedQuest.GetTitle( m_journalManager ) );
					questType = m_journalManager.GetQuestType( m_trackedQuest );
					isQuestType = ( ( questType == gameJournalQuestType.MainQuest || questType == gameJournalQuestType.SideQuest ) || questType == gameJournalQuestType.CourierSideQuest ) || questType == gameJournalQuestType.MinorQuest;
					inkTextRef.SetState( m_QuestTitle, ( ( isQuestType ) ? ( 'Quest' ) : ( 'Gigs' ) ) );
					for( z = 0; z < allPhases.Size(); z += 1 )
					{
						m_journalManager.GetChildren( allPhases[ z ], filter, allObjectives );
						SortObjectiveListByTimestamp( allObjectives );
						count = allObjectives.Size();
						for( i = 0; i < count; i += 1 )
						{
							m_objectiveEntry = ( ( JournalQuestObjective )( allObjectives[ i ] ) );
							if( m_objectiveEntry != NULL )
							{
								m_bufferedEntry = m_trackedEntry;
								m_bufferedPhase = m_trackedPhase;
								m_bufferedQuest = m_trackedQuest;
								createNewEntry = true;
								for( j = 0; j < inkCompoundRef.GetNumChildren( m_ObjectiveContainer ); j += 1 )
								{
									objectiveController = ( ( QuestTrackerObjectiveLogicController )( inkCompoundRef.GetWidgetByIndex( m_ObjectiveContainer, j ).GetController() ) );
									if( objectiveController.GetObjectiveEntry() == m_objectiveEntry )
									{
										objectiveController.SetData( m_objectiveEntry.GetDescription(), m_objectiveEntry == m_trackedEntry, m_journalManager.GetIsObjectiveOptional( m_objectiveEntry ), m_journalManager.GetObjectiveCurrentCounter( m_objectiveEntry ), m_journalManager.GetObjectiveTotalCounter( m_objectiveEntry ), m_objectiveEntry, isQuestType );
										createNewEntry = false;
									}
								}
								if( createNewEntry && m_journalManager.GetEntryState( m_objectiveEntry ) == gameJournalEntryState.Active )
								{
									entryWidget = SpawnFromLocal( inkCompoundRef.Get( m_ObjectiveContainer ), 'Objective' );
									objectiveController = ( ( QuestTrackerObjectiveLogicController )( entryWidget.GetController() ) );
									objectiveController.SetData( m_objectiveEntry.GetDescription(), m_objectiveEntry == m_trackedEntry, m_journalManager.GetIsObjectiveOptional( m_objectiveEntry ), m_journalManager.GetObjectiveCurrentCounter( m_objectiveEntry ), m_journalManager.GetObjectiveTotalCounter( m_objectiveEntry ), m_objectiveEntry, isQuestType );
									if( !( ignoreIntroAnimation ) )
									{
										objectiveController.PlayIntroAnim();
										PlaySound( 'MapPin', 'OnCreate' );
									}
								}
							}
						}
					}
				}
			}
		}
	}

	protected event OnTrackedQuestPhaseUpdateRequest( evt : TrackedQuestPhaseUpdateRequest )
	{
		UpdateTrackerData();
	}

}

class QuestTrackerObjectiveLogicController extends inkLogicController
{
	private editable var m_objectiveTitle : inkTextRef;
	private editable var m_trackingIcon : inkWidgetRef;
	private editable var m_trackingFrame : inkWidgetRef;
	private var m_objectiveEntry : weak< JournalQuestObjective >;
	private var m_AnimProxy : inkAnimProxy;
	private var m_IntroAnimProxy : inkAnimProxy;
	private var m_AnimOptions : inkAnimOptions;
	private var readyToRemove : Bool;

	protected event OnInitialize()
	{
		m_AnimOptions.playReversed = false;
		m_AnimOptions.executionDelay = 0.0;
		m_AnimOptions.loopType = inkanimLoopType.None;
		m_AnimOptions.loopInfinite = false;
	}

	protected event OnUninitialize() {}

	private function SetObjectiveState( state : CName )
	{
		inkTextRef.SetState( m_objectiveTitle, state );
		inkWidgetRef.SetState( m_trackingFrame, state );
		inkWidgetRef.SetState( m_trackingIcon, state );
	}

	public function PlayIntroAnim()
	{
		m_IntroAnimProxy = PlayLibraryAnimation( 'ObjectiveIntro' );
		m_IntroAnimProxy.RegisterToCallback( inkanimEventType.OnEndLoop, this, 'OnIntroAnimEnd' );
	}

	public function IsReadyToRemove() : Bool
	{
		return readyToRemove;
	}

	public constexpr static function IsObjectiveEntry() : Bool
	{
		return true;
	}

	public function GetObjectiveEntry() : weak< JournalQuestObjective >
	{
		return m_objectiveEntry;
	}

	public function SetData( const objectiveTitle : ref< String >, isTracked : Bool, isOptional : Bool, currentCounter, totalCounter : Int32, objectiveEntry : weak< JournalQuestObjective >, isQuestType : Bool )
	{
		var state : CName;
		var finalTitle : String;
		var itemID : TweakDBID;
		var itemRecord : Item_Record;
		m_objectiveEntry = objectiveEntry;
		finalTitle = objectiveTitle;
		if( totalCounter > 0 )
		{
			finalTitle = ( ( ( ( GetLocalizedText( finalTitle ) + " [" ) + IntToString( currentCounter ) ) + "/" ) + IntToString( totalCounter ) ) + "]";
		}
		if( isOptional )
		{
			finalTitle = ( ( GetLocalizedText( finalTitle ) + " [" ) + GetLocalizedText( "UI-ScriptExports-Optional0" ) ) + "]";
		}
		itemID = m_objectiveEntry.GetItemID();
		if( TDBID.IsValid( itemID ) )
		{
			itemRecord = TweakDBInterface.GetItemRecord( itemID );
			finalTitle += ( ( GetLocalizedText( "Common-Characters-Semicolon" ) + " " ) + GetLocalizedText( NameToString( itemRecord.DisplayName() ) ) );
		}
		inkTextRef.SetText( m_objectiveTitle, finalTitle );
		inkWidgetRef.SetVisible( m_trackingIcon, isTracked );
		if( isTracked )
		{
			state = ( ( isQuestType ) ? ( 'tracked_quest' ) : ( 'Gigs' ) );
		}
		else
		{
			state = 'untracked';
		}
		SetObjectiveState( state );
	}

	public function SetState( state : CName )
	{
		SetObjectiveState( state );
	}

	protected event OnIntroAnimEnd( proxy : inkAnimProxy )
	{
		GetRootWidget().SetVisible( true );
		m_IntroAnimProxy.Stop();
		m_IntroAnimProxy.UnregisterFromCallback( inkanimEventType.OnEndLoop, this, 'OnIntroAnimEnd' );
	}

	protected event OnAnimEnd( proxy : inkAnimProxy )
	{
		var evt : TrackedQuestPhaseUpdateRequest;
		evt = new TrackedQuestPhaseUpdateRequest;
		GetRootWidget().SetVisible( false );
		m_AnimProxy.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnAnimEnd' );
		readyToRemove = true;
		QueueEvent( evt );
	}

	public function SetFinished()
	{
		if( !( readyToRemove ) )
		{
			SetObjectiveState( 'succeeded' );
			inkWidgetRef.SetVisible( m_trackingIcon, true );
			m_AnimProxy = PlayLibraryAnimation( 'ObjectiveSucceeded' );
			m_AnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnAnimEnd' );
		}
	}

	public function SetFailed()
	{
		if( !( readyToRemove ) )
		{
			SetObjectiveState( 'failed' );
			inkWidgetRef.SetVisible( m_trackingIcon, true );
			m_AnimProxy = PlayLibraryAnimation( 'ObjectiveFailed' );
			m_AnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnAnimEnd' );
		}
	}

}

