struct SChannelEnumData
{
	instanceeditable var channel : ETVChannel;
	default channel = ETVChannel.CH1;
}

enum ETVChannel
{
	CH1 = 0,
	CH2 = 1,
	CH3 = 2,
	CH4 = 3,
	CH5 = 4,
	INVALID = 5,
}

class TVController extends MediaDeviceController
{

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

}

struct TVResaveData
{
	var m_mediaResaveData : MediaResaveData;
	var m_channels : array< STvChannel >;
	var m_securedText : CName;
	var m_muteInterface : Bool;
	var m_useWhiteNoiseFX : Bool;
}

struct TVSetup
{
	mutable instanceeditable var m_channels : array< STvChannel >;
	instanceeditable var m_initialChannel : Int32;
	[ customEditor = "TweakDBGroupInheritance;ChannelData" ]
	instanceeditable var m_initialGlobalTvChannel : TweakDBID;
	instanceeditable var m_muteInterface : Bool;
	default m_muteInterface = false;
	instanceeditable var m_isInteractive : Bool;
	instanceeditable persistent var m_isGlobalTvOnly : Bool;
	default m_isGlobalTvOnly = false;
}

class TVControllerPS extends MediaDeviceControllerPS
{
	default m_deviceName = "LocKey#97";
	default m_tweakDBRecord = T"Devices.TV";
	default m_tweakDBDescriptionRecord = T"device_descriptions.TV";
	default m_hasAuthorizationModule = true;
	protected instanceeditable persistent var m_tvSetup : TVSetup;
	[ category = "Glitch" ]
	protected instanceeditable var m_defaultGlitchVideoPath : ResRef;
	[ category = "Glitch" ]
	protected instanceeditable var m_broadcastGlitchVideoPath : ResRef;
	private var m_globalTVInitialized : Bool;
	private var m_backupCustomChannels : array< STvChannel >;

	protected event OnInstantiated()
	{
		super.OnInstantiated();
		if( !( IsStringValid( m_deviceName ) ) )
		{
			m_deviceName = "Gameplay-Devices-DisplayNames-TV";
		}
	}

	protected override function GameAttached()
	{
		InitializeTv();
	}

	private function InitializeTv()
	{
		if( !( IsGlobalTvOnly() ) && HasBackupedCustomChannels() )
		{
			m_tvSetup.m_channels = m_backupCustomChannels;
		}
		InitializeGlobalTV();
		m_amountOfStations = m_tvSetup.m_channels.Size();
		m_activeChannelName = m_tvSetup.m_channels[ GetActiveStationIndex() ].channelName;
		SetInteractionState( m_tvSetup.m_isInteractive );
	}

	public const function IsInterfaceMuted() : Bool
	{
		return m_tvSetup.m_muteInterface;
	}

	public function SetInterfaceMuted( mute : Bool )
	{
		m_tvSetup.m_muteInterface = mute;
	}

	public function SetIsInteractive( isInteractive : Bool )
	{
		m_tvSetup.m_isInteractive = isInteractive;
	}

	public const function GetDefaultGlitchVideoPath() : ResRef
	{
		if( ResRef.IsValid( m_defaultGlitchVideoPath ) )
		{
			return m_defaultGlitchVideoPath;
		}
		else
		{
			return R"base\movies\misc\generic_noise_white.bk2";
		}
	}

	public const function GetBroadcastGlitchVideoPath() : ResRef
	{
		if( ResRef.IsValid( m_broadcastGlitchVideoPath ) )
		{
			return m_broadcastGlitchVideoPath;
		}
		else
		{
			return R"base\movies\misc\distraction_generic.bk2";
		}
	}

	public const function HasCustomChannels() : Bool
	{
		var i : Int32;
		for( i = 0; i < m_tvSetup.m_channels.Size(); i += 1 )
		{
			if( !( TDBID.IsValid( m_tvSetup.m_channels[ i ].channelTweakID ) ) )
			{
				return true;
			}
		}
		return false;
	}

