class Radio extends InteractiveDevice
{
	default m_controllerTypeName = 'RadioController';
	[ category = "High Pitch Noise Quick Hack Data" ]
	private editable inlined var m_effectAction : ScriptableDeviceAction;
	[ category = "High Pitch Noise Quick Hack Data" ]
	private editable var m_effectRef : EffectRef;
	[ category = "High Pitch Noise Quick Hack Data" ]
	private editable var m_statusEffect : TweakDBID;
	[ category = "Aoe Damage Quick Hack Data" ][ customEditor = "TweakDBGroupInheritance;Attacks.DeviceAttack" ]
	private editable var m_damageType : TweakDBID;
	private var m_startingStation : Int32;
	private var m_isInteractive : Bool;
	private var m_isShortGlitchActive : Bool;
	private var m_shortGlitchDelayID : DelayID;
	private var m_effectInstance : EffectInstance;
	private var m_targets : array< weak< ScriptedPuppet > >;
	private var m_vfxInstance : FxInstance;

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		EntityRequestComponentsInterface.RequestComponent( ri, 'audio', 'soundComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'radio_ui', 'worlduiWidgetComponent', false );
		super.OnRequestComponents( ri );
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		super.OnTakeControl( ri );
		m_uiComponent = ( ( worlduiWidgetComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'radio_ui' ) ) );
		m_controller = ( ( RadioController )( EntityResolveComponentsInterface.GetComponent( ri, 'controller' ) ) );
	}

	protected override function ResolveGameplayState()
	{
		super.ResolveGameplayState();
		if( IsUIdirty() && m_isInsideLogicArea )
		{
			RefreshUI();
		}
	}

	protected event OnToggleON( evt : ToggleON )
	{
		super.OnToggleON( evt );
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

	protected event OnTogglePower( evt : TogglePower )
	{
		super.OnTogglePower( evt );
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

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

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

	protected const override function GetController() : RadioController
	{
		return ( ( RadioController )( m_controller ) );
	}

	private function PlayGivenStation()
	{
		var isMetal : Bool;
		var radioStation : ERadioStationList;
		var stationEventName : CName;
		radioStation = GetDevicePS().GetActiveRadioStation();
		stationEventName = RadioStationDataProvider.GetStationName( radioStation );
		AudioSwitch( this, 'radio_station', stationEventName, 'radio' );
		isMetal = radioStation == ERadioStationList.METAL;
		MetalItUp( isMetal );
	}

	private function MetalItUp( isMetal : Bool )
	{
		if( GetDevicePS().GetDurabilityType() != EDeviceDurabilityType.INVULNERABLE )
		{
			if( isMetal )
			{
				GetDevicePS().SetDurabilityType( EDeviceDurabilityType.INDESTRUCTIBLE );
			}
			else
			{
				GetDevicePS().SetDurabilityType( EDeviceDurabilityType.DESTRUCTIBLE );
			}
		}
	}

	protected event OnNextStation( evt : NextStation )
	{
		PlayGivenStation();
		UpdateDeviceState();
		RefreshUI();
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

	protected event OnPreviousStation( evt : PreviousStation )
	{
		PlayGivenStation();
		UpdateDeviceState();
		RefreshUI();
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

	protected event OnQuestSetChannel( evt : QuestSetChannel )
	{
		PlayGivenStation();
		RefreshUI();
	}

	protected event OnQuestDefaultStation( evt : QuestDefaultStation )
	{
		PlayGivenStation();
		RefreshUI();
	}

	protected event OnSpiderbotDistraction( evt : SpiderbotDistraction )
	{
		OrderSpiderbot();
	}

	protected event OnSpiderbotOrderCompletedEvent( evt : SpiderbotOrderCompletedEvent )
	{
		SendSetIsSpiderbotInteractionOrderedEvent( false );
		GameInstance.GetActivityLogSystem( GetGame() ).AddLog( "SPIDERBOT HAS FINISHED ACTIVATING THE DEVICE ... " );
		GetDevicePS().CauseDistraction();
	}

	protected override function TurnOnDevice()
	{
		super.TurnOnDevice();
		if( m_uiComponent )
		{
			m_uiComponent.Toggle( true );
		}
		PlayGivenStation();
		UpdateDeviceState();
		GameObjectEffectHelper.ActivateEffectAction( this, gamedataFxActionType.Start, 'radio_idle' );
		RefreshUI();
	}

	protected override function TurnOffDevice()
	{
		super.TurnOffDevice();
		AudioSwitch( this, 'radio_station', 'station_none', 'radio' );
		UpdateDeviceState();
		GameObjectEffectHelper.ActivateEffectAction( this, gamedataFxActionType.BreakLoop, 'radio_idle' );
		RefreshUI();
	}

	protected override function CutPower()
	{
		super.CutPower();
		if( m_uiComponent )
		{
			m_uiComponent.Toggle( false );
		}
		GameObjectEffectHelper.ActivateEffectAction( this, gamedataFxActionType.BreakLoop, 'radio_idle' );
		UpdateDeviceState();
	}

	protected override function DeactivateDevice()
	{
		super.DeactivateDevice();
		if( m_uiComponent )
		{
			m_uiComponent.Toggle( false );
		}
		GameObjectEffectHelper.ActivateEffectAction( this, gamedataFxActionType.Kill, 'radio_idle' );
	}

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

	protected override function StartGlitching( glitchState : EGlitchState, optional intensity : Float )
	{
		var evt : AdvertGlitchEvent;
		if( intensity == 0.0 )
		{
			intensity = 1.0;
		}
		evt = new AdvertGlitchEvent;
		evt.SetShouldGlitch( intensity );
		QueueEvent( evt );
		UpdateDeviceState();
		PlaySound( this, GetDevicePS().GetGlitchSFX() );
	}

	protected override function StopGlitching()
	{
		var evt : AdvertGlitchEvent;
		evt = new AdvertGlitchEvent;
		evt.SetShouldGlitch( 0.0 );
		QueueEvent( evt );
	}

	private function StartShortGlitch()
	{
		var evt : StopShortGlitchEvent;
		if( GetDevicePS().IsGlitching() )
		{
			return;
		}
		if( !( m_isShortGlitchActive ) )
		{
			evt = new StopShortGlitchEvent;
			StartGlitching( EGlitchState.DEFAULT, 1.0 );
			m_shortGlitchDelayID = GameInstance.GetDelaySystem( GetGame() ).DelayEvent( this, evt, 0.25 );
			m_isShortGlitchActive = true;
		}
	}

	protected event OnStopShortGlitch( evt : StopShortGlitchEvent )
	{
		m_isShortGlitchActive = false;
		if( !( GetDevicePS().IsGlitching() ) )
		{
			StopGlitching();
		}
	}

	protected event OnQuickHackHighPitchNoise( evt : QuickHackHighPitchNoise )
	{
		if( evt.IsStarted() )
		{
			StartStunEffect();
		}
		else
		{
			StopStunEffect();
		}
	}

	protected event OnQuickHackAoeDamage( evt : QuickHackAoeDamage )
	{
		PlayAoeDamageSFX();
		PlayAoeDamageVFX();
		ApplyElectricDamage();
		Die();
	}

	private function ApplyElectricDamage()
	{
		var attack : Attack_GameEffect;
		attack = RPGManager.PrepareGameEffectAttack( GetGame(), this, this, m_damageType, GetAttackPosition() );
		if( attack )
		{
			attack.StartAttack();
		}
	}

	private function GetAttackPosition() : Vector4
	{
		return GetAcousticQuerryStartPoint();
	}

	private function StartStunEffect()
	{
		StopStunEffect();
		CreateGameEffect();
		PlayHighPitchNoiseSFX();
		PlayHighPitchNoiseVFX();
	}

	private function StopStunEffect()
	{
		TryTerminateEffectInstance();
		RemoveStatusEffectFromTargets();
		ClearTargets();
		TryStopVfx();
	}

	private function TryTerminateEffectInstance()
	{
		if( m_effectInstance )
		{
			m_effectInstance.Terminate();
			m_effectInstance = NULL;
		}
	}

	private function RemoveStatusEffectFromTargets()
	{
		var i : Int32;
		if( IsStatusEffectValid() )
		{
			for( i = 0; i < m_targets.Size(); i += 1 )
			{
				RemoveStatusEffect( m_targets[ i ] );
			}
		}
	}

	private function ClearTargets()
	{
		m_targets.Clear();
	}

	private function CreateGameEffect()
	{
		m_effectInstance = GameInstance.GetGameEffectSystem( GetGame() ).CreateEffect( m_effectRef, this );
		EffectData.SetFloat( m_effectInstance.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.radius, GetDevicePS().GetHighPitchNoiseRadius() );
		EffectData.SetVector( m_effectInstance.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, GetAttackPosition() );
		m_effectInstance.Run();
	}

	private function PlayHighPitchNoiseSFX()
	{
		PlaySound( this, GetDevicePS().GetHighPitchNoiseSFX() );
	}

	private function PlayAoeDamageSFX()
	{
		PlaySound( this, GetDevicePS().GetAoeDamageSFX() );
	}

	private function PlayHighPitchNoiseVFX()
	{
		PlayVfx( GetDevicePS().GetHighPitchNoiseVFX() );
	}

	private function PlayAoeDamageVFX()
	{
		PlayVfx( GetDevicePS().GetAoeDamageVFX() );
	}

	private function PlayVfx( fxResource : FxResource )
	{
		TryStopVfx();
		if( FxResource.IsValid( fxResource ) )
		{
			m_vfxInstance = CreateFxInstance( fxResource, GetCenterWorldTransform() );
		}
	}

	private function GetCenterWorldTransform() : WorldTransform
	{
		var transform : WorldTransform;
		var position : WorldPosition;
		WorldPosition.SetVector4( position, GetWorldPosition() );
		WorldTransform.SetWorldPosition( transform, position );
		return transform;
	}

	private function TryStopVfx()
	{
		if( m_vfxInstance )
		{
			m_vfxInstance.Kill();
		}
	}

	private function ApplyStatusEffect( target : weak< GameObject > )
	{
		if( IsStatusEffectValid() )
		{
			StatusEffectHelper.ApplyStatusEffect( target, m_statusEffect, GetEntityID() );
		}
	}

	private function RemoveStatusEffect( target : weak< GameObject > )
	{
		if( IsStatusEffectValid() )
		{
			StatusEffectHelper.RemoveStatusEffect( target, m_statusEffect );
		}
	}

	private function IsStatusEffectValid() : Bool
	{
		return TDBID.IsValid( m_statusEffect );
	}

	protected event OnTargetAcquired( evt : TargetAcquiredEvent )
	{
		TryAddToTargets( evt.target );
		ApplyStatusEffect( evt.target );
	}

	private function TryAddToTargets( target : weak< ScriptedPuppet > )
	{
		if( target && !( m_targets.Contains( target ) ) )
		{
			m_targets.PushBack( target );
		}
	}

	protected event OnTargetLost( evt : TargetLostEvent )
	{
		RemoveStatusEffect( evt.target );
		TryRemoveFromTargets( evt.target );
	}

	private function TryRemoveFromTargets( target : weak< ScriptedPuppet > )
	{
		var i : Int32;
		if( target )
		{
			i = m_targets.FindFirst( target );
			if( i >= 0 )
			{
				m_targets.EraseFast( i );
			}
		}
	}

}

