struct QuestUIUtils
{

	public static function GetJournalStateName( state : gameJournalEntryState, isTracked : Bool ) : CName
	{
		switch( state )
		{
			case gameJournalEntryState.Active:
				if( isTracked )
				{
					return 'tracked';
				}
				else
				{
					return 'untracked';
				}
			case gameJournalEntryState.Succeeded:
				return 'succeeded';
			case gameJournalEntryState.Failed:
				return 'failed';
		}
		return 'Default';
	}

	public static function GetEntryTypeFromName( entryTypeName : CName ) : UIObjectiveEntryType
	{
		switch( entryTypeName )
		{
			case 'gameJournalQuest':
				return UIObjectiveEntryType.Quest;
			case 'gameJournalQuestObjective':
				return UIObjectiveEntryType.Objective;
			case 'gameJournalQuestSubObjective':
				return UIObjectiveEntryType.SubObjective;
			default:
				return UIObjectiveEntryType.Invalid;
		}
	}

	public static function GetLibraryIDForEntryType( entryType : UIObjectiveEntryType ) : CName
	{
		switch( entryType )
		{
			case UIObjectiveEntryType.Quest:
				return 'QuestEntry';
			case UIObjectiveEntryType.Objective:
				return 'ObjectiveEntry';
			case UIObjectiveEntryType.SubObjective:
				return 'SubObjectiveEntry';
			default:
				return '';
		}
	}

}

class QuestListGameController extends inkHUDGameController
{
	private editable var m_entryList : inkVerticalPanelRef;
	private editable var m_scanPulse : inkCompoundRef;
	private editable var m_optionalHeader : inkWidgetRef;
	private editable var m_toDoHeader : inkWidgetRef;
	private editable var m_optionalList : inkVerticalPanelRef;
	private editable var m_nonOptionalList : inkVerticalPanelRef;
	private var m_entryControllers : inkArray;
	private var m_scanPulseAnimProxy : inkAnimProxy;
	private var m_stateChangesBlackboardId : Uint32;
	private var m_trackedChangesBlackboardId : Uint32;
	private var m_JournalWrapper : JournalWrapper;
	private var m_player : weak< GameObject >;
	private var m_optionalHeaderController : weak< QuestListHeaderLogicController >;
	private var m_toDoHeaderController : weak< QuestListHeaderLogicController >;
	private var m_lastNonOptionalObjective : QuestObjectiveWrapper;

	protected event OnInitialize()
	{
		var ownerEntity : weak< GameObject >;
		var gameInstance : GameInstance;
		ownerEntity = ( ( GameObject )( GetOwnerEntity() ) );
		gameInstance = ( ( GameInstance )( ownerEntity.GetGame() ) );
		m_JournalWrapper = new JournalWrapper;
		m_JournalWrapper.Init( gameInstance );
		m_JournalWrapper.GetJournalManager().RegisterScriptCallback( this, 'OnStateChanges', gameJournalListenerType.State );
		m_JournalWrapper.GetJournalManager().RegisterScriptCallback( this, 'OnTrackedEntryChanges', gameJournalListenerType.Tracked );
		m_JournalWrapper.GetJournalManager().RegisterScriptCallback( this, 'OnCounterChanged', gameJournalListenerType.Counter );
		m_JournalWrapper.GetJournalManager().RegisterScriptCallback( this, 'OnObjectiveIsOptionalChanged', gameJournalListenerType.ObjectiveOptional );
		m_entryControllers = new inkArray;
		m_player = ( ( GameObject )( GetOwnerEntity() ) );
		m_player.RegisterInputListener( this, 'VisionPush' );
		m_player.RegisterInputListener( this, 'UI_DPadDown' );
		m_optionalHeaderController = ( ( QuestListHeaderLogicController )( inkWidgetRef.GetController( m_optionalHeader ) ) );
		m_optionalHeaderController.SetLabel( "UI-Cyberpunk-HUD-QuestList-optional" );
		inkWidgetRef.SetVisible( m_optionalHeader, false );
		m_toDoHeaderController = ( ( QuestListHeaderLogicController )( inkWidgetRef.GetController( m_toDoHeader ) ) );
		m_toDoHeaderController.SetLabel( "UI-Cyberpunk-HUD-QuestList-toDo" );
		inkWidgetRef.SetVisible( m_toDoHeader, false );
		UpdateEntries();
	}

	protected event OnUninitialize()
	{
		if( m_JournalWrapper.GetJournalManager() )
		{
			m_JournalWrapper.GetJournalManager().UnregisterScriptCallback( this, 'OnStateChanges' );
			m_JournalWrapper.GetJournalManager().UnregisterScriptCallback( this, 'OnTrackedEntryChanges' );
			m_JournalWrapper.GetJournalManager().UnregisterScriptCallback( this, 'OnCounterChanged' );
			m_JournalWrapper.GetJournalManager().UnregisterScriptCallback( this, 'OnObjectiveIsOptionalChanged' );
		}
	}

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

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

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

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

