struct ProgramData
{
	var id : String;
	var commandLists : array< array< ElementData > >;
	var effects : array< ProgramEffect >;
	var type : ProgramType;
	var localizationKey : String;
	var startAsUnknown : Bool;
	var wasCompleted : Bool;
}

enum ProgramEffect
{
	GrantAccess = 0,
	BlockAccess = 1,
	UnlockQuestFact = 2,
}

enum ProgramType
{
	BasicAccess = 0,
	ExtraPlayerProgram = 1,
	ExtraServerProgram = 2,
	EnemyProgram = 3,
	EnemyLockNetwork = 4,
}

struct ProgramProgressData
{
	var id : String;
	var completionProgress : array< Int32 >;
	var isComplete : Bool;
	var revealLocalizedName : Bool;
}

class NetworkMinigameProgramController extends inkLogicController
{
	protected editable var m_text : inkTextRef;
	protected const editable var m_commandElementSlotsContainer : array< inkWidgetRef >;
	protected editable var m_elementLibraryName : CName;
	protected editable var m_completedMarker : inkWidgetRef;
	protected editable var m_imageRef : inkImageRef;
	protected var m_slotList : array< array< weak< NetworkMinigameElementController > > >;
	protected var m_data : ProgramData;
	private var m_animProxy : inkAnimProxy;

	protected event OnInitialize()
	{
		inkWidgetRef.SetVisible( m_completedMarker, false );
		inkTextRef.SetText( m_text, "" );
	}

	public function Spawn( const data : ref< ProgramData > )
	{
		var slotLogic : weak< NetworkMinigameElementController >;
		var slotLogicContent : array< weak< NetworkMinigameElementController > >;
		var slot : weak< inkWidget >;
		var i : Int32;
		var j : Int32;
		m_data = data;
		inkTextRef.SetText( m_text, data.id );
		RefreshImage();
		for( j = 0; j < data.commandLists.Size(); j += 1 )
		{
			slotLogicContent.Clear();
			for( i = 0; i < data.commandLists[ j ].Size(); i += 1 )
			{
				slot = SpawnFromLocal( inkWidgetRef.Get( m_commandElementSlotsContainer[ j ] ), m_elementLibraryName );
				slotLogic = ( ( NetworkMinigameElementController )( slot.GetController() ) );
				slotLogic.SetContent( data.commandLists[ j ][ i ] );
				slotLogicContent.PushBack( slotLogic );
			}
			m_slotList.PushBack( slotLogicContent );
		}
	}

	public function UpdatePartialCompletionState( const progress : ref< ProgramProgressData > )
	{
		if( progress.isComplete )
		{
			if( !( m_data.wasCompleted ) )
			{
				ShowCompleted( progress.revealLocalizedName );
			}
			return;
		}
		SetHighlightedUpUntil( progress.completionProgress );
	}

	private function SetHighlightedUpUntil( const lastHighlighted : ref< array< Int32 > > )
	{
		var i : Int32;
		var j : Int32;
		for( j = 0; j < m_slotList.Size(); j += 1 )
		{
			for( i = 0; i < m_slotList[ j ].Size(); i += 1 )
			{
				m_slotList[ j ][ i ].SetHighlightStatus( i < lastHighlighted[ j ] );
			}
		}
	}

	public virtual function ShowCompleted( revealLocalizedName : Bool )
	{
		inkWidgetRef.SetVisible( m_completedMarker, true );
		m_data.wasCompleted = true;
		if( revealLocalizedName )
		{
			inkTextRef.SetText( m_text, m_data.localizationKey );
		}
		PlayAnim( 'program_unlocked' );
	}

	public function GetData() : ProgramData
	{
		return m_data;
	}

	public function RefreshImage()
	{
		switch( m_data.id )
		{
			case "Encrypted Data Package":
				inkImageRef.SetTexturePart( m_imageRef, 'program_ico_01' );
			break;
			case "Basic Access":
				inkImageRef.SetTexturePart( m_imageRef, 'program_ico_01' );
			break;
			case "Network Cache":
				inkImageRef.SetTexturePart( m_imageRef, 'program_ico_02' );
			break;
			case "Camera Malfunction":
				inkImageRef.SetTexturePart( m_imageRef, 'program_ico_03' );
			break;
			case "Officer tracing":
				inkImageRef.SetTexturePart( m_imageRef, 'program_ico_04' );
			break;
		}
	}

