class QuickhackData extends IScriptable
{
	var m_actionOwner : EntityID;
	var m_action : BaseScriptableAction;
	var m_actionOwnerName : CName;
	var m_itemID : ItemID;
	var m_icon : TweakDBID;
	var m_iconCategory : CName;
	var m_title : String;
	var m_titleAlternative : String;
	var m_description : String;
	var m_inactiveReason : String;
	var m_isLocked : Bool;
	var m_actionState : EActionInactivityReson;
	var m_type : gamedataObjectActionType;
	var m_cost : Int32;
	var m_awarenessCost : Float;
	default m_awarenessCost = 0f;
	var m_showRevealInfo : Bool;
	default m_showRevealInfo = false;
	var m_willReveal : Bool;
	default m_willReveal = false;
	var m_costRaw : Int32;
	var m_uploadTime : Float;
	var m_duration : Float;
	var m_ICELevelVisible : Bool;
	var m_ICELevel : Float;
	var m_vulnerabilities : array< TweakDBID >;
	var m_quality : Int32;
	var m_isInstant : Bool;
	var m_cooldown : Float;
	var m_cooldownTweak : TweakDBID;
	var m_actionMatchesTarget : Bool;
	var m_maxListSize : Int32;
	var m_category : weak< HackCategory_Record >;
	var m_actionCompletionEffects : array< weak< ObjectActionEffect_Record > >;
	var m_noQuickhackData : Bool;
	default m_noQuickhackData = false;
	var m_networkBreached : Bool;
}

class QuickhacksListGameController extends inkHUDGameController
{
	private editable var m_timeBetweenIntroAndIntroDescription : Float;
	private var m_timeBetweenIntroAndDescritpionDelayID : DelayID;
	private var m_timeBetweenIntroAndDescritpionCheck : Bool;
	private var m_introDescriptionAnimProxy : inkAnimProxy;
	private editable var m_middleDots : inkWidgetRef;
	private editable var m_memoryWidget : inkWidgetRef;
	private editable var m_avaliableMemory : inkTextRef;
	private editable var m_listWidget : inkWidgetRef;
	private editable var m_noQuickhacks : inkCompoundRef;
	private editable var m_openCyberdeckBtn : inkWidgetRef;
	private editable var m_executeBtn : inkWidgetRef;
	private editable var m_executeAndCloseBtn : inkWidgetRef;
	private editable var m_changeTarget : inkWidgetRef;
	private editable var m_tutorialButton : inkWidgetRef;
	private editable var m_rightPanel : inkWidgetRef;
	private editable var m_networkBreach : inkWidgetRef;
	private editable var m_costReductionPanel : inkWidgetRef;
	private editable var m_costReductionText : inkTextRef;
	private editable var m_costReductionValue : inkTextRef;
	private editable var m_targetName : inkTextRef;
	private editable var m_icePanel : inkWidgetRef;
	private editable var m_iceValue : inkTextRef;
	private editable var m_vulnerabilitiesPanel : inkWidgetRef;
	private const editable var m_vulnerabilityFields : array< inkWidgetRef >;
	private editable var m_subHeader : inkTextRef;
	private editable var m_tier : inkTextRef;
	private editable var m_description : inkTextRef;
	private editable var m_recompileTimer : inkTextRef;
	private editable var m_damage : inkTextRef;
	private editable var m_duration : inkTextRef;
	private editable var m_cooldown : inkTextRef;
	private editable var m_uploadTime : inkTextRef;
	private editable var m_memoryCost : inkTextRef;
	private editable var m_memoryRawCost : inkTextRef;
	private editable var m_warningWidget : inkWidgetRef;
	private editable var m_warningText : inkTextRef;
	private editable var m_recompilePanel : inkWidgetRef;
	private editable var m_recompileText : inkTextRef;
	private var m_isUILocked : Bool;
	private var m_gameInstance : GameInstance;
	private var m_visionModeSystem : weak< VisionModeSystem >;
	private var m_scanningCtrl : weak< ScanningController >;
	private var m_uiSystem : UISystem;
	private var m_contextHelpOverlay : Bool;
	private var m_quickHackDescriptionVisibility : Uint32;
	private var m_buffListListener : CallbackHandle;
	private var m_memoryBoard : weak< IBlackboard >;
	private var m_memoryBoardDef : UI_PlayerBioMonitorDef;
	private var m_memoryPercentListener : CallbackHandle;
	private var m_quickhackBarArray : array< weak< inkCompoundWidget > >;
	private var m_maxQuickhackBars : Int32;
	private var m_listController : weak< ListController >;
	private var m_data : array< QuickhackData >;
	private var m_selectedData : QuickhackData;
	private var m_active : Bool;
	default m_active = false;
	private var m_memorySpendAnimation : inkAnimProxy;
	private var m_memorySpendCounter : Int32;
	private var m_memorySpendIndex : Int32;
	default m_memorySpendIndex = -1;
	private var m_selectedMemoryLoop : array< inkAnimProxy >;
	private var inkIntroAnimProxy : inkAnimProxy;
	private var inkVulnerabilityAnimProxy : inkAnimProxy;
	private var inkWarningAnimProxy : inkAnimProxy;
	private var inkRecompileAnimProxy : inkAnimProxy;
	private var inkReductionAnimProxy : inkAnimProxy;
	private var HACK_wasPlayedOnTarget : Bool;
	private var inkMemoryWarningTransitionAnimProxy : inkAnimProxy;
	private var m_lastMemoryWarningTransitionAnimName : CName;
	private var m_hasActiveUpload : Bool;
	private var m_lastCompiledTarget : EntityID;
	private var m_statPoolListenersIndexes : array< Int32 >;
	protected var m_chunkBlackboard : weak< IBlackboard >;
	private var m_nameCallbackID : CallbackHandle;
	private var m_uiScannerChangeTargetTooltipVisibilityCallback : CallbackHandle;
	private var m_lastFillCells : Int32;
	private var m_lastUsedCells : Int32;
	private var m_lastMaxCells : Int32;