	private function UpdateEntries()
	{
		var i, limit, oldEntriesNum : Int32;
		var listQuests : array< weak< JournalEntry > >;
		var entryControllers : array< weak< IScriptable > >;
		var entryController : ObjectiveEntryLogicController;
		entryControllers = m_entryControllers.Get();
		for( i = 0, oldEntriesNum = entryControllers.Size(); i < oldEntriesNum; i += 1 )
		{
			entryController = ( ( ObjectiveEntryLogicController )( entryControllers[ i ] ) );
			entryController.SetUpdated( false );
		}
		m_JournalWrapper.GetQuests( listQuests );
		for( i = 0, limit = listQuests.Size(); i < limit; i += 1 )
		{
			UpdateQuest( m_JournalWrapper.BuildQuestData( ( ( JournalQuest )( listQuests[ i ] ) ) ) );
		}
		entryControllers = m_entryControllers.Get();
		for( i = 0, limit = entryControllers.Size(); i < limit; i += 1 )
		{
			entryController = ( ( ObjectiveEntryLogicController )( entryControllers[ i ] ) );
			if( !( entryController.IsUpdated() ) )
			{
				entryController.Hide();
			}
		}
	}

	private function UpdateQuest( questData : QuestDataWrapper ) : Bool
	{
		var entryController : weak< ObjectiveEntryLogicController >;
		var trackedQuest : Bool;
		var isOptional : Bool;
		var hasOptionalObjectives : Bool;
		trackedQuest = questData.IsTracked() || questData.IsTrackedInHierarchy();
		if( trackedQuest )
		{
			entryController = GetOrCreateEntry( questData.GetUniqueId(), UIObjectiveEntryType.Quest, NULL );
			if( entryController )
			{
				entryController.SetEntryData( BuildEntryData( questData ) );
				isOptional = UpdateObjectives( questData, entryController, trackedQuest );
				if( !( hasOptionalObjectives ) && isOptional )
				{
					hasOptionalObjectives = true;
				}
			}
		}
		else
		{
			entryController = FindEntry( questData.GetUniqueId() );
			if( entryController )
			{
				entryController.SetEntryData( BuildEntryData( questData ) );
				isOptional = UpdateObjectives( questData, entryController, trackedQuest );
				if( !( hasOptionalObjectives ) && isOptional )
				{
					hasOptionalObjectives = true;
				}
			}
			if( entryController.GetEntryState() != gameJournalEntryState.Succeeded && entryController.GetEntryState() != gameJournalEntryState.Failed )
			{
				entryController.Hide();
			}
		}
		return hasOptionalObjectives;
	}

	private function UpdateObjectives( questData : QuestDataWrapper, parent : weak< ObjectiveEntryLogicController >, isParentTracked : Bool ) : Bool
	{
		var questObjectives : array< QuestObjectiveWrapper >;
		var i, limit : Int32;
		var hasOptionalObjectives : Bool;
		var journalManager : weak< JournalManager >;
		var isOptional : Bool;
		hasOptionalObjectives = false;
		journalManager = m_JournalWrapper.GetJournalManager();
		isOptional = false;
		questObjectives = questData.GetObjectives();
		for( i = 0, limit = questObjectives.Size(); i < limit; i += 1 )
		{
			UpdateObjective( questObjectives[ i ], parent, isParentTracked );
			isOptional = journalManager.GetIsObjectiveOptional( questObjectives[ i ].GetQuestObjective() );
			if( !( hasOptionalObjectives ) && isOptional )
			{
				hasOptionalObjectives = true;
			}
		}
		return hasOptionalObjectives;
	}

	private function UpdateObjective( objectiveData : QuestObjectiveWrapper, parent : weak< ObjectiveEntryLogicController >, isParentTracked : Bool )
	{
		var entryController : weak< ObjectiveEntryLogicController >;
		var entryState : gameJournalEntryState;
		var journalManager : weak< JournalManager >;
		journalManager = m_JournalWrapper.GetJournalManager();
		if( isParentTracked && objectiveData.GetStatus() == gameJournalEntryState.Active )
		{
			entryController = GetOrCreateEntry( objectiveData.GetUniqueId(), UIObjectiveEntryType.Objective, parent, journalManager.GetIsObjectiveOptional( objectiveData.GetQuestObjective() ) );
		}
		else
		{
			entryController = FindEntry( objectiveData.GetUniqueId() );
		}
		if( entryController )
		{
			entryController.SetEntryData( BuildEntryData( objectiveData ) );
			UpdateSubObjectives( objectiveData, entryController, objectiveData.IsTracked() || objectiveData.IsTrackedInHierarchy() );
			entryState = entryController.GetEntryState();
			if( ( !( isParentTracked ) && entryState != gameJournalEntryState.Succeeded ) && entryState != gameJournalEntryState.Failed )
			{
				entryController.Hide();
			}
		}
	}

