class UpdateDebuggerRequest extends ScriptableSystemRequest
{
	var m_system : SecuritySystemControllerPS;
	var m_time : Float;
	var m_instructionAttached : Bool;
	var m_inputAttached : Bool;
	var m_callstack : CName;
	var m_instruction : EReprimandInstructions;
	var m_recentInput : SecuritySystemInput;
}

class RealTimeUpdateRequest extends ScriptableSystemRequest
{
	var m_evt : TickableEvent;
	var m_time : Float;
}

class SysDebuggerEvent extends TickableEvent
{
}

class SecSystemDebugger extends ScriptableSystem
{
	[ unsavable = "true" ]
	private persistent var lastInstruction : EReprimandInstructions;
	[ unsavable = "true" ]
	private persistent var instructionSet : Bool;
	[ unsavable = "true" ]
	private persistent var lastInstructionTime : Float;
	[ unsavable = "true" ]
	private persistent var lastInput : ESecurityNotificationType;
	[ unsavable = "true" ]
	private persistent var inputSet : Bool;
	[ unsavable = "true" ]
	private persistent var lastInputTime : Float;
	[ unsavable = "true" ]
	private persistent var lastUpdateTime : Float;
	[ unsavable = "true" ]
	private persistent var realTimeCallbackID : DelayID;
	[ unsavable = "true" ]
	private persistent var realTimeCallback : Bool;
	[ unsavable = "true" ]
	private persistent var realTime : Float;
	[ unsavable = "true" ]
	private persistent var callstack : array< CName >;
	[ unsavable = "true" ]
	private persistent var ids : array< Uint32 >;
	[ unsavable = "true" ]
	private persistent var background : Uint32;
	[ unsavable = "true" ]
	private persistent var sysName : Uint32;
	[ unsavable = "true" ]
	private persistent var sysState : Uint32;
	[ unsavable = "true" ]
	private persistent var mostDangerousArea : Uint32;
	[ unsavable = "true" ]
	private persistent var blacklistReason : Uint32;
	[ unsavable = "true" ]
	private persistent var blacklistCount : Uint32;
	[ unsavable = "true" ]
	private persistent var reprimand : Uint32;
	[ unsavable = "true" ]
	private persistent var repInstruction : Uint32;
	[ unsavable = "true" ]
	private persistent var reprimandID : Uint32;
	[ unsavable = "true" ]
	private persistent var input : Uint32;
	[ unsavable = "true" ]
	private persistent var regTime : Uint32;
	[ unsavable = "true" ]
	private persistent var inputTime : Uint32;
	[ unsavable = "true" ]
	private persistent var instructionTime : Uint32;
	[ unsavable = "true" ]
	private persistent var actualTime : Uint32;
	private var system : SecuritySystemControllerPS;
	private var refreshTime : Float;
	default refreshTime = 60.f;

	private function OnRealTimeUpdate( req : RealTimeUpdateRequest )
	{
		RealTimeUpdate( req.m_evt, req.m_time );
	}

	private function OnUpdateDebuggerRequest( req : UpdateDebuggerRequest )
	{
		Update( req.m_system, req.m_time, req.m_instructionAttached, req.m_inputAttached, req.m_callstack, req.m_instruction, req.m_recentInput );
	}

	private function RealTimeUpdate( evt : TickableEvent, time : Float )
	{
		var color : Color;
		var threshold : Float;
		var dvs : DebugVisualizerSystem;
		var tickableEvent : SysDebuggerEvent;
		if( !( system ) )
		{
			return;
		}
		dvs = GameInstance.GetDebugVisualizerSystem( system.GetGameInstance() );
		dvs.ClearLayer( regTime );
		dvs.ClearLayer( inputTime );
		dvs.ClearLayer( instructionTime );
		dvs.ClearLayer( actualTime );
		realTime = time;
		if( ( realTime - lastUpdateTime ) > ( refreshTime - 10.0 ) )
		{
			actualTime = dvs.DrawText( Vector4( 500.0, 630.0, 0.0, 0.0 ), "REAL TIME: " + FloatToString( realTime ), gameDebugViewETextAlignment.Left, SColor.Red() );
		}
		else
		{
			actualTime = dvs.DrawText( Vector4( 500.0, 630.0, 0.0, 0.0 ), "REAL TIME: " + FloatToString( realTime ), gameDebugViewETextAlignment.Left, SColor.White() );
		}
		threshold = realTime - lastUpdateTime;
		if( threshold < 1.0 )
		{
			color = SColor.Green();
		}
		else if( threshold < 3.0 )
		{
			color = SColor.Yellow();
		}
		else
		{
			color = SColor.Red();
		}
		regTime = dvs.DrawText( Vector4( 500.0, 650.0, 0.0, 0.0 ), ( "LAST UPDATE: " + FloatToString( threshold ) ) + " sec ago", gameDebugViewETextAlignment.Left, color );
		threshold = realTime - lastInputTime;
		if( threshold < 1.0 )
		{
			color = SColor.Green();
		}
		else if( threshold < 3.0 )
		{
			color = SColor.Yellow();
		}
		else
		{
			color = SColor.Red();
		}
		inputTime = dvs.DrawText( Vector4( 500.0, 670.0, 0.0, 0.0 ), ( "LAST INPUT: " + FloatToString( threshold ) ) + " sec ago", gameDebugViewETextAlignment.Left, color );
		threshold = realTime - lastInstructionTime;
		if( threshold < 1.0 )
		{
			color = SColor.Green();
		}
		else if( threshold < 3.0 )
		{
			color = SColor.Yellow();
		}
		else
		{
			color = SColor.Red();
		}
		instructionTime = dvs.DrawText( Vector4( 500.0, 690.0, 0.0, 0.0 ), ( "LAST INSTRUCTION: " + FloatToString( threshold ) ) + " sec ago", gameDebugViewETextAlignment.Left, color );
		ids.PushBack( dvs.DrawText( Vector4( 500.0, 720.0, 0.0, 0.0 ), "INPUTS: " + system.DebugGetInputsCount(), gameDebugViewETextAlignment.Left, SColor.White() ) );
		ids.PushBack( dvs.DrawText( Vector4( 600.0, 720.0, 0.0, 0.0 ), "OUTPUTS: " + system.DebugGetOutputsCount(), gameDebugViewETextAlignment.Left, SColor.White() ) );
		if( evt.GetState() == gameTickableEventState.LastTick )
		{
			if( ( realTime - lastUpdateTime ) < refreshTime )
			{
				GameInstance.GetDelaySystem( system.GetGameInstance() ).CancelTick( realTimeCallbackID );
				tickableEvent = new SysDebuggerEvent;
				realTimeCallbackID = GameInstance.GetDelaySystem( system.GetGameInstance() ).TickOnEvent( GameInstance.GetPlayerSystem( system.GetGameInstance() ).GetLocalPlayerControlledGameObject(), tickableEvent, refreshTime );
			}
			else
			{
				Clean( dvs );
			}
		}
	}