	public function PlayAnim( anim : CName )
	{
		m_animProxy = PlayLibraryAnimation( anim );
	}

}

class NetworkMinigameBasicProgramController extends NetworkMinigameProgramController
{

	public override function ShowCompleted( revealLocalizedName : Bool )
	{
		inkWidgetRef.SetVisible( m_completedMarker, true );
		m_data.wasCompleted = true;
		if( revealLocalizedName )
		{
			inkTextRef.SetText( m_text, m_data.localizationKey );
		}
		PlayAnim( 'basic_access_unlocked' );
	}

}

class NetworkMinigameProgramListController extends inkLogicController
{
	protected editable var m_programPlayerContainer : inkWidgetRef;
	protected editable var m_programNetworkContainer : inkWidgetRef;
	protected editable var m_programLibraryName : CName;
	protected var m_slotList : array< weak< NetworkMinigameProgramController > >;
	private var m_animProxy_02 : inkAnimProxy;
	private editable var m_headerBG : inkWidgetRef;

	public function Spawn( const contents : ref< array< ProgramData > > )
	{
		var i : Int32;
		for( i = 0; i < contents.Size(); i += 1 )
		{
			m_slotList.PushBack( SpawnSlot( contents[ i ] ) );
		}
	}

	private function SpawnSlot( const data : ref< ProgramData > ) : weak< NetworkMinigameProgramController >
	{
		var slotLogic : weak< NetworkMinigameProgramController >;
		var slot : weak< inkWidget >;
		var toAppendTo : inkWidgetRef;
		toAppendTo = GetDesignatedParent( data );
		slot = SpawnFromLocal( inkWidgetRef.Get( toAppendTo ), m_programLibraryName );
		slotLogic = ( ( NetworkMinigameProgramController )( slot.GetController() ) );
		slotLogic.Spawn( data );
		return slotLogic;
	}

	private function GetDesignatedParent( const data : ref< ProgramData > ) : inkWidgetRef
	{
		switch( data.type )
		{
			case ProgramType.ExtraPlayerProgram:
				return m_programPlayerContainer;
			case ProgramType.ExtraServerProgram:
				return m_programNetworkContainer;
		}
		return m_programPlayerContainer;
	}

	public function UpdatePartialCompletionState( const progressList : ref< array< ProgramProgressData > > )
	{
		var i : Int32;
		var j : Int32;
		for( i = 0; i < progressList.Size(); i += 1 )
		{
			j = FindSlotIndexByID( progressList[ i ].id );
			if( j >= 0 )
			{
				m_slotList[ j ].UpdatePartialCompletionState( progressList[ i ] );
			}
		}
	}

	public function ShowCompleted( const id : ref< String >, revealLocalizedName : Bool )
	{
		var i : Int32;
		i = FindSlotIndexByID( id );
		if( i >= 0 )
		{
			m_slotList[ i ].ShowCompleted( revealLocalizedName );
		}
	}

	public function PlaySideBarAnim()
	{
		inkWidgetRef.SetVisible( m_headerBG, true );
	}

	public function ProcessListModified( shouldModify : Bool, const playerProgramsAdded : ref< array< ProgramData > >, const playerProgramsRemoved : ref< array< ProgramData > > )
	{
		var i : Int32;
		var j : Int32;
		var parentCompound : weak< inkCompoundWidget >;
		var parentCompoundRef : inkWidgetRef;
		if( shouldModify )
		{
			for( i = 0; i < playerProgramsRemoved.Size(); i += 1 )
			{
				j = FindSlotIndexByID( playerProgramsRemoved[ i ].id );
				if( j >= 0 )
				{
					parentCompoundRef = GetDesignatedParent( playerProgramsRemoved[ i ] );
					parentCompound = ( ( inkCompoundWidget )( inkWidgetRef.Get( parentCompoundRef ) ) );
					parentCompound.RemoveChild( m_slotList[ j ].GetRootWidget() );
					m_slotList.EraseFast( j );
				}
			}
			for( i = 0; i < playerProgramsAdded.Size(); i += 1 )
			{
				m_slotList.PushBack( SpawnSlot( playerProgramsAdded[ i ] ) );
			}
		}
	}

	private function FindSlotIndexByID( const id : ref< String > ) : Int32
	{
		var i : Int32;
		var data : ProgramData;
		for( i = 0; i < m_slotList.Size(); i += 1 )
		{
			data = m_slotList[ i ].GetData();
			if( id == data.id )
			{
				return i;
			}
		}
		return -1;
	}

}

