struct STvChannel
{
	instanceeditable var channelName : String;
	default channelName = "1";
	instanceeditable var videoPath : ResRef;
	[ customEditor = "TweakDBGroupInheritance;ScreenMessageData" ]
	instanceeditable editable var m_messageRecordID : TweakDBID;
	[ customEditor = "AudioEvent" ]
	instanceeditable var audioEvent : CName;
	instanceeditable var looped : Bool;
	default looped = true;
	const instanceeditable var m_sequence : array< SequenceVideo >;
	var channelTweakID : TweakDBID;
}

struct SequenceVideo
{
	instanceeditable var videoPath : ResRef;
	[ customEditor = "AudioEvent" ]
	instanceeditable var audioEvent : CName;
	instanceeditable var looped : Bool;
	default looped = false;
}

class SetGlobalTvChannel extends Event
{
	[ customEditor = "TweakDBGroupInheritance;ChannelData" ]
	editable var m_channel : TweakDBID;

	public constexpr export function GetFriendlyDescription() : String
	{
		return "Set Global TV Channel";
	}

}

class SetGlobalTvOnly extends Event
{
	editable var m_isGlobalTvOnly : Bool;

	public constexpr function GetFriendlyDescription() : String
	{
		return "Set Global TV Only";
	}

}

class TV extends InteractiveDevice
{
	default m_controllerTypeName = 'TVController';
	private var m_isShortGlitchActive : Bool;
	private var m_shortGlitchDelayID : DelayID;
	protected editable var m_isTVMoving : Bool;

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

	public export override function ResavePersistentData( ps : PersistentState ) : Bool
	{
		return true;
	}

	protected export override function IsDeviceMovableScript() : Bool
	{
		return m_isTVMoving;
	}

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

	protected override function ResolveGameplayState()
	{
		super.ResolveGameplayState();
		if( GetDevicePS().IsInterfaceMuted() )
		{
			ToggleSoundEmmiter( true );
		}
		if( !( GetDevicePS().IsInteractive() ) )
		{
			ToggleDirectLayer( false );
		}
		if( IsUIdirty() && m_isInsideLogicArea )
		{
			RefreshUI();
		}
	}

	public const override function IsReadyForUI() : Bool
	{
		return m_isVisible || GetDevicePS().ForceResolveGameplayStateOnAttach();
	}

	protected event OnDetach()
	{
		super.OnDetach();
		GameInstance.GetGlobalTVSystem( GetGame() ).UnregisterTVChannelFromEntity( this );
	}

	protected override function CreateBlackboard()
	{
		m_blackboard = IBlackboard.Create( GetAllBlackboardDefs().TVDeviceBlackboard );
	}

	public const override function GetBlackboardDef() : TVDeviceBlackboardDef
	{
		return GetDevicePS().GetBlackboardDef();
	}

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

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