	private function Clean( dvs : DebugVisualizerSystem )
	{
		var i : Int32;
		instructionSet = false;
		inputSet = false;
		GameInstance.GetDelaySystem( system.GetGameInstance() ).CancelTick( realTimeCallbackID );
		realTimeCallback = false;
		dvs.ClearLayer( regTime );
		dvs.ClearLayer( inputTime );
		dvs.ClearLayer( instructionTime );
		dvs.ClearLayer( actualTime );
		for( i = 0; i < ids.Size(); i += 1 )
		{
			dvs.ClearLayer( ids[ i ] );
		}
		ids.Clear();
		callstack.Clear();
		system = NULL;
	}

	private function Update( const sys : SecuritySystemControllerPS, time : Float, instructionsAttached : Bool, inputAttached : Bool, trace : CName, optional instruction : EReprimandInstructions, optional recentInput : SecuritySystemInput )
	{
		var i, j : Int32;
		var dvs : DebugVisualizerSystem;
		var color : Color;
		var area : SecurityAreaControllerPS;
		var blackReason : BlacklistReason;
		var warningCount : Int32;
		var repOngoing : Bool;
		var extraTag : String;
		var tickableEvent : SysDebuggerEvent;
		var vPos : Float;
		dvs = GameInstance.GetDebugVisualizerSystem( sys.GetGameInstance() );
		if( !( system ) )
		{
			system = sys;
		}
		if( sys != system )
		{
			Clean( dvs );
			system = sys;
		}
		if( callstack.Size() > 10 )
		{
			callstack.Erase( 0 );
		}
		callstack.PushBack( trace );
		if( !( realTimeCallback ) )
		{
			tickableEvent = new SysDebuggerEvent;
			realTimeCallbackID = GameInstance.GetDelaySystem( sys.GetGameInstance() ).TickOnEvent( GameInstance.GetPlayerSystem( sys.GetGameInstance() ).GetLocalPlayerControlledGameObject(), tickableEvent, refreshTime );
			realTimeCallback = true;
		}
		lastUpdateTime = time;
		if( instructionsAttached )
		{
			lastInstruction = instruction;
			instructionSet = true;
			lastInstructionTime = time;
		}
		if( inputAttached )
		{
			lastInput = recentInput.GetNotificationType();
			inputSet = true;
			lastInputTime = time;
		}
		for( i = 0; i < ids.Size(); i += 1 )
		{
			dvs.ClearLayer( ids[ i ] );
		}
		ids.Clear();
		ids.PushBack( background = dvs.DrawRect( Vector4( 490.0, 480.0, 0.0, 0.0 ), Vector4( 500.0, 620.0, 0.0, 0.0 ), SColor().Black() ) );
		ids.PushBack( background = dvs.DrawRect( Vector4( 490.0, 480.0, 0.0, 0.0 ), Vector4( 500.0, 620.0, 0.0, 0.0 ), SColor().Black() ) );
		ids.PushBack( sysName = dvs.DrawText( Vector4( 500.0, 500.0, 0.0, 0.0 ), NameToString( sys.GetDebugPath() ), gameDebugViewETextAlignment.Left, SColor.White() ) );
		switch( sys.GetSecurityState() )
		{
			case ESecuritySystemState.SAFE:
				color = SColor.Green();
			break;
			case ESecuritySystemState.ALERTED:
				color = SColor.Yellow();
			break;
			case ESecuritySystemState.COMBAT:
				color = SColor.Red();
			break;
			default:
				color = SColor.White();
			break;
		}
		ids.PushBack( sysState = dvs.DrawText( Vector4( 500.0, 520.0, 0.0, 0.0 ), "STATE: " + EnumValueToString( "ESecuritySystemState", ( ( Int32 )( sys.GetSecurityState() ) ) ), gameDebugViewETextAlignment.Left, color ) );
		area = sys.GetMostDangerousSecurityAreaForEntityID( sys.GetPlayerEntityID() );
		if( area )
		{
			switch( area.GetSecurityAreaType() )
			{
				case ESecurityAreaType.SAFE:
					color = SColor.Green();
				break;
				case ESecurityAreaType.RESTRICTED:
					color = SColor.Yellow();
				break;
				case ESecurityAreaType.DANGEROUS:
					color = SColor.Red();
				break;
				default:
					color = SColor.White();
				break;
			}
			ids.PushBack( mostDangerousArea = dvs.DrawText( Vector4( 500.0, 540.0, 0.0, 0.0 ), "AREA: " + EnumValueToString( "ESecurityAreaType", ( ( Int32 )( area.GetSecurityAreaType() ) ) ), gameDebugViewETextAlignment.Left, color ) );
		}
		else
		{
			ids.PushBack( mostDangerousArea = dvs.DrawText( Vector4( 500.0, 540.0, 0.0, 0.0 ), "NOT IN ANY AREA", gameDebugViewETextAlignment.Left, SColor.Green() ) );
		}
		blackReason = sys.Debug_GetPlayerBlacklistReason();
		switch( blackReason )
		{
			case BlacklistReason.TRESPASSING:
				color = SColor.Yellow();
			break;
			case BlacklistReason.REPRIMAND:
				color = SColor.Orange();
			break;
			case BlacklistReason.COMBAT:
				color = SColor.Red();
			break;
			default:
				color = SColor.Green();
			break;
		}
		if( blackReason != BlacklistReason.UNINITIALIZED )
		{
			ids.PushBack( blacklistReason = dvs.DrawText( Vector4( 500.0, 560.0, 0.0, 0.0 ), "BLACKLISTED FOR: " + EnumValueToString( "BlacklistReason", ( ( Int32 )( blackReason ) ) ), gameDebugViewETextAlignment.Left, color ) );
		}
		else
		{
			ids.PushBack( blacklistReason = dvs.DrawText( Vector4( 500.0, 560.0, 0.0, 0.0 ), "NOT BLACKLISTED", gameDebugViewETextAlignment.Left, color ) );
		}
		warningCount = sys.Debug_GetPlayerWarningCount();
		switch( warningCount )
		{
			case 0:
				color = SColor.Green();
			break;
			case 1:
				color = SColor.Yellow();
			break;
			case 2:
				color = SColor.Orange();
			break;
			default:
				color = SColor.Red();
			break;
		}
		ids.PushBack( blacklistCount = dvs.DrawText( Vector4( 750.0, 560.0, 0.0, 0.0 ), "WARNINGS: " + IntToString( warningCount ), gameDebugViewETextAlignment.Left, color ) );
		repOngoing = sys.IsReprimandOngoing();
		if( repOngoing )
		{
			ids.PushBack( reprimand = dvs.DrawText( Vector4( 500.0, 580.0, 0.0, 0.0 ), "REPRIMAND: " + BoolToString( repOngoing ), gameDebugViewETextAlignment.Left, SColor.Orange() ) );
		}
		else
		{
			ids.PushBack( reprimand = dvs.DrawText( Vector4( 500.0, 580.0, 0.0, 0.0 ), "REPRIMAND: " + BoolToString( repOngoing ), gameDebugViewETextAlignment.Left, SColor.Green() ) );
		}
		if( instructionSet )
		{
			if( instructionsAttached && ( ( time - lastInstructionTime ) < 5.0 ) )
			{
				extraTag = "[ ! ] ";
			}
			ids.PushBack( repInstruction = dvs.DrawText( Vector4( 650.0, 580.0, 0.0, 0.0 ), ( extraTag + "INSTRUCTION: " ) + EnumValueToString( "EReprimandInstructions", ( ( Int32 )( lastInstruction ) ) ), gameDebugViewETextAlignment.Left, SColor.Yellow() ) );
		}
		ids.PushBack( reprimandID = dvs.DrawText( Vector4( 925.0, 580.0, 0.0, 0.0 ), "ID: " + IntToString( sys.Debug_GetReprimandID() ), gameDebugViewETextAlignment.Left, SColor.Yellow() ) );
		if( inputSet )
		{
			if( inputAttached && ( ( time - lastInputTime ) < 5.0 ) )
			{
				extraTag = "[ ! ] ";
			}
			ids.PushBack( input = dvs.DrawText( Vector4( 500.0, 600.0, 0.0, 0.0 ), ( extraTag + "LAST INPUT: " ) + EnumValueToString( "ESecurityNotificationType", ( ( Int32 )( lastInput ) ) ), gameDebugViewETextAlignment.Left, SColor.Yellow() ) );
		}
		ids.PushBack( input = dvs.DrawText( Vector4( 500.0, 740.0, 0.0, 0.0 ), "CALLSTACK:", gameDebugViewETextAlignment.Left, SColor.Yellow() ) );
		vPos = 740.0;
		j = 0;
		for( i = callstack.Size() - 1; i >= 0; i -= 1 )
		{
			vPos += 20.0;
			extraTag = ( "[ " + j ) + " ] ";
			if( i == ( callstack.Size() - 1 ) )
			{
				ids.PushBack( dvs.DrawText( Vector4( 500.0, vPos, 0.0, 0.0 ), extraTag + NameToString( callstack[ i ] ), gameDebugViewETextAlignment.Left, SColor.Green() ) );
			}
			else if( i == ( callstack.Size() - 2 ) )
			{
				ids.PushBack( dvs.DrawText( Vector4( 500.0, vPos, 0.0, 0.0 ), extraTag + NameToString( callstack[ i ] ), gameDebugViewETextAlignment.Left, SColor.Yellow() ) );
			}
			else
			{
				ids.PushBack( dvs.DrawText( Vector4( 500.0, vPos, 0.0, 0.0 ), extraTag + NameToString( callstack[ i ] ), gameDebugViewETextAlignment.Left, SColor.White() ) );
			}
			j += 1;
		}
	}

}

