enum MessengerContactType : Uint8
{
	SingleThread = 0,
	MultiThread = 1,
	Contact = 2,
	Fake_ShowAll = 3,
}

class MessengerContactSelectedEvent extends Event
{
	var m_type : MessengerContactType;
	var m_entryHash : Int32;
	var m_level : Int32;
}

class MessengerContactSyncBackEvent extends Event
{
}

class MessengerThreadSelectedEvent extends Event
{
	var m_hash : Uint32;
}

class MessengerContactSyncData extends IScriptable
{
	var m_type : MessengerContactType;
	var m_entryHash : Int32;
	var m_level : Int32;
}

class MessengerForceSelectionEvent extends Event
{
	var m_selectionIndex : Int32;
	var m_hash : Int32;
	var m_toggle : Bool;
}

class SimpleMessengerContactDataView extends ScriptableDataView
{
	private var m_compareBuilder : CompareBuilder;

	public function Setup()
	{
		m_compareBuilder = CompareBuilder.Make();
	}

	public override function SortItem( left : IScriptable, right : IScriptable ) : Bool
	{
		var leftData, rightData : ContactData;
		leftData = ( ( ContactData )( left ) );
		rightData = ( ( ContactData )( right ) );
		m_compareBuilder.Reset();
		return m_compareBuilder.BoolTrue( leftData.questRelated, rightData.questRelated ).BoolTrue( leftData.unreadMessages.Size() > 0, rightData.unreadMessages.Size() > 0 ).GameTimeDesc( leftData.timeStamp, rightData.timeStamp ).GetBool();
	}

}

class SimpleMessengerContactsVirtualListController extends inkVirtualListController
{
	private var m_dataView : SimpleMessengerContactDataView;
	protected var m_dataSource : ScriptableDataSource;
	protected var m_classifier : QuestListVirtualTemplateClassifier;

	protected event OnInitialize()
	{
		m_dataView = new SimpleMessengerContactDataView;
		m_dataSource = new ScriptableDataSource;
		m_classifier = new QuestListVirtualTemplateClassifier;
		m_dataView.Setup();
		m_dataView.SetSource( m_dataSource );
		SetSource( m_dataView );
		SetClassifier( m_classifier );
	}

	protected event OnUninitialize()
	{
		m_dataView.SetSource( NULL );
		SetSource( NULL );
		SetClassifier( NULL );
		m_dataSource = NULL;
	}

	public function GetDataView() : ScriptableDataView
	{
		var view : SimpleMessengerContactDataView;
		view = new SimpleMessengerContactDataView;
		m_dataView = view;
		return view;
	}

	public function GetIndexByJournalHash( hash : Int32 ) : Int32
	{
		var i : Int32;
		var dataSize : Int32;
		var currentContactData : weak< ContactData >;
		if( m_dataView == NULL )
		{
			return -1;
		}
		dataSize = ( ( Int32 )( m_dataView.Size() ) );
		for( i = 0; i < dataSize; i = i + 1 )
		{
			currentContactData = ( ( ContactData )( m_dataView.GetItem( ( ( Uint32 )( i ) ) ) ) );
			if( currentContactData.hash == ( ( Int32 )( hash ) ) )
			{
				return i;
			}
		}
		return -1;
	}

	public virtual function EnableSorting()
	{
		m_dataView.EnableSorting();
	}

	public virtual function DisableSorting()
	{
		m_dataView.DisableSorting();
	}

	public virtual function SetData( const data : ref< array< IScriptable > >, optional sortOnce : Bool )
	{
		m_dataSource.Reset( data );
		EnableSorting();
		if( sortOnce )
		{
			DisableSorting();
		}
	}

	public virtual function GetDataSize() : Int32
	{
		return ( ( Int32 )( m_dataView.Size() ) );
	}

}

class MessengerGameController extends gameuiMenuGameController
{
	private editable var m_buttonHintsManagerRef : inkWidgetRef;
	private editable var m_contactsRef : inkWidgetRef;
	private editable var m_dialogRef : inkWidgetRef;
	private var m_buttonHintsController : weak< ButtonHints >;
	private var m_dialogController : weak< MessengerDialogViewController >;
	private var m_listController : weak< SimpleMessengerContactsVirtualListController >;
	private var m_journalManager : weak< JournalManager >;
	private var m_menuEventDispatcher : weak< inkMenuEventDispatcher >;
	private var m_activeData : MessengerContactSyncData;

	protected event OnInitialize()
	{
		var hintsWidget : weak< inkWidget >;
		hintsWidget = SpawnFromExternal( inkWidgetRef.Get( m_buttonHintsManagerRef ), R"base\gameplay\gui\common\buttonhints.inkwidget", 'Root' );
		m_buttonHintsController = ( ( ButtonHints )( hintsWidget.GetController() ) );
		m_buttonHintsController.AddButtonHint( 'back', GetLocalizedText( "Common-Access-Close" ) );
		m_dialogController = ( ( MessengerDialogViewController )( inkWidgetRef.GetController( m_dialogRef ) ) );
		m_listController = ( ( SimpleMessengerContactsVirtualListController )( inkWidgetRef.GetController( m_contactsRef ) ) );
		m_activeData = new MessengerContactSyncData;
		PlayLibraryAnimation( 'contacts_intro' );
	}

	protected event OnUninitialize()
	{
		m_menuEventDispatcher.UnregisterFromEvent( 'OnBack', this, 'OnBack' );
	}

