class ToggleCustomActionEvent extends Event
{
	var actionID : CName;
	var enabled : Bool;
}

struct GenericDeviceActionsData
{
	instanceeditable persistent var m_stateActionsOverrides : SGenericDeviceActionsData;
	instanceeditable persistent var m_customActions : SCustomDeviceActionsData;
}

struct SGenericDevicePersistentData
{
	var genericActions : SGenericDeviceActionsData;
	var customActions : SCustomDeviceActionsData;
}

class QuestCustomAction extends ActionName
{

	public function SetProperties( actionID : CName )
	{
		actionName = 'QuestCustomAction';
		prop = DeviceActionPropertyFunctions.SetUpProperty_Name( 'actionID', actionID );
	}

}

class QuestToggleCustomAction extends ActionName
{

	public function SetProperties( actionID : CName, enable : Bool )
	{
		if( enable )
		{
			actionName = 'EnableCustomAction';
		}
		else
		{
			actionName = 'DisableCustomAction';
		}
		prop = DeviceActionPropertyFunctions.SetUpProperty_Name( 'actionID', actionID );
	}

}

class CustomDeviceAction extends ActionBool
{

	public function SetProperties( customActionName : CName, displayName : CName )
	{
		if( !( IsNameValid( customActionName ) ) )
		{
			actionName = 'wrong_name';
		}
		actionName = customActionName;
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( customActionName, true, displayName, displayName );
	}

	public override function SetObjectActionID( id : TweakDBID )
	{
		m_objectActionID = id;
		ProduceInteractionPart();
	}

	public static function IsDefaultConditionMet( device : ScriptableDeviceComponentPS, const context : ref< GetActionsContext >, const actionData : ref< SDeviceActionCustomData > ) : Bool
	{
		if( IsAvailable( device, actionData ) && IsClearanceValid( context.clearance ) )
		{
			return true;
		}
		return false;
	}

	public static function IsAvailable( device : ScriptableDeviceComponentPS, const actionData : ref< SDeviceActionCustomData > ) : Bool
	{
		if( device.IsDisabled() )
		{
			return false;
		}
		else
		{
			if( actionData.RestrictDuringPhonecall == true && StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayer( GetGameInstance() ), 'PhoneCall' ) )
			{
				return false;
			}
			if( actionData.RestrictInCombat == true && GetPlayer( GetGameInstance() ).IsInCombat() == true )
			{
				return false;
			}
			if( actionData.RestrictIfWanted == true && ( GameInstance.GetQuestsSystem( GetGameInstance() ).GetFact( 'wanted_level' ) != 0 ) )
			{
				return false;
			}
			if( device.IsON() && actionData.On )
			{
				return true;
			}
			if( device.IsOFF() && actionData.Off )
			{
				return true;
			}
			if( device.IsUnpowered() && actionData.Unpowered )
			{
				return true;
			}
		}
		return false;
	}

	public static function IsClearanceValid( clearance : Clearance ) : Bool
	{
		if( Clearance.IsInRange( clearance, DefaultActionsParametersHolder.GetQuestClearance() ) )
		{
			return true;
		}
		return false;
	}

	public function IsCustomClearanceValid( clearance : Clearance ) : Bool
	{
		if( Clearance.IsInRange( clearance, clearanceLevel ) )
		{
			return true;
		}
		return false;
	}

	public override function GetActivationTime() : Float
	{
		if( IsQuickHack() )
		{
			return super.GetActivationTime();
		}
		return 0.0;
	}

}

class GenericDeviceController extends ScriptableDeviceComponent
{

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

}