struct RevealPlayerSettings
{
	editable var revealPlayer : ERevealPlayerType;
	[ tooltip = "If true - player will be revealed to NPCs even if he is already outside of SecuritySystem perimeter" ]
	editable var revealPlayerOutsideSecurityPerimeter : Bool;
}

class PendingSecuritySystemDisable extends Event
{
	var isPending : Bool;
}

class SecuritySystemDisabled extends Event
{
}

class SecuritySystemEnabled extends Event
{
}

class SecuritySystemSupport extends Event
{
	var supportGranted : Bool;
}

class PlayerSpotted extends Event
{
	private var comesFromNPC : Bool;
	private var ownerID : PersistentID;
	private var doesSee : Bool;
	private var agentAreas : array< SecurityAreaControllerPS >;

	public static function Construct( isReporterNPC : Bool, owner : PersistentID, doSee : Bool, const areas : ref< array< SecurityAreaControllerPS > > ) : PlayerSpotted
	{
		var ps : PlayerSpotted;
		if( !( PersistentID.IsDefined( owner ) ) )
		{
			return ( ( PlayerSpotted )( NULL ) );
		}
		ps = new PlayerSpotted;
		ps.comesFromNPC = isReporterNPC;
		ps.ownerID = owner;
		ps.doesSee = doSee;
		ps.agentAreas = areas;
		return ps;
	}

