class QuestForceFakeElevatorArrows extends ActionBool
{

	public function SetProperties( property : Bool )
	{
		if( property )
		{
			actionName = 'QuestForceFakeElevatorArrowsUp';
		}
		else
		{
			actionName = 'QuestForceFakeElevatorArrowsDown';
		}
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestForceFakeElevatorArrows', property, 'QuestForceFakeElevatorArrows', 'QuestForceFakeElevatorArrows' );
	}

}

class QuestResetFakeElevatorArrows extends ActionBool
{

	public function SetProperties()
	{
		actionName = 'QuestResetFakeElevatorArrows';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( 'QuestResetFakeElevatorArrows', true, 'QuestResetFakeElevatorArrows', 'QuestResetFakeElevatorArrows' );
	}

}

struct DeviceCounter
{
	var devices : array< DeviceComponentPS >;
	var systemType : EVirtualSystem;
}

class TerminalSetState extends Event
{
	var state : gameinteractionsReactionState;
}

class TerminalController extends MasterController
{

	public const override function GetPS() : TerminalControllerPS
	{
		return ( ( TerminalControllerPS )( GetBasePS() ) );
	}

}

struct TerminalSetup
{
	[ rangeMin = "1" ][ rangeMax = "9" ]
	instanceeditable var m_minClearance : Int32;
	default m_minClearance = 1;
	[ rangeMin = "5" ][ rangeMax = "10" ]
	instanceeditable var m_maxClearance : Int32;
	default m_maxClearance = 10;
	[ tooltip = "If on then ignores that slave device is secured and allows player to access other devices" ]
	instanceeditable persistent var m_ignoreSlaveAuthorizationModule : Bool;
	default m_ignoreSlaveAuthorizationModule = false;
	instanceeditable persistent var m_allowRemoteAuthorization : Bool;
	instanceeditable var m_shouldForceVirtualSystem : Bool;
	default m_shouldForceVirtualSystem = true;
}

class TerminalControllerPS extends MasterControllerPS
{
	default m_deviceName = "LocKey#112";
	default m_tweakDBRecord = T"Devices.Terminal";
	default m_tweakDBDescriptionRecord = T"device_descriptions.Terminal";
	default m_hasAuthorizationModule = true;
	default m_hasUICameraZoom = false;
	private instanceeditable persistent var m_terminalSetup : TerminalSetup;
	private instanceeditable inlined var m_terminalSkillChecks : HackEngContainer;
	protected var m_spawnedSystems : array< VirtualSystemPS >;
	private instanceeditable var m_useKeyloggerHack : Bool;
	[ category = "UI" ]
	protected instanceeditable var m_shouldShowTerminalTitle : Bool;
	[ category = "Glitch" ]
	protected instanceeditable var m_defaultGlitchVideoPath : ResRef;
	[ category = "Glitch" ]
	protected instanceeditable var m_broadcastGlitchVideoPath : ResRef;
	protected persistent var m_state : gameinteractionsReactionState;
	protected persistent var m_forcedElevatorArrowsState : EForcedElevatorArrowsState;

	protected event OnInstantiated()
	{
		super.OnInstantiated();
		if( !( IsStringValid( m_deviceName ) ) )
		{
			m_deviceName = "Gameplay-Devices-DisplayNames-Terminal";
		}
	}

	protected override function GameAttached() {}

	protected override function GetSkillCheckContainerForSetup() : BaseSkillCheckContainer
	{
		return m_terminalSkillChecks;
	}

	protected override function LogicReady()
	{
		InitializeVirtualSystems();
		super.LogicReady();
	}

	private function InitializeVirtualSystems_Test()
	{
		var i, k : Int32;
		var devices : array< DeviceComponentPS >;
		var systemsData : array< DeviceCounter >;
		var data : DeviceCounter;
		if( !( m_terminalSetup.m_shouldForceVirtualSystem ) )
		{
			return;
		}
		devices = GetImmediateSlaves();
		while( devices.Size() > 0 )
		{
			i = devices.Size() - 1;
			if( GetMatchingVirtualSystemsData( devices[ i ], devices, data ) )
			{
				systemsData.PushBack( data );
				for( k = data.devices.Size() - 1; k >= 0; k -= 1 )
				{
					devices.Remove( data.devices[ k ] );
				}
			}
			else
			{
				devices.Erase( i );
			}
		}
		for( i = 0; i < systemsData.Size(); i += 1 )
		{
			if( systemsData[ i ].systemType == EVirtualSystem.SurveillanceSystem )
			{
				SpawnSurveillanceSystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.SecuritySystem )
			{
				SpawnSecuritySystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.DoorSystem )
			{
				SpawnDoorSystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.MediaSystem )
			{
				SpawnMediaSystemUI( systemsData[ i ].devices );
			}
		}
	}