	public function SetIsGlobalTvOnly( isGlobalTv : Bool )
	{
		if( isGlobalTv != m_tvSetup.m_isGlobalTvOnly )
		{
			m_tvSetup.m_isGlobalTvOnly = isGlobalTv;
			m_globalTVInitialized = false;
			m_dataInitialized = false;
			InitializeTv();
		}
	}

	public const function IsGlobalTvOnly() : Bool
	{
		return m_tvSetup.m_isGlobalTvOnly;
	}

	public const function GetGlobalTVChannels() : array< weak< ChannelData_Record > >
	{
		var tvRecord : weak< TVBase_Record >;
		var channels : array< weak< ChannelData_Record > >;
		tvRecord = TweakDBInterface.GetTVBaseRecord( m_tweakDBRecord );
		if( tvRecord )
		{
			tvRecord.Channels( channels );
		}
		return channels;
	}

	public const function GetAmmountOfGlobalTVChannels() : Int32
	{
		var channels : array< weak< ChannelData_Record > >;
		channels = GetGlobalTVChannels();
		return channels.Size();
	}

	private function InitializeGlobalTV() : Int32
	{
		var i : Int32;
		var channels : array< weak< ChannelData_Record > >;
		var channelData : STvChannel;
		if( IsGlobalTvOnly() )
		{
			BackupCustomChannels();
			m_tvSetup.m_channels.Clear();
		}
		if( !( TDBID.IsValid( m_tweakDBRecord ) ) )
		{
			InitializeRPGParams();
		}
		channels = GetGlobalTVChannels();
		if( m_globalTVInitialized )
		{
			return channels.Size();
		}
		for( i = 0; i < channels.Size(); i += 1 )
		{
			channelData.channelName = channels[ i ].LocalizedName();
			channelData.channelTweakID = channels[ i ].GetID();
			m_tvSetup.m_channels.PushBack( channelData );
		}
		m_globalTVInitialized = true;
		return channels.Size();
	}

	private function BackupCustomChannels()
	{
		var i : Int32;
		var currentChannel : STvChannel;
		for( i = 0; i < m_tvSetup.m_channels.Size(); i += 1 )
		{
			if( !( TDBID.IsValid( m_tvSetup.m_channels[ i ].channelTweakID ) ) )
			{
				currentChannel = m_tvSetup.m_channels[ i ];
				m_backupCustomChannels.PushBack( currentChannel );
			}
		}
	}

	private const function HasBackupedCustomChannels() : Bool
	{
		return m_backupCustomChannels.Size() > 0;
	}

	protected const override function CanCreateAnyQuickHackActions() : Bool
	{
		return true;
	}

	protected override function GetQuickHackActions( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		var currentAction : ScriptableDeviceAction;
		if( GlitchScreen.IsDefaultConditionMet( this, context ) )
		{
			currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenSuicide", T"QuickHack.SuicideHackBase" );
			currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
			outActions.PushBack( currentAction );
			currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenBlind", T"QuickHack.BlindHack" );
			currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
			outActions.PushBack( currentAction );
			currentAction = ActionGlitchScreen( T"DeviceAction.GlitchScreenGrenade", T"QuickHack.GrenadeHackBase" );
			currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
			outActions.PushBack( currentAction );
		}
		if( !( GlitchScreen.IsDefaultConditionMet( this, context ) ) )
		{
			SetActionsInactiveAll( outActions, "LocKey#7003" );
		}
		currentAction = ActionQuickHackDistraction();
		currentAction.SetObjectActionID( T"DeviceAction.MalfunctionClassHack" );
		currentAction.SetDurationValue( GetDistractionDuration( currentAction ) );
		currentAction.SetInactiveWithReason( QuickHackDistraction.IsDefaultConditionMet( this, context ), "LocKey#7003" );
		outActions.PushBack( currentAction );
		if( IsGlitching() || IsDistracting() )
		{
			SetActionsInactiveAll( outActions, "LocKey#7004" );
		}
		FinalizeGetQuickHackActions( outActions, context );
	}