	private function UpdateSubObjectives( questData : QuestObjectiveWrapper, parent : weak< ObjectiveEntryLogicController >, isParentTracked : Bool )
	{
		var questSubObjectives : array< QuestSubObjectiveWrapper >;
		var i, limit : Int32;
		questSubObjectives = questData.GetSubObjectives();
		for( i = 0, limit = questSubObjectives.Size(); i < limit; i += 1 )
		{
			UpdateSubObjective( questSubObjectives[ i ], parent, isParentTracked );
		}
	}

	private function UpdateSubObjective( subObjectiveData : QuestSubObjectiveWrapper, parent : weak< ObjectiveEntryLogicController >, isParentTracked : Bool )
	{
		var entryController : weak< ObjectiveEntryLogicController >;
		if( isParentTracked && subObjectiveData.GetStatus() == gameJournalEntryState.Active )
		{
			entryController = GetOrCreateEntry( subObjectiveData.GetUniqueId(), UIObjectiveEntryType.SubObjective, parent );
		}
		else
		{
			entryController = FindEntry( subObjectiveData.GetUniqueId() );
		}
		if( entryController )
		{
			entryController.SetEntryData( BuildEntryData( subObjectiveData ) );
			if( ( !( isParentTracked ) && entryController.GetEntryState() != gameJournalEntryState.Succeeded ) && entryController.GetEntryState() != gameJournalEntryState.Failed )
			{
				entryController.Hide();
			}
		}
	}

	private function FindEntry( entryId : Int32 ) : weak< ObjectiveEntryLogicController >
	{
		var entryControllers : array< weak< IScriptable > >;
		var totalControllers : Int32;
		var i : Int32;
		var entryController : weak< ObjectiveEntryLogicController >;
		entryControllers = m_entryControllers.Get();
		totalControllers = entryControllers.Size();
		for( i = 0; i < totalControllers; i += 1 )
		{
			entryController = ( ( ObjectiveEntryLogicController )( entryControllers[ i ] ) );
			if( entryController.GetEntryId() == entryId )
			{
				return entryController;
			}
		}
		return NULL;
	}

	private function GetOrCreateEntry( id : Int32, entryType : UIObjectiveEntryType, parent : weak< ObjectiveEntryLogicController >, optional isOptional : Bool ) : weak< ObjectiveEntryLogicController >
	{
		var entryWidget : weak< inkWidget >;
		var entryController : weak< ObjectiveEntryLogicController >;
		var libraryID : CName;
		var entryIndex : Int32;
		var list : inkVerticalPanelRef;
		libraryID = QuestUIUtils.GetLibraryIDForEntryType( entryType );
		entryController = FindEntry( id );
		if( entryController == NULL )
		{
			if( isOptional )
			{
				list = m_optionalList;
			}
			else
			{
				list = m_nonOptionalList;
			}
			entryWidget = SpawnFromLocal( inkVerticalPanelRef.Get( list ), libraryID );
			entryWidget.SetHAlign( inkEHorizontalAlign.Right );
			entryController = ( ( ObjectiveEntryLogicController )( entryWidget.GetController() ) );
			entryController.SetEntryId( id );
			entryController.RegisterToCallback( 'OnReadyToRemove', this, 'OnRemoveEntry' );
			if( entryType != UIObjectiveEntryType.SubObjective || ( parent == NULL ) )
			{
				m_entryControllers.PushBack( entryController );
			}
			else
			{
				entryIndex = FindNewEntryIndex( entryType, parent );
				if( entryIndex > -1 )
				{
					inkVerticalPanelRef.ReorderChild( list, entryWidget, entryIndex );
					m_entryControllers.InsertAt( ( ( Uint32 )( entryIndex ) ), entryController );
				}
				else
				{
					m_entryControllers.PushBack( entryController );
				}
				entryController.AttachToParent( parent );
			}
		}
		return entryController;
	}

	private function FindNewEntryIndex( entryType : UIObjectiveEntryType, parent : ObjectiveEntryLogicController ) : Int32
	{
		var entryControllers : array< weak< IScriptable > >;
		var totalControllers : Int32;
		var i : Int32;
		var currEntryController : weak< ObjectiveEntryLogicController >;
		var foundParent : Bool;
		foundParent = false;
		entryControllers = m_entryControllers.Get();
		totalControllers = entryControllers.Size();
		for( i = 0; i < totalControllers; i += 1 )
		{
			currEntryController = ( ( ObjectiveEntryLogicController )( entryControllers[ i ] ) );
			if( !( foundParent ) )
			{
				foundParent = currEntryController.GetEntryId() == parent.GetEntryId();
			}
			else
			{
				if( currEntryController.GetEntryType() != entryType )
				{
					return i;
				}
			}
		}
		return -1;
	}