	private function InitializeVirtualSystems()
	{
		var i, k : Int32;
		var devices : array< DeviceComponentPS >;
		var systemsData : array< DeviceCounter >;
		var data : DeviceCounter;
		if( !( m_terminalSetup.m_shouldForceVirtualSystem ) )
		{
			return;
		}
		devices = GetImmediateSlaves();
		while( devices.Size() > 0 )
		{
			i = devices.Size() - 1;
			if( GetMatchingVirtualSystemsData( devices[ i ], devices, data ) )
			{
				systemsData.PushBack( data );
				for( k = data.devices.Size() - 1; k >= 0; k -= 1 )
				{
					devices.Remove( data.devices[ k ] );
				}
			}
			else
			{
				devices.Erase( i );
			}
		}
		for( i = 0; i < systemsData.Size(); i += 1 )
		{
			if( systemsData[ i ].systemType == EVirtualSystem.SurveillanceSystem )
			{
				SpawnSurveillanceSystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.SecuritySystem )
			{
				SpawnSecuritySystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.DoorSystem )
			{
				SpawnDoorSystemUI( systemsData[ i ].devices );
			}
			else if( systemsData[ i ].systemType == EVirtualSystem.MediaSystem )
			{
				SpawnMediaSystemUI( systemsData[ i ].devices );
			}
		}
	}

	protected const override function GetClearance() : Clearance
	{
		return Clearance.CreateClearance( m_terminalSetup.m_minClearance, m_terminalSetup.m_maxClearance );
	}

	public const function ShouldShowTerminalTitle() : Bool
	{
		return m_shouldShowTerminalTitle;
	}

	public const function GetDefaultGlitchVideoPath() : ResRef
	{
		return m_defaultGlitchVideoPath;
	}

	public const function GetBroadcastGlitchVideoPath() : ResRef
	{
		if( ResRef.IsValid( m_broadcastGlitchVideoPath ) )
		{
			return m_broadcastGlitchVideoPath;
		}
		else
		{
			return R"base\movies\misc\distraction_generic.bk2";
		}
	}

	protected const override function GenerateContext( requestType : gamedeviceRequestType, providedClearance : Clearance, optional providedProcessInitiator : GameObject, optional providedRequestor : EntityID ) : GetActionsContext
	{
		var generatedContext : GetActionsContext;
		generatedContext = super.GenerateContext( requestType, providedClearance, providedProcessInitiator, providedRequestor );
		generatedContext.ignoresAuthorization = m_terminalSetup.m_ignoreSlaveAuthorizationModule;
		generatedContext.allowsRemoteAuthorization = m_terminalSetup.m_allowRemoteAuthorization;
		return generatedContext;
	}

	private function GetMatchingVirtualSystemsData( device : DeviceComponentPS, const listToCheck : ref< array< DeviceComponentPS > >, data : ref< DeviceCounter > ) : Bool
	{
		var i : Int32;
		var type : EVirtualSystem;
		var returnValue : Bool;
		if( device == NULL )
		{
			return false;
		}
		returnValue = false;
		type = device.GetVirtualSystemType();
		if( type != EVirtualSystem.None )
		{
			data.systemType = type;
			for( i = 0; i < listToCheck.Size(); i += 1 )
			{
				if( device == listToCheck[ i ] )
				{
					continue;
				}
				if( listToCheck[ i ].GetVirtualSystemType() == type )
				{
					data.devices.PushBack( listToCheck[ i ] );
					returnValue = true;
				}
			}
		}
		if( returnValue )
		{
			data.devices.PushBack( device );
		}
		return returnValue;
	}

	private function HasMatchingVirtualSystemType( device : DeviceComponentPS, const listToCheck : ref< array< DeviceComponentPS > >, out type : EVirtualSystem ) : Bool
	{
		var i : Int32;
		if( device == NULL )
		{
			return false;
		}
		type = device.GetVirtualSystemType();
		if( type != EVirtualSystem.None )
		{
			for( i = 0; i < listToCheck.Size(); i += 1 )
			{
				if( device == listToCheck[ i ] )
				{
					continue;
				}
				if( listToCheck[ i ].GetVirtualSystemType() == type )
				{
					return true;
				}
			}
		}
		return false;
	}