	public override function GetQuestActionByName( actionName : CName ) : DeviceAction
	{
		var action : DeviceAction;
		action = super.GetQuestActionByName( actionName );
		if( action == NULL )
		{
			switch( actionName )
			{
				case 'QuestMuteSounds':
					action = ActionQuestMuteSounds( true );
				break;
				case 'QuestUnMuteSounds':
					action = ActionQuestMuteSounds( false );
				break;
				case 'QuestEnableInteractivity':
					action = ActionQuestToggleInteractivity( true );
				break;
				case 'QuestDisableInteractivity':
					action = ActionQuestToggleInteractivity( false );
				break;
			}
		}
		return action;
	}

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

	public virtual function OnQuestDefaultStation( evt : QuestDefaultStation ) : EntityNotificationType
	{
		m_activeStation = EstablishInitialActiveChannelIndex();
		m_activeChannelName = m_tvSetup.m_channels[ m_activeStation ].channelName;
		UseNotifier( evt );
		return EntityNotificationType.SendThisEventToEntity;
	}

	public override function GetActiveStationIndex() : Int32
	{
		if( !( m_dataInitialized ) )
		{
			m_dataInitialized = true;
			m_previousStation = -1;
			m_activeStation = EstablishInitialActiveChannelIndex();
		}
		return m_activeStation;
	}

	private function EstablishInitialActiveChannelIndex() : Int32
	{
		var idx : Int32;
		var globalTVChannelsCount : Int32;
		if( !( m_globalTVInitialized ) )
		{
			InitializeGlobalTV();
		}
		if( TDBID.IsValid( m_tvSetup.m_initialGlobalTvChannel ) )
		{
			idx = GetGlobalTVChannelIDX( m_tvSetup.m_initialGlobalTvChannel );
		}
		else if( HasCustomChannels() )
		{
			idx = m_tvSetup.m_initialChannel;
		}
		else
		{
			globalTVChannelsCount = GetAmmountOfGlobalTVChannels();
			if( globalTVChannelsCount > 0 )
			{
				idx = RandRange( 0, globalTVChannelsCount );
			}
		}
		return idx;
	}

	public const function GetGlobalTVChannelIDX( id : TweakDBID ) : Int32
	{
		var i : Int32;
		for( i = 0; i < m_tvSetup.m_channels.Size(); i += 1 )
		{
			if( m_tvSetup.m_channels[ i ].channelTweakID == id )
			{
				return i;
			}
		}
		return -1;
	}

	private const function GetActiveChannelVideoPath() : ResRef
	{
		var channelData : STvChannel;
		channelData = GetChannelData( m_activeStation );
		return channelData.videoPath;
	}

	private const function GetActiveChannelTweakDBID() : TweakDBID
	{
		var channelData : STvChannel;
		channelData = GetChannelData( m_activeStation );
		return channelData.channelTweakID;
	}

	private const function GlobalTVChannelIDToEnum( id : TweakDBID ) : ETVChannel
	{
		var channel : ETVChannel;
		switch( id )
		{
			case T"Channels.CH1":
				channel = ETVChannel.CH1;
			break;
			case T"Channels.CH2":
				channel = ETVChannel.CH2;
			break;
			case T"Channels.CH3":
				channel = ETVChannel.CH3;
			break;
			case T"Channels.CH4":
				channel = ETVChannel.CH4;
			break;
			case T"Channels.CH5":
				channel = ETVChannel.CH5;
			break;
			default:
				channel = ETVChannel.INVALID;
			break;
		}
		return channel;
	}

	private const function GlobalTVChannelIDToInt( id : TweakDBID ) : Int32
	{
		var channel : Int32;
		switch( id )
		{
			case T"Channels.CH1":
				channel = 1;
			break;
			case T"Channels.CH2":
				channel = 2;
			break;
			case T"Channels.CH3":
				channel = 3;
			break;
			case T"Channels.CH4":
				channel = 4;
			break;
			case T"Channels.CH5":
				channel = 5;
			break;
			default:
				channel = -1;
			break;
		}
		return channel;
	}