class GenericDeviceControllerPS extends ScriptableDeviceComponentPS
{
	default m_tweakDBRecord = T"Devices.GenericDevice";
	default m_tweakDBDescriptionRecord = T"device_descriptions.GenericDevice";
	[ category = "Senses" ]
	private instanceeditable persistent var m_isRecognizableBySenses : Bool;
	[ category = "Device Operations" ]
	protected instanceeditable persistent var m_genericDeviceActionsSetup : GenericDeviceActionsData;
	protected instanceeditable inlined var m_genericDeviceSkillChecks : GenericContainer;
	[ category = "UI" ][ customEditor = "TweakDBGroupInheritance;DeviceWidgetDefinition" ]
	instanceeditable editable var m_deviceWidgetRecord : TweakDBID;
	[ category = "UI" ][ customEditor = "TweakDBGroupInheritance;ThumbnailWidgetDefinition" ]
	instanceeditable editable var m_thumbnailWidgetRecord : TweakDBID;
	private persistent var m_performedCustomActionsIDs : array< CName >;

	protected event OnInstantiated()
	{
		super.OnInstantiated();
		if( !( IsStringValid( m_deviceName ) ) )
		{
			m_deviceName = "LocKey#42164";
		}
	}

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

	protected function ActionQuestCustomAction() : QuestCustomAction
	{
		var action : QuestCustomAction;
		action = new QuestCustomAction;
		action.SetUp( this );
		action.SetProperties( 'InvalidID' );
		action.AddDeviceName( m_deviceName );
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		return action;
	}

	protected function ActionQuestToggleCustomAction( enable : Bool ) : QuestToggleCustomAction
	{
		var action : QuestToggleCustomAction;
		action = new QuestToggleCustomAction;
		action.SetUp( this );
		action.SetProperties( 'InvalidID', enable );
		action.AddDeviceName( m_deviceName );
		action.clearanceLevel = DefaultActionsParametersHolder.GetQuestClearance();
		return action;
	}

	protected function ActionCustom( const actionData : ref< SDeviceActionCustomData > ) : CustomDeviceAction
	{
		var action : CustomDeviceAction;
		var tweakDBName : String;
		var displayName : String;
		if( TDBID.IsValid( actionData.displayNameRecord ) )
		{
			displayName = LocKeyToString( TweakDBInterface.GetInteractionBaseRecord( actionData.displayNameRecord ).Caption() );
		}
		else
		{
			tweakDBName = actionData.displayName;
		}
		action = new CustomDeviceAction;
		action.clearanceLevel = actionData.customClearance;
		action.SetUp( this );
		action.SetProperties( actionData.actionID, StringToName( displayName ) );
		action.AddDeviceName( m_deviceName );
		if( TDBID.IsValid( actionData.objectActionRecord ) )
		{
			action.SetObjectActionID( actionData.objectActionRecord );
		}
		else if( actionData.isQuickHack )
		{
			action.SetObjectActionID( T"DeviceAction.MalfunctionClassHack" );
		}
		if( ( actionData.hasInteraction || actionData.isQuickHack ) || actionData.isSpiderbotAction )
		{
			action.CreateCustomInteraction( , tweakDBName, tweakDBName, actionData.displayNameRecord, actionData.displayNameRecord );
		}
		if( actionData.hasUI )
		{
			if( TDBID.IsValid( actionData.widgetRecord ) )
			{
				action.CreateActionWidgetPackage( actionData.widgetRecord );
			}
			else
			{
				action.CreateActionWidgetPackage();
			}
		}
		return action;
	}