	protected event OnInitialize()
	{
		var bbSystem : BlackboardSystem;
		bbSystem = GetBlackboardSystem();
		m_listController = ( ( ListController )( inkWidgetRef.GetController( m_listWidget ) ) );
		m_listController.RegisterToCallback( 'OnItemSelected', this, 'OnItemSelected' );
		m_memoryBoardDef = GetAllBlackboardDefs().UI_PlayerBioMonitor;
		m_memoryBoard = GetBlackboardSystem().Get( m_memoryBoardDef );
		m_memoryPercentListener = m_memoryBoard.RegisterDelayedListenerFloat( m_memoryBoardDef.MemoryPercent, this, 'OnMemoryPercentUpdate' );
		m_memoryBoard.Signal( m_memoryBoardDef.MemoryPercent );
		m_gameInstance = ( ( PlayerPuppet )( GetOwnerEntity() ) ).GetGame();
		m_visionModeSystem = GameInstance.GetVisionModeSystem( m_gameInstance );
		m_scanningCtrl = m_visionModeSystem.GetScanningController();
		m_uiSystem = GameInstance.GetUISystem( m_gameInstance );
		m_chunkBlackboard = bbSystem.Get( GetAllBlackboardDefs().UI_ScannerModules );
		if( m_chunkBlackboard )
		{
			m_nameCallbackID = m_chunkBlackboard.RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_ScannerModules.ScannerName, this, 'OnTargetDisplayNameChanged' );
		}
		m_uiScannerChangeTargetTooltipVisibilityCallback = bbSystem.Get( GetAllBlackboardDefs().UI_Scanner ).RegisterListenerBool( GetAllBlackboardDefs().UI_Scanner.scannerChangeTargetTooltipVisibility, this, 'OnScannerChangeTargetTooltipVisibilityChanged' );
		SetupQuickhacksMemoryBar();
		OnMemoryPercentUpdate( 1.0 );
		GetRootWidget().SetVisible( false );
	}

	protected event OnUninitialize()
	{
		var bbSystem : BlackboardSystem;
		bbSystem = GetBlackboardSystem();
		if( m_memoryBoard && m_memoryPercentListener )
		{
			m_memoryBoard.UnregisterDelayedListener( m_memoryBoardDef.MemoryPercent, m_memoryPercentListener );
		}
		if( m_chunkBlackboard && m_nameCallbackID )
		{
			m_chunkBlackboard.UnregisterDelayedListener( GetAllBlackboardDefs().UI_ScannerModules.ScannerName, m_nameCallbackID );
		}
		if( m_uiScannerChangeTargetTooltipVisibilityCallback )
		{
			bbSystem.Get( GetAllBlackboardDefs().UI_Scanner ).UnregisterListenerBool( GetAllBlackboardDefs().UI_Scanner.scannerChangeTargetTooltipVisibility, m_uiScannerChangeTargetTooltipVisibilityCallback );
		}
		SetVisibility( false );
	}

	protected event OnTargetDisplayNameChanged( value : Variant )
	{
		var nameData : ScannerName;
		var displayNmae : String;
		nameData = ( ( ScannerName )value );
		if( nameData )
		{
			displayNmae = nameData.GetDisplayName();
			if( nameData.GetTextParams() )
			{
				inkTextRef.SetLocalizedTextScript( m_targetName, displayNmae, nameData.GetTextParams() );
			}
			else
			{
				inkTextRef.SetText( m_targetName, displayNmae );
			}
		}
	}

	protected event OnScannerChangeTargetTooltipVisibilityChanged( value : Bool )
	{
		inkWidgetRef.SetVisible( m_changeTarget, value );
	}

	protected event OnQuickhackStarted( value : RevealInteractionWheel )
	{
		if( value.shouldReveal )
		{
			m_data = value.commands;
			if( !( value.shouldReveal ) || GetPlayerControlledObject().GetHudManager().GetActiveMode() != ActiveMode.FOCUS )
			{
				return false;
			}
			if( value.lookAtObject )
			{
				m_hasActiveUpload = value.lookAtObject.HasActiveQuickHackUpload();
			}
			else
			{
				m_hasActiveUpload = false;
			}
			PopulateData( m_data );
			SetVisibility( true );
			RegisterCooldownStatPoolUpdate();
		}
		else
		{
			SetVisibility( false );
		}
	}

	protected event OnItemSelected( index : Int32, itemController : ListItemController )
	{
		SelectData( ( ( QuickhackData )( itemController.GetData() ) ) );
		m_memoryBoard.Signal( m_memoryBoardDef.MemoryPercent );
	}


	private var m_axisInputConsumed : Bool;

	protected event OnAction( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		var actionName : CName;
		var isReleased : Bool;
		var isMinigameActive : Bool;
		isReleased = ListenerAction.GetType( action ) == gameinputActionType.BUTTON_PRESSED || ListenerAction.GetType( action ) == gameinputActionType.AXIS_CHANGE;
		isMinigameActive = GetPlayerControlledObject().GetHudManager().IsHackingMinigameActive();
		if( ( isReleased && !( isMinigameActive ) ) && !( m_isUILocked ) )
		{
			actionName = ListenerAction.GetName( action );
			switch( actionName )
			{
				case 'UI_MoveDown':
					ListenerActionConsumer.Consume( consumer );
				PlaySound( 'QuickHackMenu', 'OnHover' );
				m_listController.Next();
				break;
				case 'UI_MoveUp':
					ListenerActionConsumer.Consume( consumer );
				PlaySound( 'QuickHackMenu', 'OnHover' );
				m_listController.Prior();
				break;
				case 'context_help':
					ToggleTutorialOverlay();
				break;
				case 'UI_ApplyAndClose':
					if( m_selectedData.m_noQuickhackData )
					{
						ShowInventory();
					}
					else
					{
						ApplyQuickHack( true );
					}
				break;
				default:
					break;
			}
		}
	}

	private function ToggleTutorialOverlay()
	{
		if( !( m_contextHelpOverlay ) && !( m_active ) )
		{
			return;
		}
		if( m_selectedData.m_noQuickhackData )
		{
			return;
		}
		m_contextHelpOverlay = !( m_contextHelpOverlay );
		ShowTutorialOverlay( m_contextHelpOverlay );
	}

	private function ShowTutorialOverlay( value : Bool )
	{
		var data : TutorialOverlayData;
		data.itemName = 'Root';
		data.widgetLibraryResource = R"base\gameplay\gui\widgets\tutorial\vr_quickhacks_tutorial.inkwidget";
		m_contextHelpOverlay = value;
		if( m_contextHelpOverlay )
		{
			m_uiSystem.ShowTutorialOverlay( data );
		}
		else
		{
			m_uiSystem.HideTutorialOverlay( data );
		}
	}

	private function SelectData( data : QuickhackData )
	{
		var description : String;
		var progressBarBB : weak< IBlackboard >;
		progressBarBB = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_HUDProgressBar );
		progressBarBB.SetFloat( GetAllBlackboardDefs().UI_HUDProgressBar.ProgressBump, 0.0 );
		m_selectedData = data;
		description = GetLocalizedText( m_selectedData.m_description );
		inkTextRef.SetText( m_subHeader, GetLocalizedText( m_selectedData.m_title ) );
		m_playerObject = GetPlayerControlledObject();
		if( ( m_selectedData.m_isLocked && m_selectedData.m_actionMatchesTarget ) && m_hasActiveUpload )
		{
			inkWidgetRef.SetState( m_executeBtn, 'Disabled' );
			inkWidgetRef.SetState( m_executeAndCloseBtn, 'Disabled' );
			inkTextRef.SetState( m_description, 'Locked' );
			inkTextRef.SetState( m_subHeader, 'Locked' );
			SendOverclockPreviewEvent( false );
			if( inkWarningAnimProxy )
			{
				inkWarningAnimProxy.Stop();
			}
			inkWarningAnimProxy = PlayLibraryAnimation( 'deviceOnly_hack', GetAnimOptionsInfiniteLoop( inkanimLoopType.Cycle ) );
			if( m_lastMemoryWarningTransitionAnimName != 'memoryToWarning_transition' )
			{
				if( inkMemoryWarningTransitionAnimProxy )
				{
					inkMemoryWarningTransitionAnimProxy.Stop();
				}
				inkMemoryWarningTransitionAnimProxy = PlayLibraryAnimation( 'memoryToWarning_transition' );
				m_lastMemoryWarningTransitionAnimName = 'memoryToWarning_transition';
			}
			ApplyQuickhackSelection();
			inkTextRef.SetText( m_warningText, GetLocalizedText( m_selectedData.m_inactiveReason ) );
		}
		else if( m_selectedData.m_isLocked )
		{
			ResetQuickhackSelection();
			inkWidgetRef.SetState( m_executeBtn, 'Disabled' );
			inkWidgetRef.SetState( m_executeAndCloseBtn, 'Disabled' );
			inkTextRef.SetState( m_description, 'Locked' );
			inkTextRef.SetState( m_subHeader, 'Locked' );
			SendOverclockPreviewEvent( false );
			if( inkWarningAnimProxy )
			{
				inkWarningAnimProxy.Stop();
			}
			if( m_selectedData.m_noQuickhackData )
			{
				inkWidgetRef.SetVisible( m_warningWidget, false );
			}
			else
			{
				inkWarningAnimProxy = PlayLibraryAnimation( 'deviceOnly_hack' );
				if( m_lastMemoryWarningTransitionAnimName != 'memoryToWarning_transition' )
				{
					if( inkMemoryWarningTransitionAnimProxy )
					{
						inkMemoryWarningTransitionAnimProxy.Stop();
					}
					inkMemoryWarningTransitionAnimProxy = PlayLibraryAnimation( 'memoryToWarning_transition' );
					m_lastMemoryWarningTransitionAnimName = 'memoryToWarning_transition';
				}
			}
			inkTextRef.SetText( m_warningText, GetLocalizedText( m_selectedData.m_inactiveReason ) );
		}
		else
		{
			inkWidgetRef.SetState( m_executeBtn, 'Default' );
			inkWidgetRef.SetState( m_executeAndCloseBtn, 'Default' );
			inkTextRef.SetState( m_description, 'Default' );
			inkTextRef.SetState( m_subHeader, 'Default' );
			ApplyQuickhackSelection();
			if( m_selectedData.m_cost > m_lastFillCells )
			{
				SendOverclockPreviewEvent( true, m_selectedData.m_cost );
			}
			else
			{
				SendOverclockPreviewEvent( false );
			}
			if( inkWarningAnimProxy )
			{
				inkWarningAnimProxy.Stop();
			}
			if( m_selectedData.m_showRevealInfo )
			{
				progressBarBB.SetFloat( GetAllBlackboardDefs().UI_HUDProgressBar.ProgressBump, m_selectedData.m_awarenessCost / 100.0 );
			}
			inkWarningAnimProxy = PlayLibraryAnimation( 'warningOut' );
			if( m_lastMemoryWarningTransitionAnimName != 'warningToMemory_transition' )
			{
				if( inkMemoryWarningTransitionAnimProxy )
				{
					inkMemoryWarningTransitionAnimProxy.Stop();
				}
				inkMemoryWarningTransitionAnimProxy = PlayLibraryAnimation( 'warningToMemory_transition' );
				m_lastMemoryWarningTransitionAnimName = 'warningToMemory_transition';
			}
		}
		if( !( m_timeBetweenIntroAndDescritpionCheck ) )
		{
		}
		inkTextRef.SetText( m_description, description );
		SetupTargetName();
		SetupTier();
		SetupVulnerabilities();
		SetupICE();
		SetupUploadTime();
		SetupDuration();
		SetupMaxCooldown();
		SetupMemoryCost();
		SetupMemoryCostDifferance();
		SetupNetworkBreach();
		if( !( IsCurrentSelectionOnStatPoolIndexes() ) )
		{
			UpdateRecompileTime( false, 0.0 );
		}
		GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_QuickSlotsData ).SetVariant( GetAllBlackboardDefs().UI_QuickSlotsData.quickHackDataSelected, m_selectedData, true );
	}

	private function SetupTier()
	{
		var value : Int32;
		var textParams : inkTextParams;
		value = m_selectedData.m_quality / 2;
		textParams = new inkTextParams;
		textParams.AddNumber( "VALUE", value );
		( ( inkText )( inkTextRef.Get( m_tier ) ) ).SetLocalizedTextString( "LocKey#40895", textParams );
	}

	private function SetupMaxCooldown()
	{
		var value : Float;
		var textParams : inkTextParams;
		value = m_selectedData.m_cooldown;
		textParams = new inkTextParams;
		textParams.AddNumber( "VALUE", value );
		textParams.AddLocalizedString( "SEC", "LocKey#40730" );
		( ( inkText )( inkTextRef.Get( m_cooldown ) ) ).SetLocalizedTextString( "LocKey#40729", textParams );
		if( value == 0.0 )
		{
			inkTextRef.SetState( m_cooldown, 'Locked' );
		}
		else
		{
			inkTextRef.SetState( m_cooldown, 'Default' );
		}
	}

	private function SetupDuration()
	{
		var value : Float;
		var textParams : inkTextParams;
		value = m_selectedData.m_duration;
		textParams = new inkTextParams;
		if( value < 0.0 )
		{
			textParams.AddLocalizedString( "value", "LocKey#49549" );
			textParams.AddString( "unit", "" );
			( ( inkText )( inkTextRef.Get( m_duration ) ) ).SetLocalizedTextScript( "LocKey#78476", textParams );
		}
		else
		{
			textParams.AddNumber( "VALUE", value );
			textParams.AddLocalizedString( "SEC", "LocKey#40730" );
			( ( inkText )( inkTextRef.Get( m_duration ) ) ).SetLocalizedTextScript( "LocKey#40736", textParams );
		}
		if( value == 0.0 )
		{
			inkTextRef.SetState( m_duration, 'Locked' );
		}
		else
		{
			inkTextRef.SetState( m_duration, 'Default' );
		}
	}

	private function SetupUploadTime()
	{
		var value : Float;
		var textParams : inkTextParams;
		value = m_selectedData.m_uploadTime;
		textParams = new inkTextParams;
		textParams.AddNumber( "VALUE", value );
		textParams.AddLocalizedString( "SEC", "LocKey#40730" );
		( ( inkText )( inkTextRef.Get( m_uploadTime ) ) ).SetLocalizedTextString( "LocKey#40737", textParams );
		if( value == 0.0 )
		{
			inkTextRef.SetState( m_uploadTime, 'Locked' );
		}
		else
		{
			inkTextRef.SetState( m_uploadTime, 'Default' );
		}
	}

	private function SetupMemoryCost()
	{
		var value : Int32;
		var textParams : inkTextParams;
		inkTextRef.SetText( m_memoryCost, IntToString( m_selectedData.m_cost ) );
		value = m_selectedData.m_costRaw;
		textParams = new inkTextParams;
		textParams.AddNumber( "VALUE", value );
		( ( inkText )( inkTextRef.Get( m_memoryRawCost ) ) ).SetLocalizedTextString( "LocKey#40804", textParams );
	}

	private function SetupMemoryCostDifferance()
	{
		var reducedCost, increasedCost : Int32;
		reducedCost = m_selectedData.m_costRaw - m_selectedData.m_cost;
		increasedCost = m_selectedData.m_cost - m_selectedData.m_costRaw;
		if( m_selectedData.m_type == gamedataObjectActionType.VehicleQuickHack )
		{
			inkWidgetRef.SetVisible( m_costReductionPanel, false );
		}
		else if( ( reducedCost > 0 ) || ( m_selectedData.m_cost < m_selectedData.m_costRaw ) )
		{
			inkTextRef.SetText( m_costReductionValue, IntToString( reducedCost ) );
			inkTextRef.SetText( m_costReductionText, GetLocalizedText( "LocKey#48365" ) );
			inkWidgetRef.SetVisible( m_costReductionPanel, true );
			inkWidgetRef.SetState( m_costReductionPanel, 'Up' );
		}
		else if( ( increasedCost > 0 ) || ( m_selectedData.m_cost > m_selectedData.m_costRaw ) )
		{
			inkTextRef.SetText( m_costReductionValue, IntToString( increasedCost ) );
			inkTextRef.SetText( m_costReductionText, GetLocalizedText( "LocKey#94471" ) );
			inkWidgetRef.SetVisible( m_costReductionPanel, true );
			inkWidgetRef.SetState( m_costReductionPanel, 'Low' );
		}
		else
		{
			inkWidgetRef.SetVisible( m_costReductionPanel, false );
		}
	}

	private function SetupNetworkBreach()
	{
		if( m_selectedData.m_networkBreached )
		{
			inkWidgetRef.SetVisible( m_networkBreach, true );
			if( !( HACK_wasPlayedOnTarget ) )
			{
				if( inkReductionAnimProxy )
				{
					inkReductionAnimProxy.Stop();
				}
				inkReductionAnimProxy = PlayLibraryAnimation( 'network_scan' );
				HACK_wasPlayedOnTarget = true;
			}
		}
		else
		{
			inkWidgetRef.SetVisible( m_networkBreach, false );
			HACK_wasPlayedOnTarget = false;
		}
	}

	private function SetupICE()
	{
		if( m_selectedData.m_ICELevelVisible )
		{
			inkTextRef.SetText( m_iceValue, IntToString( ( ( Int32 )( m_selectedData.m_ICELevel ) ) ) );
		}
		else
		{
			inkTextRef.SetText( m_iceValue, "X" );
		}
	}

	private function SetupTargetName()
	{
		var displayName : String;
		displayName = ( ( String )( m_selectedData.m_actionOwnerName ) );
		if( displayName != "None" )
		{
			inkTextRef.SetText( m_targetName, displayName );
		}
	}

	private function UpdateRecompileTime( isVisible : Bool, value : Float )
	{
		inkWidgetRef.SetVisible( m_recompilePanel, isVisible );
		inkTextRef.SetText( m_recompileText, FloatToString( value ) );
	}

	private function SetupVulnerabilities()
	{
		var vulnerabilityRecord : weak< ObjectActionGameplayCategory_Record >;
		var i : Int32;
		if( m_selectedData.m_vulnerabilities.Size() == 0 )
		{
			if( inkWidgetRef.IsVisible( m_vulnerabilitiesPanel ) && !( IsIntroPlaying() ) )
			{
				if( inkVulnerabilityAnimProxy )
				{
					inkVulnerabilityAnimProxy.Stop();
				}
				inkVulnerabilityAnimProxy = PlayLibraryAnimation( 'vulnerabilityOut' );
			}
			inkWidgetRef.SetVisible( m_vulnerabilitiesPanel, false );
			return;
		}
		if( !( inkWidgetRef.IsVisible( m_vulnerabilitiesPanel ) ) && !( IsIntroPlaying() ) )
		{
			if( inkVulnerabilityAnimProxy )
			{
				inkVulnerabilityAnimProxy.Stop();
			}
			inkVulnerabilityAnimProxy = PlayLibraryAnimation( 'vulnerabilityIn' );
		}
		inkWidgetRef.SetVisible( m_vulnerabilitiesPanel, true );
		for( i = 0; i < 4; i += 1 )
		{
			if( i < m_selectedData.m_vulnerabilities.Size() )
			{
				vulnerabilityRecord = TweakDBInterface.GetObjectActionGameplayCategoryRecord( m_selectedData.m_vulnerabilities[ i ] );
				inkWidgetRef.SetVisible( m_vulnerabilityFields[ i ], true );
				( ( QuickhacksVulnerabilityLogicController )( inkWidgetRef.GetController( m_vulnerabilityFields[ i ] ) ) ).SetText( vulnerabilityRecord.LocalizedDescription() );
			}
			else
			{
				inkWidgetRef.SetVisible( m_vulnerabilityFields[ i ], false );
			}
		}
	}

	protected event OnSpecialQuickhackAttackTriggered( evt : OnSpecialQuickhackTriggeredEvent )
	{
		if( evt.quickhackData )
		{
			m_selectedData = evt.quickhackData;
			ApplyQuickHack( false );
		}
	}

	private function ShowInventory()
	{
		var evt : StartHubMenuEvent;
		var wrapper : CyberwareDisplayWrapper;
		evt = new StartHubMenuEvent;
		wrapper = new CyberwareDisplayWrapper;
		wrapper.displayData = GetItemDisplayData();
		evt.SetStartMenu( 'inventory_screen', , wrapper );
		QueueBroadcastEvent( evt );
	}

	private function GetItemDisplayData() : InventoryItemDisplayData
	{
		var data : InventoryItemDisplayData;
		data.m_itemID = EquipmentSystem.GetData( m_playerObject ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		data.m_equipmentArea = gamedataEquipmentArea.SystemReplacementCW;
		data.m_slotIndex = 0;
		return data;
	}

	private function ApplyQuickHack( shouldUseUI : Bool ) : Bool
	{
		var cmd : QuickSlotCommandUsed;
		var hackUsed : QhackExecuted;
		if( shouldUseUI )
		{
			PlayChoiceAnimation();
			SendOverclockPreviewEvent( false, , true );
		}
		if( m_selectedData && !( m_selectedData.m_isLocked ) )
		{
			cmd = new QuickSlotCommandUsed;
			cmd.action = m_selectedData.m_action;
			LogQuickHack();
			GetPlayerControlledObject().QueueEventForEntityID( m_selectedData.m_actionOwner, cmd );
			if( GetPlayerControlledObject().GetTakeOverControlSystem().IsDeviceControlled() )
			{
				hackUsed = new QhackExecuted;
				GetPlayerControlledObject().QueueEventForEntityID( GetPlayerControlledObject().GetTakeOverControlSystem().GetControlledObject().GetEntityID(), hackUsed );
			}
			return true;
		}
		return false;
	}

	private function LogQuickHack()
	{
		var target : weak< GameObject >;
		var telemetryQuickHack : TelemetryQuickHack;
		target = ( ( GameObject )( GameInstance.FindEntityByID( m_gameInstance, m_selectedData.m_actionOwner ) ) );
		if( !( target ) )
		{
			return;
		}
		if( target.IsPuppet() )
		{
			telemetryQuickHack.targetType = "Puppet";
		}
		else if( target.IsSensor() )
		{
			telemetryQuickHack.targetType = "Sensor";
		}
		else if( target.IsTurret() )
		{
			telemetryQuickHack.targetType = "Turret";
		}
		else if( GameObject.IsVehicle( target ) )
		{
			telemetryQuickHack.targetType = "Vehicle";
		}
		else
		{
			telemetryQuickHack.targetType = "Other";
		}
		telemetryQuickHack.actionName = m_selectedData.m_action.GetActionID();
		telemetryQuickHack.titleLocKey = m_selectedData.m_title;
		telemetryQuickHack.quickHackRecordID = m_selectedData.m_action.GetObjectActionID();
		telemetryQuickHack.quality = RPGManager.GetFloatItemQuality( ( ( Float )( m_selectedData.m_quality / 2 ) ) );
		GameInstance.GetTelemetrySystem( m_gameInstance ).LogQuickHack( telemetryQuickHack );
	}

	private function PlayChoiceAnimation()
	{
		( ( QuickhacksListItemController )( m_listController.GetItemAt( m_listController.GetSelectedIndex() ).GetController() ) ).PlayChoiceAcceptedAnimation();
	}

	private function IsIntroPlaying() : Bool
	{
		return inkIntroAnimProxy.IsPlaying();
	}

	private function ApplyQuickhackSelection()
	{
		var itemChangedEvent : QHackWheelItemChangedEvent;
		if( m_selectedData )
		{
			itemChangedEvent = new QHackWheelItemChangedEvent;
			itemChangedEvent.commandData = m_selectedData;
			itemChangedEvent.currentEmpty = false;
			GetOwnerEntity().QueueEventForEntityID( m_selectedData.m_actionOwner, itemChangedEvent );
		}
	}

	private function ResetQuickhackSelection()
	{
		var itemChangedEvent : QHackWheelItemChangedEvent;
		if( m_selectedData )
		{
			itemChangedEvent = new QHackWheelItemChangedEvent;
			itemChangedEvent.currentEmpty = true;
			GetOwnerEntity().QueueEventForEntityID( m_selectedData.m_actionOwner, itemChangedEvent );
			SendOverclockPreviewEvent( false );
		}
	}


	var m_playerObject : weak< GameObject >;

	private function SetVisibility( value : Bool )
	{
		var delayIntroDescritpio : DelayedDescriptionIntro;
		var uiQuickSlotsDataBB : IBlackboard;
		var progressBarBB : weak< IBlackboard >;
		var animOptions : inkAnimOptions;
		progressBarBB = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_HUDProgressBar );
		uiQuickSlotsDataBB = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_QuickSlotsData );
		if( value )
		{
			if( !( HUDManager.HasCurrentTarget( m_gameInstance ) ) )
			{
				return;
			}
			m_playerObject = GetPlayerControlledObject();
			if( !( m_playerObject ) )
			{
				return;
			}
			if( m_lastCompiledTarget != m_data[ 0 ].m_actionOwner )
			{
				if( EntityID.IsDefined( m_data[ 0 ].m_actionOwner ) )
				{
					m_lastCompiledTarget = m_data[ 0 ].m_actionOwner;
				}
			}
			else
			{
				if( m_selectedData.m_noQuickhackData )
				{
					if( GameInstance.GetStatsSystem( m_gameInstance ).GetStatValue( m_playerObject.GetEntityID(), gamedataStatType.HasCyberdeck ) <= 0.0 )
					{
						return;
					}
				}
				else
				{
					return;
				}
			}
			SetupQuickhacksMemoryBar();
			GetRootWidget().SetVisible( true );
			if( inkIntroAnimProxy && inkIntroAnimProxy.IsPlaying() )
			{
				inkIntroAnimProxy.Stop();
			}
			animOptions.customTimeDilation = 2.0;
			animOptions.applyCustomTimeDilation = true;
			inkIntroAnimProxy = PlayLibraryAnimation( 'intro', animOptions );
			PlaySound( 'QuickHackMenu', 'OnOpen' );
			if( m_timeBetweenIntroAndDescritpionCheck )
			{
				GameInstance.GetDelaySystem( m_playerObject.GetGame() ).CancelDelay( m_timeBetweenIntroAndDescritpionDelayID );
			}
			if( m_timeBetweenIntroAndIntroDescription != 0.0 )
			{
				m_introDescriptionAnimProxy = PlayLibraryAnimation( 'outro_tooltip' );
			}
			delayIntroDescritpio = new DelayedDescriptionIntro;
			m_timeBetweenIntroAndDescritpionDelayID = GameInstance.GetDelaySystem( m_playerObject.GetGame() ).DelayEvent( m_playerObject, delayIntroDescritpio, m_timeBetweenIntroAndIntroDescription, false );
			m_timeBetweenIntroAndDescritpionCheck = true;
			if( !( m_active ) )
			{
				m_playerObject.RegisterInputListener( this, 'UI_MoveDown' );
				m_playerObject.RegisterInputListener( this, 'UI_MoveUp' );
				m_playerObject.RegisterInputListener( this, 'context_help' );
				m_playerObject.RegisterInputListener( this, 'UI_ApplyAndClose' );
			}
			RequestTimeDilation( m_playerObject, 'quickHackScreen', true );
			m_memoryBoard.Signal( m_memoryBoardDef.MemoryPercent );
			if( uiQuickSlotsDataBB )
			{
				uiQuickSlotsDataBB.SetBool( GetAllBlackboardDefs().UI_QuickSlotsData.quickhackPanelOpen, true );
			}
			GameInstance.GetUISystem( m_gameInstance ).RequestNewVisualState( 'inkQuickHackingState' );
		}
		else
		{
			PlaySound( 'QuickHackMenu', 'OnClose' );
			GetRootWidget().SetVisible( false );
			if( m_playerObject )
			{
				GameInstance.GetTargetingSystem( m_playerObject.GetGame() ).BreakLookAt( m_playerObject );
				if( m_active )
				{
					m_playerObject.UnregisterInputListener( this );
				}
				RequestTimeDilation( m_playerObject, 'quickHackScreen', false );
			}
			if( uiQuickSlotsDataBB )
			{
				uiQuickSlotsDataBB.SetBool( GetAllBlackboardDefs().UI_QuickSlotsData.quickhackPanelOpen, false );
			}
			m_playerObject = NULL;
			GameInstance.GetUISystem( m_gameInstance ).RestorePreviousVisualState( 'inkQuickHackingState' );
			progressBarBB.SetFloat( GetAllBlackboardDefs().UI_HUDProgressBar.ProgressBump, 0.0 );
			ResetQuickhackSelection();
			if( m_memorySpendAnimation )
			{
				m_memorySpendAnimation.UnregisterFromAllCallbacks( inkanimEventType.OnFinish );
			}
			HACK_wasPlayedOnTarget = false;
			m_lastCompiledTarget = EntityID();
			if( m_contextHelpOverlay )
			{
				ShowTutorialOverlay( false );
			}
		}
		m_active = value;
	}

	protected event OnDelayedDescriptionIntro( evt : DelayedDescriptionIntro )
	{
		PlayDescritpionIntroAnimaton();
		m_timeBetweenIntroAndDescritpionCheck = false;
	}

	private function PlayDescritpionIntroAnimaton()
	{
		if( m_introDescriptionAnimProxy && m_introDescriptionAnimProxy.IsPlaying() )
		{
			m_introDescriptionAnimProxy.Stop();
		}
		m_introDescriptionAnimProxy = PlayLibraryAnimation( 'intro_tooltip' );
	}

	private function RequestTimeDilation( requester : weak< GameObject >, eventId : CName, val : Bool )
	{
		var psmEvent : PSMPostponedParameterBool;
		psmEvent = new PSMPostponedParameterBool;
		psmEvent.id = eventId;
		psmEvent.value = val;
		requester.QueueEvent( psmEvent );
	}

	private function SetupQuickhacksMemoryBar()
	{
		( ( inkCompoundWidget )( inkWidgetRef.Get( m_memoryWidget ) ) ).RemoveAllChildren();
		m_quickhackBarArray.Clear();
		m_maxQuickhackBars = 0;
		UpdateQuickhacksMemoryBarSize( FloorF( GameInstance.GetStatsSystem( m_gameInstance ).GetStatValue( GetPlayerControlledObject().GetEntityID(), gamedataStatType.Memory ) ) );
		UpdateMemoryBar();
	}

	private function UpdateQuickhacksMemoryBarSize( size : Int32 )
	{
		var i : Int32;
		var cell : weak< inkCompoundWidget >;
		if( size > m_maxQuickhackBars )
		{
			for( i = m_maxQuickhackBars; i < size; i += 1 )
			{
				cell = ( ( inkCompoundWidget )( SpawnFromLocal( ( ( inkCompoundWidget )( inkWidgetRef.Get( m_memoryWidget ) ) ), 'memory_cell' ) ) );
				m_quickhackBarArray.PushBack( cell );
			}
			m_maxQuickhackBars = size;
		}
	}

	protected event OnMemoryPercentUpdate( value : Float )
	{
		var maxCells : Int32;
		var fillCells : Int32;
		var usedCells : Int32;
		if( m_selectedData )
		{
			usedCells = m_selectedData.m_cost;
			usedCells = Min( m_lastFillCells, usedCells );
		}
		maxCells = FloorF( GameInstance.GetStatsSystem( m_gameInstance ).GetStatValue( GetPlayerControlledObject().GetEntityID(), gamedataStatType.Memory ) );
		fillCells = FloorF( ( ( ( Float )( maxCells ) ) * value ) * 0.01 );
		if( !( GetRootWidget().IsVisible() ) || ( ( ( m_lastFillCells == fillCells ) && ( m_lastUsedCells == usedCells ) ) && ( m_lastMaxCells == maxCells ) ) )
		{
			return false;
		}
		m_lastFillCells = fillCells;
		m_lastUsedCells = usedCells;
		m_lastMaxCells = maxCells;
		UpdateMemoryBar();
	}

	private function UpdateMemoryBar()
	{
		var i : Int32;
		var textureWidget : weak< inkImage >;
		if( m_data.Size() > 0 )
		{
			QuickhackModule.RequestRefreshQuickhackMenu( GetPlayerControlledObject().GetGame(), m_data[ 0 ].m_actionOwner );
		}
		UpdateQuickhacksMemoryBarSize( m_lastMaxCells );
		inkTextRef.SetText( m_avaliableMemory, ( ( ( GetLocalizedText( "UI-ResourceExports-CyberdeckMemory" ) + ": " ) + m_lastFillCells ) + "/" ) + m_lastMaxCells );
		for( i = 0; i < m_selectedMemoryLoop.Size(); i += 1 )
		{
			m_selectedMemoryLoop[ i ].Stop();
			m_selectedMemoryLoop[ i ] = NULL;
		}
		m_selectedMemoryLoop.Clear();
		for( i = 0; i < m_quickhackBarArray.Size(); i += 1 )
		{
			if( i >= m_lastMaxCells )
			{
				m_quickhackBarArray[ i ].SetVisible( false );
			}
			else
			{
				textureWidget = ( ( inkImage )( m_quickhackBarArray[ i ].GetWidgetByIndex( 0 ) ) );
				if( i < m_lastFillCells )
				{
					if( i >= ( m_lastFillCells - m_lastUsedCells ) )
					{
						m_quickhackBarArray[ i ].SetState( 'Used' );
						PlayLibraryAnimationOnTargets( 'memorySelected_out', SelectWidgets( m_quickhackBarArray[ i ] ) );
						m_selectedMemoryLoop.PushBack( PlayLibraryAnimationOnTargets( 'memorySelected', SelectWidgets( m_quickhackBarArray[ i ] ), GetAnimOptionsInfiniteLoop( inkanimLoopType.Cycle ) ) );
					}
					else
					{
						m_quickhackBarArray[ i ].SetState( 'Default' );
						PlayLibraryAnimationOnTargets( 'memorySelected_out', SelectWidgets( m_quickhackBarArray[ i ] ) );
					}
					textureWidget.SetTexturePart( 'charge_free' );
				}
				else
				{
					textureWidget.SetTexturePart( 'charge_empty' );
					m_quickhackBarArray[ i ].SetState( 'Empty' );
					PlayLibraryAnimationOnTargets( 'memorySelected_out', SelectWidgets( m_quickhackBarArray[ i ] ) );
				}
				m_quickhackBarArray[ i ].SetVisible( true );
			}
		}
		DeplenishMemoryCells();
	}

	private function DeplenishMemoryCells()
	{
		if( ( m_memorySpendIndex > m_lastFillCells ) && ( m_memorySpendIndex > 0 ) )
		{
			m_memorySpendCounter = ( m_lastFillCells + m_lastUsedCells ) - 1;
			m_memorySpendIndex = m_lastFillCells;
			if( m_memorySpendAnimation )
			{
				m_memorySpendAnimation.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnDeplenishMemoryCells' );
				m_memorySpendAnimation = NULL;
			}
			OnDeplenishMemoryCells();
		}
		m_memorySpendIndex = m_lastFillCells;
	}

	protected event OnDeplenishMemoryCells( optional e : inkAnimProxy )
	{
		var textureWidget : weak< inkImage >;
		if( m_memorySpendAnimation )
		{
			m_memorySpendAnimation.Stop();
			m_memorySpendAnimation.UnregisterFromCallback( inkanimEventType.OnFinish, this, 'OnDeplenishMemoryCells' );
			m_memorySpendAnimation = NULL;
			textureWidget = ( ( inkImage )( m_quickhackBarArray[ m_memorySpendCounter ].GetWidgetByIndex( 0 ) ) );
			textureWidget.SetTexturePart( 'charge_empty' );
			m_quickhackBarArray[ m_memorySpendCounter ].SetState( 'Empty' );
			PlayLibraryAnimationOnTargets( 'memorySelected_out', SelectWidgets( m_quickhackBarArray[ m_memorySpendCounter ] ) );
			m_memorySpendCounter -= 1;
		}
		if( m_memorySpendCounter > ( m_memorySpendIndex - 1 ) )
		{
			textureWidget = ( ( inkImage )( m_quickhackBarArray[ m_memorySpendCounter ].GetWidgetByIndex( 0 ) ) );
			textureWidget.SetTexturePart( 'charge_free' );
			m_quickhackBarArray[ m_memorySpendCounter ].SetState( 'Default' );
			m_memorySpendAnimation = PlayLibraryAnimationOnTargets( 'memorySpend', SelectWidgets( m_quickhackBarArray[ m_memorySpendCounter ] ) );
			m_memorySpendAnimation.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnDeplenishMemoryCells' );
		}
	}

	private function PopulateData( data : array< QuickhackData > )
	{
		var i, count : Int32;
		m_lastMemoryWarningTransitionAnimName = '';
		m_listController.Clear( false );
		count = data.Size();
		for( i = 0; i < count; i += 1 )
		{
			data[ i ].m_maxListSize = count;
			m_listController.PushData( data[ i ], false );
		}
		m_listController.Refresh();
		if( ( count > 0 ) && data[ 0 ].m_noQuickhackData )
		{
			inkWidgetRef.SetVisible( m_listWidget, false );
			inkWidgetRef.SetVisible( m_warningWidget, false );
			inkWidgetRef.SetVisible( m_costReductionPanel, false );
			inkCompoundRef.RemoveAllChildren( m_noQuickhacks );
			AsyncSpawnFromLocal( inkCompoundRef.Get( m_noQuickhacks ), 'noQuickhacks' );
			inkCompoundRef.SetVisible( m_noQuickhacks, true );
			inkWidgetRef.SetVisible( m_openCyberdeckBtn, true );
			inkWidgetRef.SetVisible( m_executeAndCloseBtn, false );
			inkWidgetRef.SetVisible( m_tutorialButton, false );
			inkWidgetRef.SetVisible( m_middleDots, true );
			m_listController.SetSelectedIndex( 0, true );
		}
		else
		{
			inkWidgetRef.SetVisible( m_middleDots, false );
			inkWidgetRef.SetVisible( m_listWidget, true );
			inkCompoundRef.SetVisible( m_noQuickhacks, false );
			inkWidgetRef.SetVisible( m_openCyberdeckBtn, false );
			inkWidgetRef.SetVisible( m_executeAndCloseBtn, true );
			inkWidgetRef.SetVisible( m_tutorialButton, true );
			if( ( m_lastCompiledTarget != m_data[ 0 ].m_actionOwner ) || !( m_listController.HasValidSelection() ) )
			{
				m_listController.SetSelectedIndex( 0, true );
			}
			else
			{
				m_listController.SetSelectedIndex( m_listController.GetSelectedIndex(), true );
			}
		}
	}

	private function RegisterCooldownStatPoolUpdate() : Bool
	{
		var i, i1 : Int32;
		var buffList : array< BuffInfo >;
		buffList = ( ( array< BuffInfo > )( GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerBioMonitor ).GetVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.DebuffsList ) ) );
		m_statPoolListenersIndexes.Clear();
		if( buffList.Size() == 0 )
		{
			return false;
		}
		for( i = 0; i < m_data.Size(); i += 1 )
		{
			for( i1 = 0; i1 < buffList.Size(); i1 += 1 )
			{
				if( !( TDBID.IsValid( m_data[ i ].m_cooldownTweak ) ) || !( TDBID.IsValid( buffList[ i1 ].buffID ) ) )
				{
					continue;
				}
				if( buffList[ i1 ].buffID == m_data[ i ].m_cooldownTweak )
				{
					if( !( m_statPoolListenersIndexes.Contains( i ) ) )
					{
						m_statPoolListenersIndexes.PushBack( i );
					}
					break;
				}
			}
		}
		if( m_statPoolListenersIndexes.Size() == 0 )
		{
			UnregisterCooldownStatPoolUpdate();
			return false;
		}
		if( !( m_buffListListener ) )
		{
			m_buffListListener = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerBioMonitor ).RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_PlayerBioMonitor.DebuffsList, this, 'OnCooldownStatPoolUpdate' );
		}
		return true;
	}

	private function UnregisterCooldownStatPoolUpdate()
	{
		var i : Int32;
		if( m_buffListListener )
		{
			GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerBioMonitor ).UnregisterDelayedListener( GetAllBlackboardDefs().UI_PlayerBioMonitor.DebuffsList, m_buffListListener );
			for( i = 0; i < m_statPoolListenersIndexes.Size(); i += 1 )
			{
				( ( QuickhacksListItemController )( m_listController.GetItemAt( m_statPoolListenersIndexes[ i ] ).GetController() ) ).SetCooldownVisibility( false );
			}
			UpdateRecompileTime( false, 0.0 );
		}
	}

	protected event OnCooldownStatPoolUpdate( value : Variant )
	{
		var buffList : array< BuffInfo >;
		var wasMatched : Bool;
		var i, i1 : Int32;
		buffList = ( ( array< BuffInfo > )value );
		if( buffList.Size() == 0 )
		{
			UnregisterCooldownStatPoolUpdate();
			QuickhackModule.RequestRefreshQuickhackMenu( GetPlayerControlledObject().GetGame(), m_data[ 0 ].m_actionOwner );
			return false;
		}
		for( i = m_statPoolListenersIndexes.Size() - 1; i >= 0; i -= 1 )
		{
			wasMatched = false;
			for( i1 = 0; i1 < buffList.Size(); i1 += 1 )
			{
				if( buffList[ i1 ].buffID == m_data[ m_statPoolListenersIndexes[ i ] ].m_cooldownTweak )
				{
					( ( QuickhacksListItemController )( m_listController.GetItemAt( m_statPoolListenersIndexes[ i ] ).GetController() ) ).UpdateCooldown( buffList[ i1 ].timeRemaining );
					wasMatched = true;
					if( IsCurrentSelectionOnStatPoolIndexes( i ) )
					{
						UpdateRecompileTime( true, buffList[ i1 ].timeRemaining );
					}
					break;
				}
			}
			if( !( wasMatched ) )
			{
				( ( QuickhacksListItemController )( m_listController.GetItemAt( m_statPoolListenersIndexes[ i ] ).GetController() ) ).SetCooldownVisibility( false );
				if( IsCurrentSelectionOnStatPoolIndexes( i ) )
				{
					UpdateRecompileTime( false, buffList[ i1 ].timeRemaining );
				}
				m_statPoolListenersIndexes.Erase( i );
				QuickhackModule.RequestRefreshQuickhackMenu( GetPlayerControlledObject().GetGame(), m_data[ 0 ].m_actionOwner );
			}
		}
	}

	private function IsCurrentSelectionOnStatPoolIndexes() : Bool
	{
		var i : Int32;
		for( i = m_statPoolListenersIndexes.Size() - 1; i >= 0; i -= 1 )
		{
			if( m_data[ m_statPoolListenersIndexes[ i ] ].m_cooldownTweak == m_selectedData.m_cooldownTweak )
			{
				return true;
			}
		}
		return false;
	}

	private function IsCurrentSelectionOnStatPoolIndexes( index : Int32 ) : Bool
	{
		return m_data[ m_statPoolListenersIndexes[ index ] ].m_cooldownTweak == m_selectedData.m_cooldownTweak;
	}

	public static function EActionInactivityResonToLocalizationString( value : EActionInactivityReson ) : String
	{
		switch( value )
		{
			case EActionInactivityReson.Ready:
			{
				return "LocKey#40763";
			}
			case EActionInactivityReson.Locked:
			{
				return "LocKey#40765";
			}
			case EActionInactivityReson.Recompilation:
			{
				return "LocKey#40766";
			}
			case EActionInactivityReson.OutOfMemory:
			{
				return "LocKey#40767";
			}
			case EActionInactivityReson.IsQuickHacked:
			{
				return "LocKey#7004";
			}
			default:
			{
				return "LocKey#40764";
			}
		}
	}

	protected event OnQuickHackScreenOpen( evt : QuickHackScreenOpen )
	{
		if( evt.setToOpen )
		{
			QuickhackModule.RequestCloseQuickhackMenu( GetPlayerControlledObject().GetGame(), GetPlayerControlledObject().GetEntityID() );
		}
		else
		{
			SetVisibility( false );
		}
	}

	protected event OnQuickHackTimeDilationOverride( evt : QuickHackTimeDilationOverride )
	{
		GameInstance.GetBlackboardSystem( m_playerObject.GetGame() ).GetLocalInstanced( m_playerObject.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine ).SetBool( GetAllBlackboardDefs().PlayerStateMachine.OverrideQuickHackPanelDilation, evt.overrideDilationToTutorialPreset );
	}

	protected event OnQuickHackLockHacks( evt : QuickHackLockHacks )
	{
		m_isUILocked = evt.IsLocked;
		HUDManager.LockQHackInput( GetPlayerControlledObject().GetGame(), m_isUILocked );
	}

	public function SendOverclockPreviewEvent( isHovering : Bool, optional cost : Int32, optional justHacked : Bool )
	{
		var overclockPreviewEvent : OverclockDamagePreview;
		overclockPreviewEvent = new OverclockDamagePreview;
		overclockPreviewEvent.IsHovering = isHovering;
		overclockPreviewEvent.JustHacked = justHacked;
		overclockPreviewEvent.MemoryCost = cost;
		QueueEvent( overclockPreviewEvent );
	}

}