	public const function GetComesFromNPC() : Bool
	{
		return comesFromNPC;
	}

	public const function GetOwnerID() : PersistentID
	{
		return ownerID;
	}

	public const function DoesSee() : Bool
	{
		return doesSee;
	}

	public const function GetAgentAreas() : array< SecurityAreaControllerPS >
	{
		return agentAreas;
	}

}

struct NPCReference
{
	editable var communitySpawner : NodeRef;
	editable var entryName : CName;

	public static function IsValid( self : NPCReference ) : Bool
	{
		return GlobalNodeRef.IsDefined( ResolveNodeRef( self.communitySpawner, GlobalNodeID.GetRoot() ) ) && IsNameValid( self.entryName );
	}

	public static function GetSpawnerEntityID( self : NPCReference ) : EntityID
	{
		return ( ( EntityID )( ResolveNodeRef( self.communitySpawner, GlobalNodeID().GetRoot() ) ) );
	}

}

struct SecuritySystemOutputData
{
	var link : DeviceLink;
	var breachOrigin : EBreachOrigin;
	var delayDuration : Float;
}

class SetSecuritySystemState extends Event
{
	editable var state : ESecuritySystemState;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Set Security System State";
	}

}

class SuppressSecuritySystemStateChange extends Event
{
	[ tooltip = "From now on security system can be changed ONLY via quest blocks. ALL gameplay events will be discarded until this flag is set to false." ]
	editable var forceSecuritySystemIntoStrictQuestControl : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Suppress Security System State Change";
	}

}

class AuthorizePlayerInSecuritySystem extends Event
{
	[ tooltip = "Should this event authorize player, or remove him from the list of authorized users. ESL is ingored if authorize == FALSE" ]
	editable var authorize : Bool;
	default authorize = true;
	[ tooltip = "If this is turned to FALSE player will be able to use devices in given SecuritySystem (doors/computers etc) however if he is blacklisted he will still be attacked by turrets and NPCs. We support case where player is both blacklisted and authorized at the same time" ]
	editable var forceRemoveFromBlacklist : Bool;
	default forceRemoveFromBlacklist = true;
	[ tooltip = "how strong should the authorization be. If you have two areas with different ESL (i.e ESL_1 and ESL_2) with this property you can give player authorization in one area but not the other. ESL_4 == TOTAL AUTHORIZATION EVERYWHERE" ]
	editable var ESL : ESecurityAccessLevel;
	default ESL = ESecurityAccessLevel.ESL_4;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Authorize Player in Security System";
	}

}

class BlacklistPlayer extends Event
{
	[ tooltip = "Should this event blacklist player, or removing him from the blacklist. Reason is ingored if blacklist == FALSE" ]
	editable var blacklist : Bool;
	default blacklist = true;
	[ tooltip = "Determine why target is blacklisted. It will influence how Security System resolves response against player in the future. Trespasser will be treated differently than someone blacklisted for starting a combat." ]
	editable var reason : BlacklistReason;
	default reason = BlacklistReason.COMBAT;
	[ tooltip = "Player's authorization will be void. Keep in mind this may remove authorization that player has granted himself via gameplay if it was possible" ]
	editable var forceRemoveAuthorization : Bool;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Blacklist player";
	}

}

class SuppressNPCInSecuritySystem extends Event
{
	[ tooltip = "This NPC will NO LONGER receive events FROM security system" ]
	editable var suppressIncomingEvents : Bool;
	[ tooltip = "This NPC will NO LONGER send events TO security system" ]
	editable var suppressOutgoingEvents : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Suppress Communication Between Security System & NPC";
	}

}

class QuestChangeSecuritySystemAttitudeGroup extends Event
{
	[ customEditor = "TweakDBGroupInheritance;AttitudeGroup" ][ tooltip = "Security System and all of its DEVICES (not NPCs) will change attitude group to this one" ]
	editable var newAttitudeGroup : TweakDBID;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Change Security System Attitude Group";
	}

}

struct SpawnerData
{
	var spawnerID : EntityID;
	var entryNames : array< CName >;

	public static function Construct( id : EntityID, entry : CName ) : SpawnerData
	{
		var s : SpawnerData;
		s.spawnerID = id;
		s.entryNames.PushBack( entry );
		return s;
	}

}

abstract class QuestSecuritySystemInput extends Event
{
	[ tooltip = "GLOBAL = All NPCs in Security System + SpecificNPCs. SPECIFIC_AGENTS_ONLY = Only NPCs who you specified in NotifySpecificNPCs" ]
	editable var notificationScope : SecurityEventScopeSettings;
	[ tooltip = "This npc will react to this event. They don't have to be part of Security System" ]
	const editable var notifySpecificNPCs : array< NPCReference >;
	editable var revealPlayerSettings : RevealPlayerSettings;
}

class QuestIllegalActionNotification extends QuestSecuritySystemInput
{

	public constexpr function GetFriendlyDescription() : String
	{
		return "Illegal Action Notification [ SYSTEM ]";
	}

}

class QuestCombatActionNotification extends QuestSecuritySystemInput
{

	public constexpr function GetFriendlyDescription() : String
	{
		return "Combat Action Notification [ SYSTEM ]";
	}

}

class QuestAddTransition extends Event
{
	[ tooltip = "This transition will be added to  the list of already exisitng transtions. It will not be executed immediately." ]
	editable var transition : AreaTypeTransition;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Add permanent transtion";
	}

}

class PurgeAllTransitions extends Event
{
}