	public const function IsInteractive() : Bool
	{
		return GetDevicePS().IsInteractive();
	}

	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 event OnNextChannel( evt : NextStation )
	{
		var currentIndex : Int32;
		currentIndex = GetDevicePS().GetActiveStationIndex();
		SelectChannel( currentIndex );
		GetDevicePS().PassChannelName( GetChannelName( currentIndex ) );
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

	protected event OnPreviousChannel( evt : PreviousStation )
	{
		var currentIndex : Int32;
		currentIndex = GetDevicePS().GetActiveStationIndex();
		SelectChannel( currentIndex );
		GetDevicePS().PassChannelName( GetChannelName( currentIndex ) );
		TriggerArreaEffectDistraction( GetDefaultDistractionAreaEffectData(), evt.GetExecutor() );
	}

	protected event OnQuestSetChannel( evt : QuestSetChannel )
	{
		SelectChannel( GetDevicePS().GetActiveStationIndex() );
	}

	protected event OnQuestDefaultStation( evt : QuestDefaultStation )
	{
		SelectChannel( ( ( TVControllerPS )( GetDevicePS() ) ).GetActiveStationIndex() );
	}

	protected event OnQuestSetGlobalChannel( evt : SetGlobalTvChannel )
	{
		var idx : Int32;
		idx = GetDevicePS().GetGlobalTVChannelIDX( evt.m_channel );
		if( idx >= 0 )
		{
			GetDevicePS().SetActiveStationIndex( idx );
			SelectChannel( idx );
		}
	}

	protected event OnQuestSetGlobalTvOnly( evt : SetGlobalTvOnly )
	{
		GetDevicePS().SetIsGlobalTvOnly( evt.m_isGlobalTvOnly );
		SelectChannel( GetDevicePS().GetActiveStationIndex() );
	}

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

	public const function GetBroadcastGlitchVideoPath() : ResRef
	{
		return GetDevicePS().GetBroadcastGlitchVideoPath();
	}

	protected override function StartGlitching( glitchState : EGlitchState, optional intensity : Float )
	{
		var glitchData : GlitchData;
		var evt : AdvertGlitchEvent;
		glitchData.state = glitchState;
		glitchData.intensity = intensity;
		if( intensity == 0.0 )
		{
			intensity = 1.0;
		}
		evt = new AdvertGlitchEvent;
		evt.SetShouldGlitch( intensity );
		QueueEvent( evt );
		GetBlackboard().SetVariant( GetAllBlackboardDefs().TVDeviceBlackboard.GlitchData, glitchData );
		GetBlackboard().FireCallbacks();
		PlaySound( this, 'dev_screen_glitch_distraction' );
	}

	protected override function StopGlitching()
	{
		var glitchData : GlitchData;
		var evt : AdvertGlitchEvent;
		evt = new AdvertGlitchEvent;
		evt.SetShouldGlitch( 0.0 );
		QueueEvent( evt );
		glitchData.state = EGlitchState.NONE;
		GetBlackboard().SetVariant( GetAllBlackboardDefs().TVDeviceBlackboard.GlitchData, glitchData );
		GetBlackboard().FireCallbacks();
		StopSound( this, 'dev_screen_glitch_distraction' );
	}

	protected event OnHitEvent( hit : gameHitEvent )
	{
		super.OnHitEvent( hit );
		StartShortGlitch();
	}

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

	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 override function TurnOnDevice()
	{
		super.TurnOnDevice();
		TurnOnScreen();
	}

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

	private function ToggleSoundEmmiter( mute : Bool )
	{
		if( mute )
		{
			GameObject.PlaySound( this, 'mute_tv_emitter' );
		}
		else
		{
			GameObject.PlaySound( this, 'unmute_tv_emitter' );
		}
	}

	private function SelectChannel( currentChannelIDX : Int32 )
	{
		GetBlackboard().SetInt( GetAllBlackboardDefs().TVDeviceBlackboard.CurrentChannel, currentChannelIDX );
		GetBlackboard().FireCallbacks();
	}

	private const function GetChannelName( index : Int32 ) : String
	{
		return GetDevicePS().GetChannelName( index );
	}

	private function SelectChannel( const channelName : ref< String > )
	{
		var channelId : Int32;
		channelId = GetChannelId( channelName );
		GetBlackboard().SetInt( GetAllBlackboardDefs().TVDeviceBlackboard.CurrentChannel, channelId );
	}

	private function GetChannelId( const channelName : ref< String > ) : Int32
	{
		return GetDevicePS().GetChannelID( channelName );
	}

	public const function GetChannelData( channelIDX : Int32 ) : STvChannel
	{
		return GetDevicePS().GetChannelData( channelIDX );
	}

	protected virtual function TurnOffScreen()
	{
		m_uiComponent.Toggle( false );
		GameInstance.GetGlobalTVSystem( GetGame() ).UnregisterTVChannelFromEntity( this );
		ToggleSoundEmmiter( true );
		RefreshUI();
	}

	protected virtual function TurnOnScreen()
	{
		m_uiComponent.Toggle( true );
		if( !( GetDevicePS().IsInterfaceMuted() ) )
		{
			ToggleSoundEmmiter( false );
		}
		RefreshUI();
	}

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

	protected override function ApplyActiveStatusEffect( target : EntityID, statusEffect : TweakDBID )
	{
		if( IsActiveStatusEffectValid() && GetDevicePS().IsGlitching() )
		{
			GameInstance.GetStatusEffectSystem( GetGame() ).ApplyStatusEffect( target, statusEffect );
		}
	}

	protected override function UploadActiveProgramOnNPC( targetID : EntityID )
	{
		var evt : ExecutePuppetActionEvent;
		if( IsActiveProgramToUploadOnNPCValid() && GetDevicePS().IsGlitching() )
		{
			evt = new ExecutePuppetActionEvent;
			evt.actionID = GetActiveProgramToUploadOnNPC();
			QueueEventForEntityID( targetID, evt );
		}
	}

	protected event OnQuestMuteSounds( evt : QuestMuteSounds )
	{
		ToggleSoundEmmiter( GetDevicePS().IsInterfaceMuted() );
	}

	protected event OnQuestToggleInteractivity( evt : QuestToggleInteractivity ) {}

	public const function GetGlobalTVChannels() : array< weak< ChannelData_Record > >
	{
		return GetDevicePS().GetGlobalTVChannels();
	}

}