class RevealInteractionWheel extends Event
{
	var lookAtObject : weak< GameObject >;
	var commands : array< QuickhackData >;
	var shouldReveal : Bool;
}

class QuickHackScreenOpen extends Event
{
	[ tooltip = "Open or close Quick Hack menu. SetToOpen == true means open | SetToOpen == false means close" ]
	editable var setToOpen : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Control QuickHack menu visibility";
	}

}

class QuickHackTimeDilationOverride extends Event
{
	[ tooltip = "Time that will be set" ]
	editable var overrideDilationToTutorialPreset : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Change time dilation when QuickHack menu is opened to tutorial preset";
	}

}

class QuickHackLockHacks extends Event
{
	[ tooltip = "Is lock active" ]
	editable var IsLocked : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Lock QHack menu that player cannot hack anything as long as lock is on";
	}

}

enum EActionInactivityReson
{
	Ready = 0,
	Locked = 1,
	Recompilation = 2,
	OutOfMemory = 3,
	IsQuickHacked = 4,
	Invalid = 5,
}

class QuickhacksVulnerabilityLogicController extends inkLogicController
{
	private editable var m_textField : inkTextRef;

	public function SetText( locKey : CName )
	{
		inkTextRef.SetLocalizedTextScript( m_textField, locKey );
	}

}