class QuestRemoveTransition extends Event
{
	[ rangeMin = "0" ][ rangeMax = "23" ][ tooltip = "Pick hour. If any transition will be found at this hour it will be removed." ]
	editable var removeTransitionFrom : Int32;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Remove permanent transtion";
	}

}

class QuestExecuteTransition extends Event
{
	[ tooltip = "Perform transition immediately. Transition Hour property is ignored in this event" ]
	editable var transition : AreaTypeTransition;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Immediately Execute Single Transtion";
	}

}

class QuestCombatActionAreaNotification extends Event
{
	editable var revealPlayerSettings : RevealPlayerSettings;

	public constexpr function GetFriendlyDescription() : String
	{
		return "[ Area ] Combat Action Notification";
	}

}

enum EQuestFilterType
{
	DONT_CHANGE = 0,
	ALLOW_NONE = 1,
	ALLOW_COMBAT_ONLY = 2,
	ALLOW_ALL = 3,
}

class QuestModifyFilters extends Event
{
	editable var incomingFilters : EQuestFilterType;
	editable var outgoingFilters : EQuestFilterType;
}

class QuestIllegalActionAreaNotification extends Event
{
	editable var revealPlayerSettings : RevealPlayerSettings;

	public constexpr function GetFriendlyDescription() : String
	{
		return "[ Area ] Illegal Action Notification";
	}

}

enum AttitudeChange
{
	DONT_CHANGE = 0,
	FRIENDLY = 1,
	NEUTRAL = 2,
	HOSTILE = 3,
}

enum SecurityEventScopeSettings
{
	GLOBAL = 0,
	AREA_WHERE_PLAYER_IS = 1,
	SPECIFIC_AGENTS_ONLY = 2,
}

enum EPermissionSource
{
	GAMEPLAY = 0,
	PLAYER = 1,
	QUEST = 2,
}

enum ERevealPlayerType
{
	DONT_REVEAL = 0,
	REVEAL_ONCE = 1,
}

enum EAllowedTo
{
	UNDEFINED = 0,
	TRESSPASSING = 1,
	ILLEGAL_ACTIONS = 2,
	COMBAT = 3,
}

enum ESecurityAccessLevel
{
	ESL_NONE = 0,
	ESL_LOCAL = 1,
	ESL_0 = 2,
	ESL_1 = 3,
	ESL_2 = 4,
	ESL_3 = 5,
	ESL_4 = 6,
}

enum BlacklistReason
{
	UNINITIALIZED = 0,
	TRESPASSING = 1,
	REPRIMAND = 2,
	COMBAT = 3,
}

enum ESecuritySystemState
{
	UNINITIALIZED = 0,
	SAFE = 1,
	ALERTED = 2,
	COMBAT = 3,
}

enum EReprimandInstructions
{
	INITIATE_FIRST = 0,
	INITIATE_SUCCESSIVE = 1,
	TAKEOVER = 2,
	CONCLUDE_SUCCESSFUL = 3,
	CONCLUDE_FAILED = 4,
	RELEASE_TO_ANOTHER_ENTITY = 5,
}

enum EBreachOrigin
{
	LOCAL = 0,
	EXTERNAL = 1,
	GLOBAL = 2,
}

enum ESecurityNotificationType
{
	REPRIMAND_SUCCESSFUL = -2,
	DEESCALATE = -1,
	DEFAULT = 0,
	ILLEGAL_ACTION = 1,
	REPRIMAND_ESCALATE = 2,
	DEVICE_DESTROYED = 3,
	ALARM = 4,
	SECURITY_GATE = 5,
	COMBAT = 6,
	QUEST = 7,
}

enum EShouldChangeAttitude
{
	PERSISTENTLY = 0,
	TEMPORARLY = 1,
}

class BlacklistPeriodEnded extends Event
{
	var entityID : EntityID;
}

class SuppressSecuritySystemReaction extends Event
{
	var enableProtection : Bool;
	var protectedEntityID : EntityID;
	var entered : Bool;
	var hasEntityWithdrawn : Bool;
}

class NewCycleEvent extends Event
{
	var cyclesCount : Uint16;
}

class DeescalationEvent extends Event
{
	var originalNotification : SecuritySystemInput;
}

class AutomaticDeescalationEvent extends Event
{
	var originalNotification : SecuritySystemInput;
}

class AddToBlacklistEvent extends Event
{
	var entityIDToAdd : EntityID;
	var isPlayerEntity : Bool;
	var reason : BlacklistReason;
}

class RemoveFromBlacklistEvent extends Event
{
	var entityIDToRemove : EntityID;
	var isPlayerEntity : Bool;
}

class ReprimandUpdate extends Event
{
	var reprimandInstructions : EReprimandInstructions;
	var target : EntityID;
	var targetPos : Vector4;
	var currentPerformer : weak< GameObject >;

	public static function Construct( performer : GameObject, target : EntityID, instructions : EReprimandInstructions, optional pos : Vector4 ) : ReprimandUpdate
	{
		var update : ReprimandUpdate;
		update = new ReprimandUpdate;
		update.currentPerformer = performer;
		update.target = target;
		update.reprimandInstructions = instructions;
		update.targetPos = pos;
		return update;
	}

}

class ReprimandAgentDisconnectEvent extends Event
{
	var agentID : EntityID;
}

class NotifiedSecSysAboutCombat extends Event
{
}

struct AgentDistanceToTarget
{
	var distance : Float;
	var index : Int32;
}

class TakeOverSecuritySystem extends ActionBool
{
	default actionName = 'TakeOverSecuritySystem';

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

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

	public static function IsAvailable( device : ScriptableDeviceComponentPS ) : Bool
	{
		if( device.IsUnpowered() || device.IsOFF() )
		{
			return false;
		}
		return true;
	}

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

}

class FullSystemRestart extends ActionBool
{
	var m_restartDuration : Int32;
	default m_restartDuration = 10;

