class AlarmEvent extends Event
{
	var isValid : Bool;
	var ID : DelayID;
}

enum ESurveillanceCameraStatus
{
	THREAT = -1,
	STREAMING = 0,
	WORKING = 1,
}

enum ESurveillanceCameraState
{
	Off = 0,
	Active = 1,
}

struct SurveillanceCameraResaveData
{
	var m_shouldRotate : Bool;
	var m_maxRotationAngle : Float;
	var m_pitchAngle : Float;
	var m_rotationSpeed : Float;
	var m_canStreamVideo : Bool;
	var m_canDetectIntruders : Bool;
	var m_canBeControled : Bool;
	var m_factOnFeedReceived : CName;
	var m_questFactOnDetection : CName;
}

class SurveillanceCamera extends SensorDevice
{
	default m_controllerTypeName = 'SurveillanceCameraController';
	default m_idleSound = 'dev_surveillance_camera_rotating';
	default m_idleSoundStop = 'dev_surveillance_camera_rotating_stop';
	default m_soundDetectionLoop = 'dev_surveillance_camera_detection_loop_start';
	default m_soundDetectionLoopStop = 'dev_surveillance_camera_detection_loop_stop';
	private var m_virtualCam : VirtualCameraComponent;
	private var m_cameraHead : IComponent;
	private var m_cameraHeadPhysics : IComponent;
	private var m_verticalDecal1 : IComponent;
	private var m_verticalDecal2 : IComponent;
	private editable var m_meshDestrSupport : Bool;
	private var m_shouldRotate : Bool;
	default m_shouldRotate = true;
	hint m_shouldRotate = "Do you want the camera to rotate or stay still";
	private var m_canStreamVideo : Bool;
	default m_canStreamVideo = false;
	hint m_canStreamVideo = "Check this if you want camera to be able to stream video to other devices - SUPER PERFOMANCE HEAVY!";
	private var m_canDetectIntruders : Bool;
	default m_canDetectIntruders = true;
	private var m_currentAngle : Float;
	private var m_rotateLeft : Bool;
	private var m_targetPosition : Vector4;
	private var m_factOnFeedReceived : CName;
	hint m_factOnFeedReceived = "When Feed is received this fact will be set to 1 and when feed will stop it will be set to 0";
	private var m_questFactOnDetection : CName;
	hint m_questFactOnDetection = "This facts will be set to 1 if camera detects intruder and to 0 if it stops detecting";
	private var m_lookAtEvent : LookAtAddEvent;
	var m_currentYawModifier : Float;
	var m_currentPitchModifier : Float;

	private function SetForcedSensesTracing()
	{
		if( GetDevicePS().IsON() && GetSensesComponent().IsEnabled() )
		{
			GameInstance.GetDelaySystem( GetGame() ).QueueTask( this, NULL, 'SetForcedSensesTracingTask', gameScriptTaskExecutionStage.Any );
		}
	}