class DelayedDescriptionIntro extends Event
{
}

class ProgramEntry extends IScriptable
{
	var m_itemID : ItemID;
	var m_equippedItem : weak< Item_Record >;
	var m_abilities : array< InventoryItemAbility >;

	public static function Make( itemID : ItemID, equippedItem : weak< Item_Record > ) : ProgramEntry
	{
		var entry : ProgramEntry;
		entry = new ProgramEntry;
		entry.m_itemID = itemID;
		entry.m_equippedItem = equippedItem;
		entry.m_abilities = GetSpecialAbilities( equippedItem );
		return entry;
	}

	private static function GetSpecialAbilities( equippedItem : weak< Item_Record > ) : array< InventoryItemAbility >
	{
		var abilities : array< InventoryItemAbility >;
		var GLPAbilities : array< weak< GameplayLogicPackage_Record > >;
		var i, limit : Int32;
		var ability : InventoryItemAbility;
		var uiData : weak< GameplayLogicPackageUIData_Record >;
		equippedItem.OnAttach( GLPAbilities );
		for( i = 0, limit = GLPAbilities.Size(); i < limit; i += 1 )
		{
			if( GLPAbilities[ i ] )
			{
				uiData = GLPAbilities[ i ].UIData();
				if( uiData )
				{
					ability = InventoryItemAbility( uiData.IconPath(), uiData.LocalizedName(), uiData.LocalizedDescription(), UILocalizationDataPackage.FromLogicUIDataPackage( uiData,  ) );
					abilities.PushBack( ability );
				}
			}
		}
		GLPAbilities.Clear();
		equippedItem.OnEquip( GLPAbilities );
		for( i = 0, limit = GLPAbilities.Size(); i < limit; i += 1 )
		{
			if( GLPAbilities[ i ] )
			{
				uiData = GLPAbilities[ i ].UIData();
				if( uiData )
				{
					ability = InventoryItemAbility( uiData.IconPath(), uiData.LocalizedName(), uiData.LocalizedDescription(), UILocalizationDataPackage.FromLogicUIDataPackage( uiData ) );
					abilities.PushBack( ability );
				}
			}
		}
		return abilities;
	}

}