	protected event OnPlayerAttach( playerPuppet : GameObject )
	{
		m_journalManager = GameInstance.GetJournalManager( GetPlayerControlledObject().GetGame() );
		m_dialogController.AttachJournalManager( m_journalManager );
		m_dialogController.InitDelaySystem( GetPlayerControlledObject() );
		PopulateData();
		m_journalManager.RegisterScriptCallback( this, 'OnJournalUpdate', gameJournalListenerType.Visited );
	}

	protected event OnPlayerDetach( playerPuppet : GameObject )
	{
		m_dialogController.DetachJournalManager();
		m_journalManager.UnregisterScriptCallback( this, 'OnJournalUpdate' );
	}

	protected event OnSetMenuEventDispatcher( menuEventDispatcher : weak< inkMenuEventDispatcher > )
	{
		m_menuEventDispatcher = menuEventDispatcher;
		m_menuEventDispatcher.RegisterToEvent( 'OnBack', this, 'OnBack' );
	}

	protected event OnJournalUpdate( entryHash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		var selectedEvent : MessengerThreadSelectedEvent;
		var cashSelectedIdx : Uint32;
		if( ( className == 'gameJournalPhoneMessage' || className == 'gameJournalPhoneChoiceGroup' ) || className == 'gameJournalPhoneChoiceEntry' )
		{
			cashSelectedIdx = m_listController.GetToggledIndex();
			m_listController.SelectItem( cashSelectedIdx );
			m_listController.ToggleItem( cashSelectedIdx );
			ForceSelectIndex( cashSelectedIdx );
			selectedEvent = new MessengerThreadSelectedEvent;
			selectedEvent.m_hash = entryHash;
			QueueEvent( selectedEvent );
		}
	}

	protected event OnBack( userData : IScriptable )
	{
		if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayerControlledObject(), 'LockInHubMenu' ) ) )
		{
			m_menuEventDispatcher.SpawnEvent( 'OnCloseHubMenu' );
		}
	}

	protected event OnMessengerGameControllerDelayInit( evt : MessengerForceSelectionEvent )
	{
		var entry : weak< JournalEntry >;
		var contactEntry : weak< JournalContact >;
		var threadEntry : weak< JournalPhoneConversation >;
		var contactHash : Int32;
		var locatedIndex : Int32;
		var newIndex : Uint32;
		contactHash = -1;
		newIndex = 0;
		if( evt.m_selectionIndex != -1 )
		{
			newIndex = ( ( Uint32 )( evt.m_selectionIndex ) );
		}
		else
		{
			entry = m_journalManager.GetEntry( ( ( Uint32 )( evt.m_hash ) ) );
			threadEntry = ( ( JournalPhoneConversation )( entry ) );
			if( threadEntry != NULL )
			{
				contactEntry = ( ( JournalContact )( m_journalManager.GetParentEntry( threadEntry ) ) );
				contactHash = m_journalManager.GetEntryHash( contactEntry );
			}
			locatedIndex = m_listController.GetIndexByJournalHash( evt.m_hash );
			if( ( locatedIndex == -1 ) && ( contactHash != -1 ) )
			{
				locatedIndex = m_listController.GetIndexByJournalHash( contactHash );
			}
			if( locatedIndex != -1 )
			{
				newIndex = ( ( Uint32 )( locatedIndex ) );
			}
		}
		m_listController.SelectItem( newIndex );
		if( evt.m_toggle )
		{
			m_listController.ToggleItem( newIndex );
		}
	}

	protected event OnSetUserData( userData : IScriptable )
	{
		var linkData : MessageMenuAttachmentData;
		linkData = ( ( MessageMenuAttachmentData )( userData ) );
		if( linkData )
		{
			ForceSelectEntry( linkData.m_entryHash );
		}
		else
		{
			ForceSelectIndex( 0, true );
		}
	}

	private function ForceSelectIndex( idx : Uint32, optional dontToggle : Bool )
	{
		var initEvent : MessengerForceSelectionEvent;
		initEvent = new MessengerForceSelectionEvent;
		initEvent.m_selectionIndex = ( ( Int32 )( idx ) );
		initEvent.m_toggle = !( dontToggle );
		QueueEvent( initEvent );
	}

	private function ForceSelectEntry( hash : Int32, optional dontToggle : Bool )
	{
		var initEvent : MessengerForceSelectionEvent;
		initEvent = new MessengerForceSelectionEvent;
		initEvent.m_selectionIndex = -1;
		initEvent.m_hash = hash;
		initEvent.m_toggle = !( dontToggle );
		QueueEvent( initEvent );
	}

	private function PopulateData()
	{
		var data : array< IScriptable >;
		data = MessengerUtils.GetSimpleContactDataArray( m_journalManager, true, true, true, m_activeData );
		m_listController.SetData( data, true );
	}

	protected event OnContactActivated( evt : MessengerContactSelectedEvent )
	{
		switch( evt.m_type )
		{
			case MessengerContactType.SingleThread:
				SyncActiveData( evt );
			m_dialogController.ShowDialog( m_journalManager.GetEntry( ( ( Uint32 )( evt.m_entryHash ) ) ) );
			break;
			case MessengerContactType.MultiThread:
				SyncActiveData( evt );
			m_dialogController.ShowThread( m_journalManager.GetEntry( ( ( Uint32 )( evt.m_entryHash ) ) ) );
			break;
			default:
				break;
		}
	}

	private function SyncActiveData( evt : MessengerContactSelectedEvent )
	{
		var syncEvent : MessengerContactSyncBackEvent;
		m_activeData.m_type = evt.m_type;
		m_activeData.m_entryHash = evt.m_entryHash;
		m_activeData.m_level = evt.m_level;
		syncEvent = new MessengerContactSyncBackEvent;
		QueueEvent( syncEvent );
	}

}