	public const function GetChannelName( index : Int32 ) : String
	{
		if( index < 0 )
		{
			return "";
		}
		return m_tvSetup.m_channels[ index ].channelName;
	}

	public const function GetChannelID( const channelName : ref< String > ) : Int32
	{
		var i : Int32;
		for( i = 0; i < m_tvSetup.m_channels.Size(); i += 1 )
		{
			if( m_tvSetup.m_channels[ i ].channelName == channelName )
			{
				return i;
			}
		}
		return -1;
	}

	public const function GetChannelData( channelIndex : Int32 ) : STvChannel
	{
		var invalidChannel : STvChannel;
		if( ( channelIndex < 0 ) || ( channelIndex > ( m_tvSetup.m_channels.Size() - 1 ) ) )
		{
			return invalidChannel;
		}
		return m_tvSetup.m_channels[ channelIndex ];
	}

	protected override function DetermineGameplayViability( const context : ref< GetActionsContext >, hasActiveActions : Bool ) : Bool
	{
		return TVViabilityInterpreter.Evaluate( this, hasActiveActions );
	}

	public override function GetDeviceIconPath() : String
	{
		return "base/gameplay/gui/brushes/devices/icon_tv.widgetbrush";
	}

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

	public override function GetDeviceWidget( const context : ref< GetActionsContext > ) : SDeviceWidgetPackage
	{
		var widgetData : SDeviceWidgetPackage;
		var customData : TvDeviceWidgetCustomData;
		var channel : STvChannel;
		widgetData = super.GetDeviceWidget( context );
		if( IsON() )
		{
			channel = GetChannelData( m_activeStation );
			customData = new TvDeviceWidgetCustomData;
			customData.videoPath = GetActiveChannelVideoPath();
			customData.channelID = GetActiveChannelTweakDBID();
			customData.looped = channel.looped;
			customData.messageRecordID = channel.m_messageRecordID;
			widgetData.customData = customData;
			widgetData.textData = GetDeviceStatusTextData();
			widgetData.deviceStatus = "LocKey#42211";
		}
		return widgetData;
	}

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

	public const override function IsInteractive() : Bool
	{
		return m_tvSetup.m_isInteractive && super.IsInteractive();
	}

	public export function PushResaveData( data : TVResaveData ) {}

	protected override function GetDeviceIconTweakDBID() : TweakDBID
	{
		return T"DeviceIcons.ScreenDeviceIcon";
	}

	protected override function GetBackgroundTextureTweakDBID() : TweakDBID
	{
		return T"DeviceIcons.ScreenDeviceBackground";
	}

	protected function ActionQuestMuteSounds( mute : Bool ) : QuestMuteSounds
	{
		var action : QuestMuteSounds;
		action = new QuestMuteSounds;
		action.SetUp( this );
		action.SetProperties( mute );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public virtual function OnQuestMuteSounds( evt : QuestMuteSounds ) : EntityNotificationType
	{
		SetInterfaceMuted( ( ( Bool )evt.prop.first ) );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	protected function ActionQuestToggleInteractivity( enable : Bool ) : QuestToggleInteractivity
	{
		var action : QuestToggleInteractivity;
		action = new QuestToggleInteractivity;
		action.SetUp( this );
		action.SetProperties( enable );
		action.AddDeviceName( m_deviceName );
		return action;
	}

	public virtual function OnQuestToggleInteractivity( evt : QuestToggleInteractivity ) : EntityNotificationType
	{
		SetIsInteractive( ( ( Bool )evt.prop.first ) );
		UseNotifier( evt );
		if( !( IsFinal() ) )
		{
		}
		return EntityNotificationType.SendThisEventToEntity;
	}

	public quest const function IsGlobalTVChannelActive( channel : Int32 ) : Bool
	{
		var channelID : TweakDBID;
		var activeChannel : Int32;
		channelID = GetActiveChannelTweakDBID();
		if( !( TDBID.IsValid( channelID ) ) )
		{
			return false;
		}
		else
		{
			activeChannel = GlobalTVChannelIDToInt( channelID );
			return activeChannel == channel;
		}
	}

}