	protected event OnRemoveEntry( entryWidget : weak< inkWidget > )
	{
		var entryController : weak< ObjectiveEntryLogicController >;
		entryController = ( ( ObjectiveEntryLogicController )( entryWidget.GetController() ) );
		if( entryController.IsReadyToRemove() )
		{
			entryController.DetachFromParent();
			RemoveEntry( entryWidget );
			m_entryControllers.Remove( entryController );
		}
	}

	private function RemoveEntry( entryWidget : weak< inkWidget > )
	{
		var i : Int32;
		var entryController : weak< ObjectiveEntryLogicController >;
		var tempInkWidget : weak< inkWidget >;
		var tempController : weak< ObjectiveEntryLogicController >;
		entryController = ( ( ObjectiveEntryLogicController )( entryWidget.GetController() ) );
		for( i = 0; i < inkVerticalPanelRef.GetNumChildren( m_optionalList ); i += 1 )
		{
			tempInkWidget = inkVerticalPanelRef.GetWidgetByIndex( m_optionalList, i );
			tempController = ( ( ObjectiveEntryLogicController )( tempInkWidget.GetController() ) );
			if( tempController == entryController )
			{
				inkVerticalPanelRef.RemoveChild( m_optionalList, entryWidget );
				break;
			}
		}
		for( i = 0; i < inkVerticalPanelRef.GetNumChildren( m_nonOptionalList ); i += 1 )
		{
			tempInkWidget = inkVerticalPanelRef.GetWidgetByIndex( m_nonOptionalList, i );
			tempController = ( ( ObjectiveEntryLogicController )( tempInkWidget.GetController() ) );
			if( tempController == entryController )
			{
				inkVerticalPanelRef.RemoveChild( m_nonOptionalList, entryWidget );
				break;
			}
		}
	}

	private function BuildEntryData( inData : ABaseWrapper ) : UIObjectiveEntryData
	{
		var outData : UIObjectiveEntryData;
		var questData : QuestDataWrapper;
		var objectiveData : QuestObjectiveWrapper;
		var subObjectiveData : QuestSubObjectiveWrapper;
		var journalManager : weak< JournalManager >;
		journalManager = m_JournalWrapper.GetJournalManager();
		questData = ( ( QuestDataWrapper )( inData ) );
		if( questData )
		{
			outData.m_name = questData.GetTitle();
			outData.m_counter = "";
			outData.m_isTracked = questData.IsTracked();
			outData.m_type = UIObjectiveEntryType.Quest;
			outData.m_state = questData.GetStatus();
			outData.m_isOptional = questData.IsOptional();
		}
		else
		{
			objectiveData = ( ( QuestObjectiveWrapper )( inData ) );
			if( objectiveData )
			{
				outData.m_name = objectiveData.GetDescription();
				outData.m_counter = objectiveData.GetCounterText();
				outData.m_isTracked = objectiveData.IsTracked();
				outData.m_type = UIObjectiveEntryType.Objective;
				outData.m_state = objectiveData.GetStatus();
				outData.m_isOptional = questData.IsOptional() || journalManager.GetIsObjectiveOptional( objectiveData.GetQuestObjective() );
			}
			else
			{
				subObjectiveData = ( ( QuestSubObjectiveWrapper )( inData ) );
				if( subObjectiveData )
				{
					outData.m_name = subObjectiveData.GetDescription();
					outData.m_isTracked = subObjectiveData.IsTracked();
					outData.m_isTracked = subObjectiveData.IsTracked();
					outData.m_type = UIObjectiveEntryType.SubObjective;
					outData.m_state = subObjectiveData.GetStatus();
					outData.m_isOptional = questData.IsOptional();
				}
			}
		}
		return outData;
	}

	private function ShouldDisplayEntry( entryType : UIObjectiveEntryType ) : Bool
	{
		return entryType != UIObjectiveEntryType.Invalid;
	}

	protected event OnAction( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		var actionName : CName;
		var actionType : gameinputActionType;
		actionName = ListenerAction.GetName( action );
		actionType = ListenerAction.GetType( action );
		if( actionName == 'VisionPush' && actionType == gameinputActionType.BUTTON_PRESSED )
		{
			if( m_scanPulseAnimProxy.IsPlaying() )
			{
				m_scanPulseAnimProxy.Stop();
			}
			m_scanPulseAnimProxy = PlayLibraryAnimation( 'ScanPulseAnimation' );
		}
	}

}

class QuestListHeaderLogicController extends inkLogicController
{
	private editable var m_label : inkTextRef;

	public function SetLabel( const text : ref< String > )
	{
		inkTextRef.SetText( m_label, text );
	}

}