class EquippedQuickHackData extends IScriptable
{
	private var m_programEntries : array< ProgramEntry >;

	public static function Make( player : weak< PlayerPuppet > ) : EquippedQuickHackData
	{
		var data : EquippedQuickHackData;
		var systemReplacementID : ItemID;
		var parts : array< SPartSlots >;
		data = new EquippedQuickHackData;
		systemReplacementID = EquipmentSystem.GetData( player ).GetActiveItem( gamedataEquipmentArea.SystemReplacementCW );
		parts = ItemModificationSystem.GetAllSlots( player, systemReplacementID );
		if( EquipmentSystem.IsCyberdeckEquipped( player ) )
		{
			data.m_programEntries = GetShardsQuickHacks( parts );
		}
		return data;
	}

	public function GetAbilitiesByItemID( itemID : ItemID ) : array< InventoryItemAbility >
	{
		var i : Int32;
		var entry : ProgramEntry;
		var emptyArray : array< InventoryItemAbility >;
		var currentItem : ItemID;
		var currentTweakID : TweakDBID;
		var itemTweakID : TweakDBID;
		for( i = 0; i < m_programEntries.Size(); i += 1 )
		{
			entry = m_programEntries[ i ];
			currentItem = entry.m_itemID;
			currentTweakID = ItemID.GetTDBID( currentItem );
			itemTweakID = ItemID.GetTDBID( itemID );
			if( currentTweakID == itemTweakID )
			{
				return entry.m_abilities;
			}
		}
		return emptyArray;
	}

	private static function GetShardsQuickHacks( parts : array< SPartSlots > ) : array< ProgramEntry >
	{
		var entries : array< ProgramEntry >;
		var equippedShard : weak< Item_Record >;
		var part : SPartSlots;
		var installedItem : ItemID;
		var programEntry : ProgramEntry;
		var i : Int32;
		for( i = 0; i < parts.Size(); i += 1 )
		{
			part = parts[ i ];
			installedItem = part.installedPart;
			equippedShard = RPGManager.GetItemRecord( installedItem );
			if( equippedShard )
			{
				programEntry = ProgramEntry.Make( installedItem, equippedShard );
				entries.PushBack( programEntry );
			}
		}
		return entries;
	}

}