	public function SetProperties( isRestarting : Bool, duration : Int32 )
	{
		actionName = 'FullSystemRestart';
		m_restartDuration = duration;
		prop = DeviceActionPropertyFunctions.SetUpProperty_Bool( actionName, isRestarting, 'LocKey#17836', 'LocKey#17837' );
	}

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

	public static function IsAvailable( device : ScriptableDeviceComponentPS ) : Bool
	{
		if( device.IsUnpowered() || device.IsOFF() )
		{
			return false;
		}
		return true;
	}

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

}

class SecuritySystemStatus extends BaseDeviceStatus
{

	public override function SetProperties( const deviceRef : ScriptableDeviceComponentPS )
	{
		super.SetProperties( deviceRef );
		actionName = 'SecuritySystemStatus';
		prop.second = ( ( Int32 )( ( ( SecuritySystemControllerPS )( deviceRef ) ).GetSecurityState() ) );
	}

	public const override function GetCurrentDisplayString() : String
	{
		var str : String;
		if( m_isRestarting )
		{
			return "LocKey#17797";
		}
		if( ( ( Int32 )prop.first ) > 0 )
		{
			switch( prop.second )
			{
				case 1:
					str = "LocKey#17798";
				break;
				case 2:
					str = "LocKey#17799";
				break;
				case 3:
					str = "LocKey#17800";
				break;
				default:
					if( !( IsFinal() ) )
					{
					}
			}
		}
		else
		{
			return super.GetCurrentDisplayString();
		}
		return str;
	}

	public const override function GetStatusValue() : Int32
	{
		if( ( ( Int32 )prop.first ) > 0 )
		{
			return ( ( Int32 )prop.second );
		}
		else
		{
			return ( ( Int32 )prop.first );
		}
	}

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

	public static function IsAvailable( device : ScriptableDeviceComponentPS ) : Bool
	{
		return BaseDeviceStatus.IsAvailable( device );
	}

	public static function IsClearanceValid( clearance : Clearance ) : Bool
	{
		return BaseDeviceStatus.IsClearanceValid( clearance );
	}

	public override function GetTweakDBChoiceRecord() : String
	{
		return "wrong_action";
	}

}

class SecuritySystemInput extends SecurityAreaEvent
{
	private var m_lastKnownPosition : Vector4;
	private var m_notifier : SharedGameplayPS;
	private var m_type : ESecurityNotificationType;
	private var m_objectOfInterest : weak< GameObject >;
	private var m_canPerformReprimand : Bool;
	private var m_shouldLeadReprimend : Bool;
	private var m_id : Int32;
	default m_id = -1;
	private var m_customRecipientsList : array< EntityID >;
	private var m_isSharingRestricted : Bool;
	private var m_debugReporterCharRecord : Character_Record;
	private var m_stimTypeTriggeredAlarm : gamedataStimType;

	public function Initialize( initialEvent : SecuritySystemInput )
	{
		SetProperties( initialEvent.GetLastKnownPosition(), initialEvent.GetWhoBreached(), initialEvent.GetNotifierHandle(), initialEvent.GetNotificationType(), initialEvent.CanPerformReprimand(), initialEvent.ShouldLeadReprimend(), initialEvent.GetID(), initialEvent.GetCustomRecipientsList(), initialEvent.IsSharingRestricted() );
	}

	public function SetProperties( lkp : Vector4, whoBreached : GameObject, reporter : weak< SharedGameplayPS >, type : ESecurityNotificationType, canDoReprimand : Bool, shouldLeadReprimand : Bool, optional stimType : gamedataStimType )
	{
		m_lastKnownPosition = lkp;
		if( type == ESecurityNotificationType.ALARM || type == ESecurityNotificationType.DEVICE_DESTROYED )
		{
			m_objectOfInterest = whoBreached;
		}
		else
		{
			SetWhoBreached( whoBreached );
		}
		m_notifier = reporter;
		m_type = type;
		m_canPerformReprimand = canDoReprimand;
		m_shouldLeadReprimend = shouldLeadReprimand;
		m_stimTypeTriggeredAlarm = stimType;
	}

	public function SetProperties( lkp : Vector4, whoBreached : GameObject, reporter : weak< SharedGameplayPS >, type : ESecurityNotificationType, canDoReprimand : Bool, shouldLeadReprimand : Bool, id : Int32, const customRecipients : ref< array< EntityID > >, isSharingRestricted : Bool )
	{
		SetProperties( lkp, whoBreached, reporter, type, canDoReprimand, shouldLeadReprimand );
		m_id = id;
		m_customRecipientsList = customRecipients;
		m_isSharingRestricted = isSharingRestricted;
	}

	public const function GetNotifierHandle() : weak< SharedGameplayPS >
	{
		return m_notifier;
	}

	public const function GetNotificationType() : ESecurityNotificationType
	{
		return m_type;
	}

	public const function GetObjectOfInterest() : GameObject
	{
		return m_objectOfInterest;
	}

	public const function CanPerformReprimand() : Bool
	{
		return m_canPerformReprimand;
	}

	public const function ShouldLeadReprimend() : Bool
	{
		return m_shouldLeadReprimend;
	}

	public const function GetLastKnownPosition() : Vector4
	{
		return m_lastKnownPosition;
	}

	public const function GetID() : Int32
	{
		return m_id;
	}

	public const function HasCustomRecipients() : Bool
	{
		return m_customRecipientsList.Size() > 0;
	}

	public const function GetCustomRecipientsList() : array< EntityID >
	{
		return m_customRecipientsList;
	}

	public const function IsSharingRestricted() : Bool
	{
		return m_isSharingRestricted;
	}

	public const function GetStimTypeTriggeredAlarm() : gamedataStimType
	{
		return m_stimTypeTriggeredAlarm;
	}

	public const function GetPuppetCharRecord() : Character_Record
	{
		return m_debugReporterCharRecord;
	}