	protected export function SetForcedSensesTracingTask( data : ScriptTaskData )
	{
		if( GetDevicePS().CanTagEnemies() )
		{
			GetSensesComponent().SetForcedSensesTracing( gamedataSenseObjectType.Npc, EAIAttitude.AIA_Neutral );
			GetSensesComponent().SetForcedSensesTracing( gamedataSenseObjectType.Npc, EAIAttitude.AIA_Hostile );
			GetSensesComponent().SetTickDistanceOverride( 100.0 );
		}
		else
		{
			GetSensesComponent().RemoveForcedSensesTracing( gamedataSenseObjectType.Npc, EAIAttitude.AIA_Neutral );
			GetSensesComponent().RemoveForcedSensesTracing( gamedataSenseObjectType.Npc, EAIAttitude.AIA_Hostile );
			GetSensesComponent().SetTickDistanceOverride( -1.0 );
		}
	}

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		super.OnRequestComponents( ri );
		EntityRequestComponentsInterface.RequestComponent( ri, 'virtualcamera', 'VirtualCameraComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'updateComponent', 'UpdateComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'detectionAreaIndicator', 'MeshComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'main_red', 'gameLightComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'right_point', 'gameLightComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'middle_point', 'gameLightComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'stripes_points', 'gameLightComponent', false );
		if( m_meshDestrSupport )
		{
			EntityRequestComponentsInterface.RequestComponent( ri, 'cameraHead', 'IComponent', true );
			EntityRequestComponentsInterface.RequestComponent( ri, 'cameraHeadPhysics', 'IComponent', true );
			EntityRequestComponentsInterface.RequestComponent( ri, 'vertical_decal_1', 'IComponent', false );
			EntityRequestComponentsInterface.RequestComponent( ri, 'vertical_decal_2', 'IComponent', false );
		}
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		m_virtualCam = ( ( VirtualCameraComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'virtualcamera' ) ) );
		m_lightScanRefs.PushBack( ( ( gameLightComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'main_red' ) ) ) );
		m_lightAttitudeRefs.PushBack( ( ( gameLightComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'stripes_points' ) ) ) );
		m_lightInfoRefs.PushBack( ( ( gameLightComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'middle_point' ) ) ) );
		m_lightInfoRefs.PushBack( ( ( gameLightComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'right_point' ) ) ) );
		if( m_meshDestrSupport )
		{
			m_cameraHead = ( ( IComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'cameraHead' ) ) );
			m_verticalDecal1 = ( ( IComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'vertical_decal_1' ) ) );
			m_verticalDecal2 = ( ( IComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'vertical_decal_2' ) ) );
			m_cameraHeadPhysics = ( ( IComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'cameraHeadPhysics' ) ) );
		}
		super.OnTakeControl( ri );
		m_controller = ( ( SurveillanceCameraController )( EntityResolveComponentsInterface.GetComponent( ri, 'controller' ) ) );
	}

	protected event OnGameAttached()
	{
		super.OnGameAttached();
		SetSenseObjectType( gamedataSenseObjectType.Camera );
	}

	protected override function ResolveGameplayState()
	{
		super.ResolveGameplayState();
		RegisterToGameSessionDataSystem( GetDevicePS().CanTagEnemies() );
	}

	protected event OnDetach()
	{
		super.OnDetach();
		RegisterToGameSessionDataSystem( false );
	}

	protected override function EvaluateProximityRevealInteractionLayerState()
	{
		var player : PlayerPuppet;
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerControlledGameObject() ) );
		if( PlayerDevelopmentSystem.GetInstance( player ).IsNewPerkBought( player, gamedataNewPerkType.Intelligence_Left_Milestone_1 ) > 0 )
		{
			EnableProximityRevealInteractionLayer( true );
		}
		else
		{
			super.EvaluateProximityRevealInteractionLayerState();
		}
	}

	private const override function GetController() : SurveillanceCameraController
	{
		return ( ( SurveillanceCameraController )( m_controller ) );
	}

	public const override function GetDevicePS() : SurveillanceCameraControllerPS
	{
		return GetController().GetPS();
	}

	public const override function IsSurveillanceCamera() : Bool
	{
		return true;
	}

	protected const override function GetScannerName() : String
	{
		return "LocKey#100";
	}

	public override function OnValidTargetAppears( target : weak< GameObject > )
	{
		var puppet : ScriptedPuppet;
		super.OnValidTargetAppears( target );
		GetDevicePS().ThreatDetected( true );
		RequestAlarm();
		if( GetDevicePS().CanTagEnemies() && target.IsActive() )
		{
			puppet = ( ( ScriptedPuppet )( target ) );
			if( puppet && puppet.IsAggressive() )
			{
				GameObject.TagObject( puppet );
			}
		}
	}

	public override function OnCurrentTargetAppears( target : weak< GameObject > )
	{
		var puppet : ScriptedPuppet;
		puppet = ( ( ScriptedPuppet )( target ) );
		if( GetDevicePS().ShouldRevealEnemies() )
		{
			ScriptedPuppet.RequestRevealOutline( puppet, true, GetEntityID() );
		}
		RequestAlarm();
		PlaySoundEvent( this, 'q001_sc_00a_before_mission_tbug_hack' );
		if( ( !( GetDevicePS().GetSecuritySystem().IsSystemInCombat() ) && !( IsFriendlyTowardsPlayer( this ) ) ) && ( target == GetPlayer( GetGame() ) ) )
		{
			SetWarningMessage( "LocKey#53158" );
		}
		super.OnCurrentTargetAppears( target );
	}

	protected export override function DamagePipelineFinalized( evt : gameHitEvent )
	{
		var source : BaseGrenade;
		super.DamagePipelineFinalized( evt );
		source = ( ( BaseGrenade )( evt.attackData.GetSource() ) );
		if( source )
		{
			if( source.IsEMP() )
			{
				DeactivateDevice();
			}
		}
	}

	private function SetWarningMessage( const lockey : ref< String > )
	{
		var simpleScreenMessage : SimpleScreenMessage;
		simpleScreenMessage.isShown = true;
		simpleScreenMessage.duration = 5.0;
		simpleScreenMessage.message = lockey;
		simpleScreenMessage.isInstant = true;
		GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().UI_Notifications ).SetVariant( GetAllBlackboardDefs().UI_Notifications.WarningMessage, simpleScreenMessage, true );
	}

	public override function OnValidTargetDisappears( target : weak< GameObject > )
	{
		var puppet : ScriptedPuppet;
		puppet = ( ( ScriptedPuppet )( target ) );
		ScriptedPuppet.RequestRevealOutline( puppet, false, GetEntityID() );
		if( GetDevicePS().CanTagEnemies() )
		{
			GameObject.UntagObject( puppet );
		}
	}

	public override function OnAllValidTargetsDisappears()
	{
		super.OnAllValidTargetsDisappears();
		GetDevicePS().ThreatDetected( false );
		RequestAlarm();
	}

	protected override function PushPersistentData()
	{
		GetDevicePS().PushPersistentData();
		super.PushPersistentData();
	}

	protected override function RestoreDeviceState()
	{
		super.RestoreDeviceState();
	}

	protected override function DeactivateDevice()
	{
		super.DeactivateDevice();
		TurnOffDevice();
	}

	protected override function TurnOffDevice()
	{
		super.TurnOffDevice();
		TurnOffCamera();
	}

	protected override function TurnOnDevice()
	{
		super.TurnOnDevice();
		TurnOnCamera();
	}

	protected override function CutPower()
	{
		super.CutPower();
		TurnOffCamera();
	}

	protected event OnToggleStreamFeed( evt : ToggleStreamFeed )
	{
		UpdateDeviceState();
		ToggleFeed( GetDevicePS().ShouldStream() );
	}

	protected event OnToggleCamera( evt : ToggleON )
	{
		UpdateDeviceState();
		( ( GetDevicePS().IsON() ) ? ( TurnOnCamera() ) : ( TurnOffCamera() ) );
	}

	private function TurnOnCamera()
	{
		RequestAlarm();
		SetForcedSensesTracing();
	}

	private function TurnOffCamera()
	{
		ToggleFeed( false );
		ToggleAreaIndicator( false );
	}

	protected event OnDeath( evt : gameDeathEvent )
	{
		super.OnDeath( evt );
		if( m_meshDestrSupport )
		{
			m_cameraHead.Toggle( false );
			m_verticalDecal1.Toggle( false );
			m_verticalDecal2.Toggle( false );
			m_cameraHeadPhysics.Toggle( true );
		}
	}

	protected event OnSetDeviceAttitude( evt : SetDeviceAttitude )
	{
		super.OnSetDeviceAttitude( evt );
		SetForcedSensesTracing();
		RegisterToGameSessionDataSystem( true );
	}

	protected event OnCameraTagLockEvent( evt : CameraTagLockEvent )
	{
		GetDevicePS().SetTagLockFromSystem( evt.isLocked );
		SetForcedSensesTracing();
	}

	private function RegisterToGameSessionDataSystem( add : Bool )
	{
		var cameraTagLimitData : CameraTagLimitData;
		cameraTagLimitData = new CameraTagLimitData;
		cameraTagLimitData.add = add;
		cameraTagLimitData.object = this;
		GameSessionDataSystem.AddDataEntryRequest( GetGame(), EGameSessionDataType.CameraTagLimit, cameraTagLimitData );
	}

	private function ToggleFeed( shouldBeOn : Bool )
	{
		m_virtualCam.Toggle( shouldBeOn );
		if( shouldBeOn )
		{
			SetFactValue( GetGame(), m_factOnFeedReceived, 1 );
		}
		else
		{
			SetFactValue( GetGame(), m_factOnFeedReceived, 0 );
			GetDevicePS().ClearFeedReceivers();
		}
	}

	private function RequestAlarm()
	{
		if( GetDevicePS().IsDetecting() )
		{
			SetFactValue( GetGame(), GetDevicePS().GetQuestFactOnDetection(), 1 );
		}
		else
		{
			SetFactValue( GetGame(), GetDevicePS().GetQuestFactOnDetection(), 0 );
		}
	}

	protected override function OverrideLookAtSetupHor( out lookAtEntityEvent : LookAtAddEvent )
	{
		lookAtEntityEvent.request.limits.softLimitDegrees = GetDevicePS().GetBehaviourMaxRotationAngle() * 2.0;
	}

	public const override function DeterminGameplayRole() : EGameplayRole
	{
		return EGameplayRole.Alarm;
	}

	protected event OnActionEngineering( evt : ActionEngineering )
	{
		PlaySoundEvent( this, 'dev_surveillance_camera_fry_circuit' );
		UpdateDeviceState();
	}

	protected event OnTCSTakeOverControlActivate( evt : TCSTakeOverControlActivate )
	{
		super.OnTCSTakeOverControlActivate( evt );
		GameObjectEffectHelper.StartEffectEvent( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerControlledGameObject(), 'fish_eye' );
	}

	protected event OnTCSTakeOverControlDeactivate( evt : TCSTakeOverControlDeactivate )
	{
		super.OnTCSTakeOverControlDeactivate( evt );
		GameObjectEffectHelper.StopEffectEvent( GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerControlledGameObject(), 'fish_eye' );
	}

	public const override function GetRotationData() : CameraRotationData
	{
		var res : CameraRotationData;
		res = super.GetRotationData();
		if( GetDevicePS().ShouldOverrideTakeOverAngle() )
		{
			res.m_pitch = GetDevicePS().GetOverrideTakeOverPitch();
			res.m_yaw = GetDevicePS().GetOverrideTakeOverYaw();
		}
		else if( !( GetDevicePS().IsON() ) )
		{
			res.m_pitch = 0.0;
			res.m_yaw = 0.0;
		}
		return res;
	}

}