	public const function HasAnyVirtualSystem() : Bool
	{
		return m_spawnedSystems.Size() > 0;
	}

	public const function GetVirtualSystemsCount() : Int32
	{
		return m_spawnedSystems.Size();
	}

	public const function IsPartOfAnyVirtualSystem( slave : DeviceComponentPS ) : Bool
	{
		var vs : VirtualSystemPS;
		return GetVirtualSystem( slave, vs );
	}

	public const function IsPartOfAnyVirtualSystem( slaveID : PersistentID ) : Bool
	{
		var vs : VirtualSystemPS;
		return GetVirtualSystem( slaveID, vs );
	}

	public const override function GetVirtualSystem( slave : DeviceComponentPS, out vs : VirtualSystemPS ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_spawnedSystems.Size(); i += 1 )
		{
			if( m_spawnedSystems[ i ].IsPartOfSystem( slave ) )
			{
				vs = m_spawnedSystems[ i ];
				return true;
			}
		}
		return false;
	}

	public const override function GetVirtualSystem( id : PersistentID, out vs : VirtualSystemPS ) : Bool
	{
		var i : Int32;
		for( i = 0; i < m_spawnedSystems.Size(); i += 1 )
		{
			if( id == m_spawnedSystems[ i ].GetID() || m_spawnedSystems[ i ].IsPartOfSystem( id ) )
			{
				vs = m_spawnedSystems[ i ];
				return true;
			}
		}
		return false;
	}

	public override function GetSlaveDeviceWidget( deviceID : PersistentID ) : SDeviceWidgetPackage
	{
		var vs : VirtualSystemPS;
		if( GetVirtualSystem( deviceID, vs ) )
		{
			return vs.GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) );
		}
		return super.GetSlaveDeviceWidget( deviceID );
	}

	public override function GetDeviceWidgets() : array< SDeviceWidgetPackage >
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var widgetsData : array< SDeviceWidgetPackage >;
		var currentWidget : SDeviceWidgetPackage;
		if( !( HasAnyVirtualSystem() ) )
		{
			return super.GetDeviceWidgets();
		}
		for( i = 0; i < m_spawnedSystems.Size(); i += 1 )
		{
			m_spawnedSystems[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ), widgetsData );
		}
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( IsPartOfAnyVirtualSystem( devices[ i ] ) )
			{
				continue;
			}
			currentWidget = devices[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) );
			if( currentWidget.isValid )
			{
				widgetsData.PushBack( devices[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) ) );
			}
		}
		return widgetsData;
	}

	public override function GetThumbnailWidgets() : array< SThumbnailWidgetPackage >
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var widgetsData : array< SThumbnailWidgetPackage >;
		var currentWidget : SThumbnailWidgetPackage;
		if( !( HasAnyVirtualSystem() ) )
		{
			return super.GetThumbnailWidgets();
		}
		for( i = 0; i < m_spawnedSystems.Size(); i += 1 )
		{
			widgetsData.PushBack( m_spawnedSystems[ i ].GetThumbnailWidget() );
		}
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ].GetID() == GetID() )
			{
				continue;
			}
			if( IsPartOfAnyVirtualSystem( devices[ i ] ) )
			{
				continue;
			}
			currentWidget = devices[ i ].GetThumbnailWidget();
			if( currentWidget.isValid )
			{
				widgetsData.PushBack( currentWidget );
			}
		}
		return widgetsData;
	}

	private function SpawnSurveillanceSystemUI( const slavesToConnect : ref< array< DeviceComponentPS > > )
	{
		var virtualSystem : SurveillanceSystemUIPS;
		virtualSystem = ( ( SurveillanceSystemUIPS )( SpawnVirtualPS( GetGameInstance(), GetMyEntityID(), 'virtualPS', 'SurveillanceSystemUIPS' ) ) );
		if( virtualSystem )
		{
			virtualSystem.Initialize( slavesToConnect, this );
			m_spawnedSystems.PushBack( virtualSystem );
		}
	}

	private function SpawnSecuritySystemUI( const slavesToConnect : ref< array< DeviceComponentPS > > )
	{
		var virtualSystem : SecuritySystemUIPS;
		virtualSystem = ( ( SecuritySystemUIPS )( SpawnVirtualPS( GetGameInstance(), GetMyEntityID(), 'virtualPS', 'SecuritySystemUIPS' ) ) );
		if( virtualSystem )
		{
			virtualSystem.Initialize( slavesToConnect, this );
			m_spawnedSystems.PushBack( virtualSystem );
		}
	}

	private function SpawnDoorSystemUI( const slavesToConnect : ref< array< DeviceComponentPS > > )
	{
		var virtualSystem : DoorSystemUIPS;
		virtualSystem = ( ( DoorSystemUIPS )( SpawnVirtualPS( GetGameInstance(), GetMyEntityID(), 'virtualPS', 'DoorSystemUIPS' ) ) );
		if( virtualSystem )
		{
			virtualSystem.Initialize( slavesToConnect, this );
			m_spawnedSystems.PushBack( virtualSystem );
		}
	}

	private function SpawnMediaSystemUI( const slavesToConnect : ref< array< DeviceComponentPS > > )
	{
		var virtualSystem : MediaSystemUIPS;
		virtualSystem = ( ( MediaSystemUIPS )( SpawnVirtualPS( GetGameInstance(), GetMyEntityID(), 'virtualPS', 'DoorSystemUIPS' ) ) );
		if( virtualSystem )
		{
			virtualSystem.Initialize( slavesToConnect, this );
			m_spawnedSystems.PushBack( virtualSystem );
		}
	}

	public override function OnRequestDeviceWidgetUpdate( evt : RequestDeviceWidgetUpdateEvent )
	{
		RequestDeviceWidgetsUpdate( GetBlackboard(), evt.requester );
	}

	public function GetForcedElevatorArrowsState() : EForcedElevatorArrowsState
	{
		return m_forcedElevatorArrowsState;
	}

	public override function ActionToggleON() : ToggleON
	{
		var action : ToggleON;
		action = new ToggleON;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleOnClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState );
		action.AddDeviceName( m_deviceName );
		if( IsOFF() )
		{
			action.CreateInteraction();
		}
		action.CreateActionWidgetPackage();
		return action;
	}

	protected function ActionQuestForceFakeElevatorArrows( isArrowsUp : Bool ) : QuestForceFakeElevatorArrows
	{
		var action : QuestForceFakeElevatorArrows;
		action = new QuestForceFakeElevatorArrows;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties( isArrowsUp );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionQuestResetFakeElevatorArrows() : QuestResetFakeElevatorArrows
	{
		var action : QuestResetFakeElevatorArrows;
		action = new QuestResetFakeElevatorArrows;
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		return action;
	}

	protected function ActionInstallKeylogger() : InstallKeylogger
	{
		var action : InstallKeylogger;
		action = new InstallKeylogger;
		action.SetUp( this );
		action.SetProperties();
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public override function GetActions( out outActions : array< DeviceAction >, context : GetActionsContext ) : Bool
	{
		if( !( super.GetActions( outActions, context ) ) )
		{
			return false;
		}
		if( IsDisabled() )
		{
			return false;
		}
		if( BaseDeviceStatus.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionDeviceStatus() );
		}
		if( IsUnpowered() )
		{
			return false;
		}
		if( ToggleON.IsDefaultConditionMet( this, context ) )
		{
			outActions.PushBack( ActionToggleON() );
		}
		if( !( IsON() ) )
		{
			return false;
		}
		SetActionIllegality( outActions, m_illegalActions.regularActions );
		return true;
	}

	protected const override function CanCreateAnyQuickHackActions() : Bool
	{
		return true;
	}

	protected override function GetQuickHackActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		var currentAction : ScriptableDeviceAction;
		currentAction = ActionInstallKeylogger();
		currentAction.SetObjectActionID( T"DeviceAction.DataExtractionClassHack" );
		currentAction.SetInactiveWithReason( m_useKeyloggerHack && !( m_isKeyloggerInstalled ), "LocKey#7014" );
		outActions.PushBack( currentAction );
		currentAction = ActionQuickHackToggleOpen();
		currentAction.SetObjectActionID( T"DeviceAction.ToggleStateClassHack" );
		currentAction.SetInactiveWithReason( !( m_useKeyloggerHack ) && QuickHackToggleOpen.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		if( !( IsAuthorizationModuleOn() ) )
		{
			SetActionsInactiveAll( outActions, "LocKey#7015" );
		}
		currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenSuicide", T"QuickHack.GrenadeHackBase" );
		currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
		currentAction.SetInactiveWithReason( GlitchScreen.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenBlind", T"QuickHack.BlindHack" );
		currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
		currentAction.SetInactiveWithReason( GlitchScreen.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenGrenade", T"QuickHack.GrenadeHackBase" );
		currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
		currentAction.SetInactiveWithReason( GlitchScreen.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		currentAction = ActionQuickHackDistraction();
		currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
		currentAction.SetInactiveWithReason( QuickHackDistraction.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		if( IsGlitching() || IsDistracting() )
		{
			SetActionsInactiveAll( outActions, "LocKey#7004" );
		}
		FinalizeGetQuickHackActions( outActions, context );
	}

	public override function GetQuestActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		super.GetQuestActions( outActions, context );
		outActions.PushBack( ActionQuestForceFakeElevatorArrows( true ) );
		outActions.PushBack( ActionQuestForceFakeElevatorArrows( false ) );
		outActions.PushBack( ActionQuestResetFakeElevatorArrows() );
		return;
	}

	public export function OnQuestForceFakeElevatorArrows( evt : QuestForceFakeElevatorArrows ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetAll();
		Notify( notifier, evt );
		if( ( ( Bool )evt.prop.first ) )
		{
			m_forcedElevatorArrowsState = EForcedElevatorArrowsState.ArrowsUp;
		}
		else
		{
			m_forcedElevatorArrowsState = EForcedElevatorArrowsState.ArrowsDown;
		}
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	public function OnQuestResetFakeElevatorArrows( evt : QuestResetFakeElevatorArrows ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetAll();
		Notify( notifier, evt );
		m_forcedElevatorArrowsState = EForcedElevatorArrowsState.Disabled;
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	protected virtual function ActionQuickHackToggleOpen() : QuickHackToggleOpen
	{
		var action : QuickHackToggleOpen;
		action = new QuickHackToggleOpen;
		action.clearanceLevel = DefaultActionsParametersHolder.GetToggleOpenClearance();
		action.SetUp( this );
		action.SetProperties( false );
		action.AddDeviceName( m_deviceName );
		action.CreateInteraction();
		return action;
	}

	public function OnQuickHackToggleOpen( evt : QuickHackToggleOpen ) : EntityNotificationType
	{
		TurnAuthorizationModuleOFF();
		DisbleAuthorizationOnSlaves();
		ResolveOtherSkillchecks();
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	public override function OnActionEngineering( evt : ActionEngineering ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetAll();
		if( !( evt.WasPassed() ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		super.OnActionEngineering( evt );
		if( evt.IsCompleted() )
		{
			DisbleAuthorizationOnSlaves();
			ResolveOtherSkillchecks();
			RPGManager.GiveReward( evt.GetExecutor().GetGame(), T"RPGActionRewards.ExtractPartsTerminal" );
			Notify( notifier, evt );
			return EntityNotificationType.SendPSChangedEventToEntity;
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function OnActionInstallKeylogger( evt : InstallKeylogger ) : EntityNotificationType
	{
		m_isKeyloggerInstalled = true;
		InstallKeyloggerOnSlaves();
		return EntityNotificationType.SendPSChangedEventToEntity;
	}

	public const function IsOwningSecuritySystem( out secSys : SecuritySystemControllerPS ) : Bool
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		GetChildren( slaves );
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			if( ( ( SecuritySystemControllerPS )( slaves[ i ] ) ) )
			{
				secSys = ( ( SecuritySystemControllerPS )( slaves[ i ] ) );
				return true;
			}
		}
		return false;
	}

	protected override function ResolvePersonalLinkConnection( evt : TogglePersonalLink, abortOperations : Bool )
	{
		var secSys : SecuritySystemControllerPS;
		super.ResolvePersonalLinkConnection( evt, abortOperations );
		if( IsOwningSecuritySystem( secSys ) && IsPersonalLinkConnected() )
		{
			ExecutePSAction( ActionTakeOverSecuritySystem( evt.GetExecutor() ), secSys );
		}
	}

	protected override function ResolveOtherSkillchecks()
	{
		if( m_skillCheckContainer )
		{
			if( m_skillCheckContainer.GetHackingSlot() && m_skillCheckContainer.GetHackingSlot().IsActive() )
			{
				m_skillCheckContainer.GetHackingSlot().SetIsActive( false );
				m_skillCheckContainer.GetHackingSlot().SetIsPassed( true );
			}
			if( m_skillCheckContainer.GetEngineeringSlot() && m_skillCheckContainer.GetEngineeringSlot().IsActive() )
			{
				m_skillCheckContainer.GetEngineeringSlot().SetIsActive( false );
				m_skillCheckContainer.GetEngineeringSlot().SetIsPassed( true );
			}
		}
		m_authorizationProperties.m_isAuthorizationModuleOn = false;
	}

	public override function OnAuthorizeUser( evt : AuthorizeUser ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetAll();
		if( UserAuthorizationAttempt( evt.GetExecutor().GetEntityID(), evt.GetEnteredPassword() ) )
		{
			AuthorizeUserOnSlaves( evt.GetExecutor(), evt.GetEnteredPassword() );
			ResolveOtherSkillchecks();
		}
		Notify( notifier, evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public function OnSetState( evt : TerminalSetState ) : EntityNotificationType
	{
		if( ( evt.state == gameinteractionsReactionState.Starting || evt.state == gameinteractionsReactionState.Finishing ) || evt.state == gameinteractionsReactionState.Canceling )
		{
			m_state = evt.state;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public const function QuestCondition_IsStarted() : Bool
	{
		return m_state == gameinteractionsReactionState.Starting;
	}

	public const function QuestCondition_IsFinished() : Bool
	{
		return m_state == gameinteractionsReactionState.Finishing || m_state == gameinteractionsReactionState.Canceling;
	}

	protected function DisbleAuthorizationOnSlaves()
	{
		var devices : array< DeviceComponentPS >;
		var extractedAction : ScriptableDeviceAction;
		var i : Int32;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ] == this )
			{
				continue;
			}
			ExtractActionFromSlave( devices[ i ], 'SetAuthorizationModuleOFF', ( ( DeviceAction )( extractedAction ) ) );
			if( extractedAction )
			{
				extractedAction.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
				GetPersistencySystem().QueuePSDeviceEvent( extractedAction );
			}
		}
	}

	protected function EnableAuthorizationOnSlaves()
	{
		var devices : array< DeviceComponentPS >;
		var extractedAction : ScriptableDeviceAction;
		var i : Int32;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ] == this )
			{
				continue;
			}
			ExtractActionFromSlave( devices[ i ], 'SetAuthorizationModuleON', ( ( DeviceAction )( extractedAction ) ) );
			if( extractedAction )
			{
				extractedAction.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
				GetPersistencySystem().QueuePSDeviceEvent( extractedAction );
			}
		}
	}

	protected function AuthorizeUserOnSlaves( userToAuthorize : weak< GameObject >, optional password : CName )
	{
		var devices : array< DeviceComponentPS >;
		var i : Int32;
		var device : ScriptableDeviceComponentPS;
		var action : ScriptableDeviceAction;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ] == this )
			{
				continue;
			}
			device = ( ( ScriptableDeviceComponentPS )( devices[ i ] ) );
			if( device )
			{
				action = ( ( ScriptableDeviceAction )( device.GetActionByName( 'AuthorizeUser', GenerateContext( gamedeviceRequestType.External, GetClearance(), userToAuthorize, GetMyEntityID() ) ) ) );
				if( action )
				{
					action.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
					action.SetExecutor( userToAuthorize );
					GetPersistencySystem().QueuePSDeviceEvent( action );
				}
			}
		}
	}

	protected function InstallKeyloggerOnSlaves()
	{
		var devices : array< DeviceComponentPS >;
		var i : Int32;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ] == this )
			{
				continue;
			}
			if( ( ( DoorControllerPS )( devices[ i ] ) ) )
			{
				ExecutePSAction( ActionInstallKeylogger(), devices[ i ] );
			}
		}
	}

	public override function TurnAuthorizationModuleOFF()
	{
		super.TurnAuthorizationModuleOFF();
		DisbleAuthorizationOnSlaves();
		m_terminalSetup.m_ignoreSlaveAuthorizationModule = true;
	}

	public override function OnDisassembleDevice( evt : DisassembleDevice ) : EntityNotificationType
	{
		TurnAuthorizationModuleOFF();
		DisbleAuthorizationOnSlaves();
		return super.OnDisassembleDevice( evt );
	}

	protected override function GetInkWidgetTweakDBID( const context : ref< GetActionsContext > ) : TweakDBID
	{
		if( HasActiveStaticHackingSkillcheck() )
		{
			return GetKeypadWidgetStyle();
		}
		else
		{
			if( !( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) ) )
			{
				if( IsDeviceSecuredWithPassword() )
				{
					return GetKeypadWidgetStyle();
				}
				else
				{
					return GetKeypadWidgetStyle();
				}
			}
			else
			{
				return T"DevicesUIDefinitions.GenericDeviceWidget";
			}
		}
	}

}