	public const function GetPuppetDisplayName() : String
	{
		return LocKeyToString( m_debugReporterCharRecord.DisplayName() );
	}

	public function SetAsReprimendLeader( isLeader : Bool )
	{
		m_shouldLeadReprimend = isLeader;
	}

	public function SetID( id : Int32 )
	{
		m_id = id;
	}

	public function AttachCustomRecipientsList( const list : ref< array< EntityID > > )
	{
		m_customRecipientsList = list;
	}

	public function ModifyNotificationType( newEventType : ESecurityNotificationType )
	{
		m_type = newEventType;
	}

	public function RestrictSharing()
	{
		m_isSharingRestricted = true;
	}

	public function SetLastKnownPosition( lkp : Vector4 )
	{
		m_lastKnownPosition = lkp;
	}

	public function SetObjectOfInterest( object : weak< GameObject > )
	{
		m_objectOfInterest = object;
	}

	public function SetPuppetCharacterRecord( record : TweakDBID )
	{
		m_debugReporterCharRecord = TweakDBInterface.GetCharacterRecord( record );
	}

}

class SecuritySystemOutput extends ActionBool
{
	private var m_currentSecurityState : ESecuritySystemState;
	private var m_breachOrigin : EBreachOrigin;
	private var m_originalInputEvent : SecuritySystemInput;
	private var m_securityStateChanged : Bool;

	public function Initialize( originalEvent : SecuritySystemOutput )
	{
		var inputCopy : SecuritySystemInput;
		inputCopy = new SecuritySystemInput;
		inputCopy.Initialize( originalEvent.GetOriginalInputEvent() );
		m_currentSecurityState = originalEvent.GetCachedSecurityState();
		m_breachOrigin = originalEvent.GetBreachOrigin();
		m_securityStateChanged = originalEvent.GetSecurityStateChanged();
		m_originalInputEvent = inputCopy;
	}

	public function SetProperties( currentSecuritySystemState : ESecuritySystemState, notificationEvent : SecuritySystemInput )
	{
		actionName = 'SecuritySystemOutput';
		m_currentSecurityState = currentSecuritySystemState;
		m_originalInputEvent = notificationEvent;
	}

	public const function GetCachedSecurityState() : ESecuritySystemState
	{
		return m_currentSecurityState;
	}

	public const function GetOriginalInputEvent() : SecuritySystemInput
	{
		return m_originalInputEvent;
	}

	public const function GetSecurityStateChanged() : Bool
	{
		return m_securityStateChanged;
	}

	public const function GetBreachOrigin() : EBreachOrigin
	{
		return m_breachOrigin;
	}

	public function SetSecurityStateChanged( changed : Bool )
	{
		m_securityStateChanged = changed;
	}

	public function SetBreachOrigin( breachType : EBreachOrigin )
	{
		m_breachOrigin = breachType;
	}

	public function SetCachedSecuritySystemState( state : ESecuritySystemState )
	{
		m_currentSecurityState = state;
	}

}

class TargetAssessmentRequest extends ScriptableDeviceAction
{
	var targetToAssess : weak< GameObject >;
}

class SecuritySystemForceAttitudeChange extends ScriptableDeviceAction
{
	var newAttitude : CName;
}

class AddUserEvent extends Event
{
	var userEntry : SecuritySystemClearanceEntry;
}

class RevokeAuthorization extends Event
{
	var user : EntityID;
	var level : ESecurityAccessLevel;
}

struct SecurityAccessLevelEntry
{
	[ customEditor = "TweakDBGroupInheritance;Keycards.Keycard" ][ unsavable = "true" ]
	instanceeditable persistent var m_keycard : TweakDBID;
	[ unsavable = "true" ]
	instanceeditable persistent var m_password : CName;

	public static function IsDataValid( self : SecurityAccessLevelEntry ) : Bool
	{
		if( SecurityAccessLevelEntry.IsKeycardValid( self ) || SecurityAccessLevelEntry.IsPasswordValid( self ) )
		{
			return true;
		}
		return false;
	}

	public static function IsPasswordValid( self : SecurityAccessLevelEntry ) : Bool
	{
		return IsNameValid( self.m_password );
	}

	public static function IsKeycardValid( self : SecurityAccessLevelEntry ) : Bool
	{
		return TDBID.IsValid( self.m_keycard ) && ( self.m_keycard != T"Keycards.None" );
	}

}

struct SecuritySystemClearanceEntry
{
	persistent var user : EntityID;
	persistent var level : ESecurityAccessLevel;
}

struct SecurityAccessLevelEntryClient extends SecurityAccessLevelEntry
{
	[ unsavable = "true" ]
	instanceeditable persistent var m_level : ESecurityAccessLevel;

	public static function IsDataValid( self : SecurityAccessLevelEntryClient ) : Bool
	{
		var base : SecurityAccessLevelEntry;
		base.m_keycard = self.m_keycard;
		base.m_password = self.m_password;
		return SecurityAccessLevelEntry.IsDataValid( base );
	}

	public static function IsPasswordValid( self : SecurityAccessLevelEntryClient ) : Bool
	{
		return IsNameValid( self.m_password );
	}

	public static function IsKeycardValid( self : SecurityAccessLevelEntryClient ) : Bool
	{
		return TDBID.IsValid( self.m_keycard );
	}

}

class BlacklistEntry extends IScriptable
{
	[ unsavable = "true" ]
	private persistent var entryID : EntityID;
	[ unsavable = "true" ]
	private persistent var entryReason : BlacklistReason;
	[ unsavable = "true" ]
	private persistent var warningsCount : Int32;
	[ unsavable = "true" ]
	private persistent var reprimandID : Int32;

	public function Initialize( entityID : EntityID, reason : BlacklistReason, id : Int32 )
	{
		entryID = entityID;
		entryReason = reason;
		if( reason == BlacklistReason.REPRIMAND )
		{
			reprimandID = id;
			warningsCount = 1;
		}
	}