	protected function ActionTogglePower( const actionData : ref< SDeviceActionBoolData > ) : TogglePower
	{
		var action : TogglePower;
		var tweakDBNameOnTrue : String;
		var tweakDBNameOnFalse : String;
		var actionNameOnTrue : TweakDBID;
		var actionNameOnFalse : TweakDBID;
		action = new TogglePower;
		if( TDBID.IsValid( actionData.nameOnTrueRecord ) )
		{
			actionNameOnTrue = actionData.nameOnTrueRecord;
		}
		else
		{
			tweakDBNameOnTrue = actionData.nameOnTrue;
			actionNameOnTrue = TDBID.Create( "Interactions." + tweakDBNameOnTrue );
		}
		if( TDBID.IsValid( actionData.nameOnFalseRecord ) )
		{
			actionNameOnFalse = actionData.nameOnFalseRecord;
		}
		else
		{
			tweakDBNameOnFalse = actionData.nameOnFalse;
			actionNameOnTrue = TDBID.Create( "Interactions." + tweakDBNameOnFalse );
		}
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState, actionNameOnTrue, actionNameOnFalse );
		action.AddDeviceName( m_deviceName );
		if( TDBID.IsValid( actionData.objectActionRecord ) )
		{
			action.SetObjectActionID( actionData.objectActionRecord );
		}
		else if( actionData.isQuickHack )
		{
			action.SetObjectActionID( T"DeviceAction.ToggleStateClassHack" );
		}
		if( actionData.hasInteraction || actionData.isQuickHack )
		{
			action.CreateCustomInteraction( , tweakDBNameOnTrue, tweakDBNameOnFalse, actionData.nameOnTrueRecord, actionData.nameOnFalseRecord );
		}
		if( actionData.hasUI )
		{
			if( TDBID.IsValid( actionData.widgetRecord ) )
			{
				action.CreateActionWidgetPackage( actionData.widgetRecord );
			}
			else
			{
				action.CreateActionWidgetPackage();
			}
		}
		return action;
	}

	public function ActionToggleON( const actionData : ref< SDeviceActionBoolData > ) : ToggleON
	{
		var action : ToggleON;
		var tweakDBNameOnTrue : String;
		var tweakDBNameOnFalse : String;
		var actionNameOnTrue : TweakDBID;
		var actionNameOnFalse : TweakDBID;
		action = new ToggleON;
		if( TDBID.IsValid( actionData.nameOnTrueRecord ) )
		{
			actionNameOnTrue = actionData.nameOnTrueRecord;
		}
		else
		{
			tweakDBNameOnTrue = actionData.nameOnTrue;
			actionNameOnTrue = TDBID.Create( "Interactions." + tweakDBNameOnTrue );
		}
		if( TDBID.IsValid( actionData.nameOnFalseRecord ) )
		{
			actionNameOnFalse = actionData.nameOnFalseRecord;
		}
		else
		{
			tweakDBNameOnFalse = actionData.nameOnFalse;
			actionNameOnTrue = TDBID.Create( "Interactions." + tweakDBNameOnFalse );
		}
		action.clearanceLevel = DefaultActionsParametersHolder.GetTogglePowerClearance();
		action.SetUp( this );
		action.SetProperties( m_deviceState, actionNameOnTrue, actionNameOnFalse );
		action.AddDeviceName( m_deviceName );
		if( TDBID.IsValid( actionData.objectActionRecord ) )
		{
			action.SetObjectActionID( actionData.objectActionRecord );
		}
		else if( actionData.isQuickHack )
		{
			action.SetObjectActionID( T"DeviceAction.ToggleStateClassHack" );
		}
		if( actionData.hasInteraction || actionData.isQuickHack )
		{
			action.CreateCustomInteraction( , tweakDBNameOnTrue, tweakDBNameOnFalse, actionData.nameOnTrueRecord, actionData.nameOnFalseRecord );
		}
		if( actionData.hasUI )
		{
			if( TDBID.IsValid( actionData.widgetRecord ) )
			{
				action.CreateActionWidgetPackage( actionData.widgetRecord );
			}
			else
			{
				action.CreateActionWidgetPackage();
			}
		}
		return action;
	}

	public override function GetActions( out actions : array< DeviceAction >, context : GetActionsContext ) : Bool
	{
		var i : Int32;
		var customAction : CustomDeviceAction;
		var action : DeviceAction;
		if( !( super.GetActions( actions, context ) ) )
		{
			return false;
		}
		if( context.processInitiatorObject && !( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) ) )
		{
			return false;
		}
		if( ToggleON.IsDefaultConditionMet( this, context ) )
		{
			if( context.requestType == gamedeviceRequestType.Direct && m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.hasInteraction )
			{
				action = ActionToggleON( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.interactionRecord = GetRecordName( action );
			}
			else if( context.requestType != gamedeviceRequestType.Direct && context.requestType != gamedeviceRequestType.Remote )
			{
				action = ActionToggleON( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.interactionRecord = GetRecordName( action );
			}
			if( action != NULL )
			{
				actions.PushBack( action );
			}
		}
		if( TogglePower.IsDefaultConditionMet( this, context ) )
		{
			action = NULL;
			if( context.requestType == gamedeviceRequestType.Direct && m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.hasInteraction )
			{
				action = ActionTogglePower( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.interactionRecord = GetRecordName( action );
			}
			else if( context.requestType != gamedeviceRequestType.Direct && context.requestType != gamedeviceRequestType.Remote )
			{
				action = ActionTogglePower( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.interactionRecord = GetRecordName( action );
			}
			if( action != NULL )
			{
				actions.PushBack( action );
			}
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled && context.requestType != gamedeviceRequestType.Remote )
			{
				if( context.requestType == gamedeviceRequestType.Direct && !( m_genericDeviceActionsSetup.m_customActions.actions[ i ].hasInteraction ) )
				{
					continue;
				}
				if( context.requestType == gamedeviceRequestType.External && !( m_genericDeviceActionsSetup.m_customActions.actions[ i ].hasUI ) )
				{
					continue;
				}
				customAction = ActionCustom( m_genericDeviceActionsSetup.m_customActions.actions[ i ] );
				if( CustomDeviceAction.IsAvailable( this, m_genericDeviceActionsSetup.m_customActions.actions[ i ] ) )
				{
					m_genericDeviceActionsSetup.m_customActions.actions[ i ].currentDisplayName = StringToName( customAction.GetCurrentDisplayString() );
					m_genericDeviceActionsSetup.m_customActions.actions[ i ].interactionRecord = GetRecordName( customAction );
					actions.PushBack( customAction );
				}
			}
		}
		SetActionIllegality( actions, m_illegalActions.regularActions );
		return true;
	}

	private function GetRecordName( inputAction : DeviceAction ) : String
	{
		var record : String;
		var action : ScriptableDeviceAction;
		var interaction : InteractionChoice;
		action = ( ( ScriptableDeviceAction )( inputAction ) );
		interaction = action.GetInteractionChoice();
		record = interaction.choiceMetaData.tweakDBName;
		return record;
	}

	public override function GetQuestActionByName( actionName : CName ) : DeviceAction
	{
		var action : DeviceAction;
		action = super.GetQuestActionByName( actionName );
		if( action == NULL )
		{
			switch( actionName )
			{
				case 'QuestCustomAction':
					action = ActionQuestCustomAction();
				break;
				case 'EnableCustomAction':
					action = ActionQuestToggleCustomAction( true );
				break;
				case 'DisableCustomAction':
					action = ActionQuestToggleCustomAction( false );
				break;
			}
		}
		return action;
	}

	public override function GetQuestActions( out actions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		super.GetQuestActions( actions, context );
		actions.PushBack( ActionQuestCustomAction() );
		actions.PushBack( ActionQuestToggleCustomAction( true ) );
		actions.PushBack( ActionQuestToggleCustomAction( false ) );
	}

	protected const override function CanCreateAnyQuickHackActions() : Bool
	{
		var i : Int32;
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.isQuickHack || m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.isQuickHack )
		{
			return true;
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled && m_genericDeviceActionsSetup.m_customActions.actions[ i ].isQuickHack )
			{
				return true;
			}
		}
		return false;
	}

	protected override function GetQuickHackActions( out actions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		var i : Int32;
		var actionON : ScriptableDeviceAction;
		var actionPower : ScriptableDeviceAction;
		var customAction : CustomDeviceAction;
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.isQuickHack && ToggleON.IsDefaultConditionMet( this, context ) )
		{
			actionON = ActionToggleON( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON );
		}
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.isQuickHack && TogglePower.IsAvailable( this ) )
		{
			actionPower = ActionTogglePower( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower );
		}
		if( actionON != NULL )
		{
			actions.PushBack( actionON );
		}
		if( actionPower != NULL )
		{
			actions.PushBack( actionPower );
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled && m_genericDeviceActionsSetup.m_customActions.actions[ i ].isQuickHack )
			{
				customAction = ActionCustom( m_genericDeviceActionsSetup.m_customActions.actions[ i ] );
				if( CustomDeviceAction.IsAvailable( this, m_genericDeviceActionsSetup.m_customActions.actions[ i ] ) )
				{
					actions.PushBack( customAction );
				}
			}
		}
		FinalizeGetQuickHackActions( actions, context );
	}

	protected override function CanCreateAnySpiderbotActions() : Bool
	{
		var i : Int32;
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.isSpiderbotAction || m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.isSpiderbotAction )
		{
			return true;
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled && m_genericDeviceActionsSetup.m_customActions.actions[ i ].isSpiderbotAction )
			{
				return true;
			}
		}
		return false;
	}

	protected override function GetSpiderbotActions( actions : ref< array< DeviceAction > >, const context : ref< GetActionsContext > )
	{
		var i : Int32;
		var actionON : DeviceAction;
		var actionPower : DeviceAction;
		var customAction : CustomDeviceAction;
		var globalNodeRef : GlobalNodeRef;
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.isSpiderbotAction && ToggleON.IsDefaultConditionMet( this, context ) )
		{
			actionON = ActionToggleON( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON );
		}
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.isSpiderbotAction && TogglePower.IsAvailable( this ) )
		{
			actionPower = ActionTogglePower( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower );
		}
		if( actionON != NULL )
		{
			actions.PushBack( actionON );
		}
		if( actionPower != NULL )
		{
			actions.PushBack( actionPower );
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled && m_genericDeviceActionsSetup.m_customActions.actions[ i ].isSpiderbotAction )
			{
				customAction = ActionCustom( m_genericDeviceActionsSetup.m_customActions.actions[ i ] );
				if( CustomDeviceAction.IsAvailable( this, m_genericDeviceActionsSetup.m_customActions.actions[ i ] ) )
				{
					globalNodeRef = ResolveNodeRefWithEntityID( m_genericDeviceActionsSetup.m_customActions.actions[ i ].spiderbotLocationOverrideReference, PersistentID.ExtractEntityID( GetID() ) );
					if( GlobalNodeRef.IsDefined( globalNodeRef ) )
					{
						customAction.SetSpiderbotLocationOverrideReference( m_genericDeviceActionsSetup.m_customActions.actions[ i ].spiderbotLocationOverrideReference );
					}
					actions.PushBack( customAction );
				}
			}
		}
		MarkActionsAsSpiderbotActions( actions );
	}

	public function StorePerformedCustomActionID( ID : CName )
	{
		if( m_performedCustomActionsIDs.Contains( ID ) )
		{
			m_performedCustomActionsIDs.Remove( ID );
		}
		m_performedCustomActionsIDs.PushBack( ID );
	}

	public function ResetPerformedCustomActionsStorage()
	{
		m_performedCustomActionsIDs.Clear();
	}

	private const function HasCustomActionStored( ID : CName ) : Bool
	{
		return m_performedCustomActionsIDs.Contains( ID );
	}

	public function ResolveCustomAction( actionID : CName )
	{
		var i : Int32;
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].actionID == actionID )
			{
				if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].disableOnUse )
				{
					m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled = false;
				}
			}
		}
	}

	public function ResolveFactOnCustomAction( factName : CName ) : Bool
	{
		var i : Int32;
		var isEnabled : Bool;
		var wasChanged : Bool;
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( factName == m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName )
			{
				isEnabled = GameInstance.GetQuestsSystem( GetGameInstance() ).GetFact( m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName ) > 0;
				if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled != isEnabled )
				{
					wasChanged = true;
				}
				m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled = isEnabled;
			}
		}
		if( wasChanged )
		{
			DetermineInitialPlaystyle();
			NotifyParents_Event();
		}
		return wasChanged;
	}

	private function ResolveFactOnCustomActionByIndex( index : Int32 ) : Bool
	{
		var isEnabled : Bool;
		if( ( index >= 0 ) && ( index < m_genericDeviceActionsSetup.m_customActions.actions.Size() ) )
		{
			isEnabled = GameInstance.GetQuestsSystem( GetGameInstance() ).GetFact( m_genericDeviceActionsSetup.m_customActions.actions[ index ].factToEnableName ) > 0;
			if( m_genericDeviceActionsSetup.m_customActions.actions[ index ].isEnabled != isEnabled )
			{
				m_genericDeviceActionsSetup.m_customActions.actions[ index ].isEnabled = isEnabled;
				DetermineInitialPlaystyle();
				NotifyParents_Event();
				return true;
			}
		}
		return false;
	}

	public function InitializeQuestDBCallbacksForCustomActions()
	{
		var i : Int32;
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( IsNameValid( m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName ) )
			{
				m_genericDeviceActionsSetup.m_customActions.actions[ i ].callbackID = GameInstance.GetQuestsSystem( GetGameInstance() ).RegisterEntity( m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName, PersistentID.ExtractEntityID( GetID() ) );
				ResolveFactOnCustomActionByIndex( i );
			}
		}
	}

	public function UnInitializeQuestDBCallbacksForCustomActions()
	{
		var i : Int32;
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( IsNameValid( m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName ) )
			{
				GameInstance.GetQuestsSystem( GetGameInstance() ).UnregisterEntity( m_genericDeviceActionsSetup.m_customActions.actions[ i ].factToEnableName, m_genericDeviceActionsSetup.m_customActions.actions[ i ].callbackID );
			}
		}
	}

	public const function GetPerformedCustomActionsStorage() : array< CName >
	{
		return m_performedCustomActionsIDs;
	}

	protected override function ResolveBaseActionOperation( action : ScriptableDeviceAction )
	{
		var customAction : CustomDeviceAction;
		customAction = ( ( CustomDeviceAction )( action ) );
		if( customAction == NULL )
		{
			super.ResolveBaseActionOperation( action );
		}
		else
		{
			ResolveCustomActionOperation( customAction );
		}
	}

	private function ResolveCustomActionOperation( action : CustomDeviceAction )
	{
		var evt : PerformedAction;
		evt = new PerformedAction;
		evt.m_action = action;
		StorePerformedCustomActionID( action.GetActionName() );
		GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( GetID() ), evt );
	}

	public virtual function OnCustomAction( evt : CustomDeviceAction ) : EntityNotificationType
	{
		var notifier : ActionNotifier;
		notifier = new ActionNotifier;
		notifier.SetNone();
		if( IsUnpowered() || IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Unpowered or Disabled" );
		}
		if( !( IsFinal() ) )
		{
		}
		Notify( notifier, evt );
		ResolveCustomAction( evt.GetActionName() );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function OnQuestCustomAction( evt : QuestCustomAction ) : EntityNotificationType
	{
		if( IsDisabled() )
		{
			return SendActionFailedEvent( evt, evt.GetRequesterID(), "Disabled" );
		}
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public function OnQuestToggleCustomAction( evt : QuestToggleCustomAction ) : EntityNotificationType
	{
		var actionID : CName;
		var enabled : Bool;
		actionID = ( ( CName )evt.prop.first );
		if( !( IsNameValid( actionID ) ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		enabled = evt.GetActionName() == 'EnableCustomAction';
		if( ToggleCustomAction( actionID, enabled ) )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		else
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
	}

	public function OnToggleCustomActionEvent( evt : ToggleCustomActionEvent ) : EntityNotificationType
	{
		if( ToggleCustomAction( evt.actionID, evt.enabled ) )
		{
			return EntityNotificationType.SendThisEventToEntity;
		}
		else
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
	}

	private function ToggleCustomAction( actionID : CName, enable : Bool ) : Bool
	{
		var i : Int32;
		var wasChanged : Bool;
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].actionID == actionID )
			{
				wasChanged = m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled != enable;
				m_genericDeviceActionsSetup.m_customActions.actions[ i ].isEnabled = enable;
				if( wasChanged )
				{
					DetermineInitialPlaystyle();
					wasChanged = false;
					NotifyParents_Event();
					return true;
				}
			}
		}
		return false;
	}

	protected override function ActionHacking( const context : ref< GetActionsContext > ) : ActionHacking
	{
		var action : ActionHacking;
		var displayName : String;
		action = super.ActionHacking( context );
		displayName = GetSkillCheckActionDisplayName( action );
		if( IsStringValid( displayName ) )
		{
			action.ResetCaption();
			action.CreateInteraction( context.processInitiatorObject, , displayName );
		}
		else
		{
			action.CreateInteraction( context.processInitiatorObject );
		}
		return action;
	}

	protected override function ResolveActionHackingCompleted( evt : ActionHacking )
	{
		super.ResolveActionHackingCompleted( evt );
		if( evt.GetAttributeCheckType() != EDeviceChallengeSkill.Invalid )
		{
			ResolveSkillCheckAction( evt );
		}
	}

	protected override function ActionEngineering( const context : ref< GetActionsContext > ) : ActionEngineering
	{
		var action : ActionEngineering;
		var displayName : String;
		action = super.ActionEngineering( context );
		displayName = GetSkillCheckActionDisplayName( action );
		if( IsStringValid( displayName ) )
		{
			action.ResetCaption();
			action.CreateInteraction( context.processInitiatorObject, , displayName );
		}
		else
		{
			action.CreateInteraction( context.processInitiatorObject );
		}
		return action;
	}

	public override function OnActionEngineering( evt : ActionEngineering ) : EntityNotificationType
	{
		if( !( evt.WasPassed() ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		super.OnActionEngineering( evt );
		if( evt.IsCompleted() )
		{
			ResolveSkillCheckAction( evt );
			return EntityNotificationType.SendPSChangedEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected override function ActionDemolition( const context : ref< GetActionsContext > ) : ActionDemolition
	{
		var action : ActionDemolition;
		var displayName : String;
		action = super.ActionDemolition( context );
		displayName = GetSkillCheckActionDisplayName( action );
		if( IsStringValid( displayName ) )
		{
			action.ResetCaption();
			action.CreateInteraction( context.processInitiatorObject, , displayName );
		}
		else
		{
			action.CreateInteraction( context.processInitiatorObject );
		}
		return action;
	}

	public override function OnActionDemolition( evt : ActionDemolition ) : EntityNotificationType
	{
		if( !( evt.WasPassed() ) )
		{
			return EntityNotificationType.DoNotNotifyEntity;
		}
		super.OnActionDemolition( evt );
		if( evt.IsCompleted() )
		{
			ResolveSkillCheckAction( evt );
			return EntityNotificationType.SendPSChangedEventToEntity;
		}
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected override function OnActivateDevice( evt : ActivateDevice ) : EntityNotificationType
	{
		super.OnActivateDevice( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	private function GetSkillCheckActionDisplayName( skillAction : ActionSkillCheck ) : String
	{
		var i : Int32;
		var displayName : String;
		var record : weak< InteractionBase_Record >;
		var id : TweakDBID;
		switch( skillAction.GetAttributeCheckType() )
		{
			case EDeviceChallengeSkill.Hacking:
				id = m_genericDeviceSkillChecks.m_hackingCheck.GetAlternativeName();
			break;
			case EDeviceChallengeSkill.Engineering:
				id = m_genericDeviceSkillChecks.m_engineeringCheck.GetAlternativeName();
			break;
			case EDeviceChallengeSkill.Athletics:
				id = m_genericDeviceSkillChecks.m_demolitionCheck.GetAlternativeName();
			break;
			default:
				break;
		}
		if( TDBID.IsValid( id ) )
		{
			record = TweakDBInterface.GetInteractionBaseRecord( id );
			if( record )
			{
				displayName = record.Name();
				if( IsStringValid( displayName ) )
				{
					return displayName;
				}
			}
		}
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.attachedToSkillCheck == skillAction.GetAttributeCheckType() )
		{
			displayName = m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.interactionRecord;
			return displayName;
		}
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.attachedToSkillCheck == skillAction.GetAttributeCheckType() )
		{
			displayName = m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.interactionRecord;
			return displayName;
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_customActions.actions[ i ].attachedToSkillCheck == skillAction.GetAttributeCheckType() )
			{
				displayName = m_genericDeviceActionsSetup.m_customActions.actions[ i ].interactionRecord;
				return displayName;
			}
		}
		return displayName;
	}

	private function ResolveSkillCheckAction( skillAction : ActionSkillCheck )
	{
		var i : Int32;
		var action : ScriptableDeviceAction;
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.attachedToSkillCheck == skillAction.GetAttributeCheckType() )
		{
			action = ActionToggleON( m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON );
			if( action )
			{
				m_genericDeviceActionsSetup.m_stateActionsOverrides.toggleON.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				action.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
				action.SetExecutor( skillAction.GetExecutor() );
				GetPersistencySystem().QueuePSDeviceEvent( action );
			}
		}
		if( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.attachedToSkillCheck == skillAction.GetAttributeCheckType() )
		{
			action = ActionTogglePower( m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower );
			if( action )
			{
				m_genericDeviceActionsSetup.m_stateActionsOverrides.togglePower.currentDisplayName = StringToName( action.GetCurrentDisplayString() );
				action.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
				action.SetExecutor( skillAction.GetExecutor() );
				GetPersistencySystem().QueuePSDeviceEvent( action );
			}
		}
		for( i = 0; i < m_genericDeviceActionsSetup.m_customActions.actions.Size(); i += 1 )
		{
			if( m_genericDeviceActionsSetup.m_customActions.actions[ i ].attachedToSkillCheck != EDeviceChallengeSkill.Invalid && m_genericDeviceActionsSetup.m_customActions.actions[ i ].attachedToSkillCheck == skillAction.GetAttributeCheckType() )
			{
				action = ( ( ScriptableDeviceAction )( ActionCustom( m_genericDeviceActionsSetup.m_customActions.actions[ i ] ) ) );
				if( action )
				{
					m_genericDeviceActionsSetup.m_customActions.actions[ i ].currentDisplayName = StringToName( action.GetCurrentDisplayString() );
					action.RegisterAsRequester( PersistentID.ExtractEntityID( GetID() ) );
					action.SetExecutor( skillAction.GetExecutor() );
					GetPersistencySystem().QueuePSDeviceEvent( action );
				}
			}
		}
	}

	protected override function GetInkWidgetTweakDBID( const context : ref< GetActionsContext > ) : TweakDBID
	{
		if( !( IsUserAuthorized( context.processInitiatorObject.GetEntityID() ) ) && !( context.ignoresAuthorization ) )
		{
			return super.GetInkWidgetTweakDBID( context );
		}
		else if( TDBID.IsValid( m_deviceWidgetRecord ) )
		{
			return m_deviceWidgetRecord;
		}
		else
		{
			return super.GetInkWidgetTweakDBID( context );
		}
	}

	public override function GetThumbnailWidget() : SThumbnailWidgetPackage
	{
		var widgetData : SThumbnailWidgetPackage;
		widgetData = super.GetThumbnailWidget();
		if( TDBID.IsValid( m_thumbnailWidgetRecord ) )
		{
			widgetData.widgetTweakDBID = m_thumbnailWidgetRecord;
		}
		return widgetData;
	}

	public quest const function WasCustomActionPerformed( actionID : CName ) : Bool
	{
		return HasCustomActionStored( actionID );
	}

}