	public const function GetEntityID() : EntityID
	{
		return entryID;
	}

	public const function GetReason() : BlacklistReason
	{
		return entryReason;
	}

	public const function GetWarningsCount() : Int32
	{
		return warningsCount;
	}

	private function AddWarning()
	{
		warningsCount += 1;
	}

	private function ResetWarnings()
	{
		warningsCount = 0;
	}

	public function UpdateBlacklistEntry( reason : BlacklistReason, id : Int32 ) : Bool
	{
		if( ( ( Int32 )( reason ) ) < ( ( Int32 )( GetReason() ) ) )
		{
			return false;
		}
		if( reason == BlacklistReason.COMBAT )
		{
			ResetWarnings();
		}
		else if( reason == BlacklistReason.REPRIMAND && ( reprimandID != id ) )
		{
			AddWarning();
		}
		if( reason != GetReason() )
		{
			entryReason = reason;
			return true;
		}
		return false;
	}

	public function ForgetReason()
	{
		entryReason = BlacklistReason.UNINITIALIZED;
	}

}

struct OutputValidationDataStruct
{
	var targetID : EntityID;
	var agentID : PersistentID;
	var reprimenderID : EntityID;
	var eventReportedFromArea : PersistentID;
	var eventType : ESecurityNotificationType;
	var breachedAreas : array< PersistentID >;

	public static function Construct( evt : SecuritySystemInput, currentReprimender : EntityID, const breachedAreas : ref< array< PersistentID > > ) : OutputValidationDataStruct
	{
		var ovd : OutputValidationDataStruct;
		ovd.targetID = evt.GetWhoBreached().GetEntityID();
		if( evt.GetNotifierHandle() )
		{
			ovd.agentID = evt.GetNotifierHandle().GetID();
		}
		ovd.reprimenderID = currentReprimender;
		ovd.eventType = evt.GetNotificationType();
		ovd.breachedAreas = breachedAreas;
		return ovd;
	}

	public static function IsDuplicated( const self : ref< OutputValidationDataStruct >, evt : SecuritySystemInput, currentReprimender : EntityID, const currentlyBreachedAreas : ref< array< PersistentID > > ) : Bool
	{
		var securityArea : SecurityAreaData;
		var i : Int32;
		var k : Int32;
		if( evt.GetWhoBreached().GetEntityID() != self.targetID )
		{
			return false;
		}
		if( evt.GetNotifierHandle().GetID() != self.agentID )
		{
			return false;
		}
		if( currentReprimender != self.reprimenderID )
		{
			return false;
		}
		if( evt.GetNotificationType() != self.eventType )
		{
			return false;
		}
		if( securityArea.id != self.eventReportedFromArea )
		{
			return false;
		}
		if( self.breachedAreas.Size() != currentlyBreachedAreas.Size() )
		{
			return false;
		}
		for( i = 0; i < self.breachedAreas.Size(); i += 1 )
		{
			for( k = 0; k < currentlyBreachedAreas.Size(); k += 1 )
			{
				if( self.breachedAreas[ i ] != currentlyBreachedAreas[ i ] )
				{
					return false;
				}
			}
		}
		return true;
	}

}

struct NPCDebugInfo
{
	var spawnerID : EntityID;
	var communityName : CName;
	var characterRecord : Character_Record;

	public static function IsValid( self : NPCDebugInfo ) : Bool
	{
		if( EntityID.IsDefined( self.spawnerID ) && IsNameValid( self.communityName ) )
		{
			return true;
		}
		return false;
	}

}

struct Time
{
	instanceeditable var days : Int32;
	default days = 0;
	[ rangeMin = "0" ][ rangeMax = "23" ]
	instanceeditable var hours : Int32;
	default hours = 0;
	[ rangeMin = "0" ][ rangeMax = "59" ]
	instanceeditable var minutes : Int32;
	default minutes = 0;
}

operator>( enum1 : gameCityAreaType, enum2 : gameCityAreaType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) > ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator||( att : EAIAttitude, match : Bool ) : Bool
{
	return true;
}

operator>( enum1 : ESecurityAccessLevel, enum2 : ESecurityAccessLevel ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) > ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator<( enum1 : ESecurityAccessLevel, enum2 : ESecurityAccessLevel ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) < ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator-( level1 : ESecurityAccessLevel, value : Int32 ) : ESecurityAccessLevel
{
	var outcomeInt : Int32;
	var outcome : ESecurityAccessLevel;
	outcomeInt = ( ( Int32 )( level1 ) ) - value;
	if( outcomeInt < 0 )
	{
		outcomeInt = 0;
	}
	if( outcomeInt > ( ( Int32 )( EnumGetMax( 'ESecurityAccessLevel' ) ) ) )
	{
		outcomeInt = ( ( Int32 )( EnumGetMax( 'ESecurityAccessLevel' ) ) );
	}
	outcome = ( ( ESecurityAccessLevel )( outcomeInt ) );
	return outcome;
}

operator>( enum1 : ESecuritySystemState, enum2 : ESecuritySystemState ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) > ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator<( enum1 : ESecuritySystemState, enum2 : ESecuritySystemState ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) < ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator>( enum1 : ESecurityNotificationType, enum2 : ESecurityNotificationType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) > ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator<( enum1 : ESecurityNotificationType, enum2 : ESecurityNotificationType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) < ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator>=( enum1 : ESecurityNotificationType, enum2 : ESecurityNotificationType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) >= ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator<=( enum1 : ESecurityNotificationType, enum2 : ESecurityNotificationType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) <= ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator>( enum1 : ESecurityAreaType, enum2 : ESecurityAreaType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) > ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

operator<( enum1 : ESecurityAreaType, enum2 : ESecurityAreaType ) : Bool
{
	if( ( ( Int32 )( enum1 ) ) < ( ( Int32 )( enum2 ) ) )
	{
		return true;
	}
	return false;
}

