class ChangeSubwayGateStateEvent extends Event
{
	var open : Bool;
}

class DataTerm extends InteractiveDevice
{
	default m_controllerTypeName = 'DataTermController';
	[ category = "Fast Travel" ]
	private instanceeditable inlined var m_linkedFastTravelPoint : FastTravelPointData;
	[ category = "Fast Travel" ]
	private instanceeditable var m_exitNode : NodeRef;
	[ category = "Subway" ]
	private instanceeditable var m_metroAnimationNode : NodeRef;
	[ category = "Subway" ]
	private instanceeditable var m_SubwayGateBroken : Bool;
	private var m_fastTravelComponent : FastTravelComponent;
	private var m_lockColiderComponent : IPlacedComponent;
	private var m_mappinID : NewMappinID;
	private var m_isShortGlitchActive : Bool;
	private var m_shortGlitchDelayID : DelayID;
	private var m_hologramMeshGreen : IPlacedComponent;
	private var m_hologramMeshRed : IPlacedComponent;

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		EntityRequestComponentsInterface.RequestComponent( ri, 'dataTerm_ui', 'worlduiWidgetComponent', true );
		EntityRequestComponentsInterface.RequestComponent( ri, 'fastTravel', 'FastTravelComponent', true );
		EntityRequestComponentsInterface.RequestComponent( ri, 'lock', 'IPlacedComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'gate_holo_green', 'IPlacedComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'gate_holo_red', 'IPlacedComponent', false );
		super.OnRequestComponents( ri );
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		m_uiComponent = ( ( worlduiWidgetComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'dataTerm_ui' ) ) );
		m_fastTravelComponent = ( ( FastTravelComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'fastTravel' ) ) );
		m_lockColiderComponent = ( ( IPlacedComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'lock' ) ) );
		m_hologramMeshGreen = ( ( IPlacedComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'gate_holo_green' ) ) );
		m_hologramMeshRed = ( ( IPlacedComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'gate_holo_red' ) ) );
		super.OnTakeControl( ri );
		m_controller = ( ( DataTermController )( EntityResolveComponentsInterface.GetComponent( ri, 'controller' ) ) );
	}

	protected override function ResolveGameplayState()
	{
		super.ResolveGameplayState();
		( ( DataTermControllerPS )( GetDevicePS() ) ).SetLinkedFastTravelPoint( m_linkedFastTravelPoint );
		ResolveGateApperance();
		RegisterFastTravelPoints();
	}

	protected event OnGameAttached()
	{
		super.OnGameAttached();
		if( GetFastTravelSystem().HasFastTravelPoint( m_linkedFastTravelPoint ) )
		{
			RegisterMappin();
		}
		ProcessSubwayGateNPCPresence( false );
	}

	protected event OnDetach()
	{
		super.OnDetach();
		UnregisterMappin();
	}

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

	protected const override function IsReadyForUI() : Bool
	{
		return m_isVisible || GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate;
	}

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

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

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

	protected event OnLogicReady( evt : SetLogicReadyEvent )
	{
		super.OnLogicReady( evt );
		if( ( IsUIdirty() && m_isInsideLogicArea ) || GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate )
		{
			RefreshUI();
		}
	}

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

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

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

	protected override function DeactivateDevice()
	{
		super.DeactivateDevice();
		ToggleLogicLayer( false );
		UnregisterMappin();
	}

	protected override function ActivateDevice()
	{
		super.ActivateDevice();
		ToggleLogicLayer( true );
	}

	protected function TurnOffScreen()
	{
		var operationsContainer : DeviceOperationsContainer;
		if( m_uiComponent )
		{
			if( GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate )
			{
				return;
			}
			m_uiComponent.Toggle( false );
		}
		operationsContainer = GetDevicePS().GetDeviceOperationsContainer();
		if( operationsContainer )
		{
			operationsContainer.Execute( 'hide_holo', this );
		}
	}

	protected function TurnOnScreen()
	{
		var operationsContainer : DeviceOperationsContainer;
		if( m_uiComponent )
		{
			m_uiComponent.Toggle( true );
		}
		operationsContainer = GetDevicePS().GetDeviceOperationsContainer();
		if( operationsContainer )
		{
			operationsContainer.Execute( 'show_holo', this );
		}
	}

	protected event OnInteractionActivated( evt : InteractionActivationEvent )
	{
		super.OnInteractionActivated( evt );
		if( evt.eventType == gameinteractionsEInteractionEventType.EIET_activate )
		{
			if( evt.layerData.tag == 'LogicArea' )
			{
				RegisterFastTravelPoints();
			}
		}
	}

	protected event OnAreaEnter( evt : AreaEnteredEvent )
	{
		var activator : GameObject;
		if( evt.componentName == 'fastTravelArea' )
		{
			if( GetDevicePS().GetFastravelTriggerType() != EFastTravelTriggerType.Auto )
			{
				return false;
			}
			if( ( m_linkedFastTravelPoint == NULL ) || ( m_linkedFastTravelPoint && !( m_linkedFastTravelPoint.IsValid() ) ) )
			{
				return false;
			}
			if( GetFastTravelSystem().IsFastTravelEnabledOnMap() )
			{
				return false;
			}
			activator = ( ( GameObject )( EntityGameInterface.GetEntity( evt.activator ) ) );
			if( activator.IsPlayer() )
			{
				RequestFastTravelMenu();
				TeleportToExitNode( activator );
			}
		}
		else
		{
			if( evt.componentName == 'SubwayGateArea' && GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate )
			{
				activator = ( ( GameObject )( EntityGameInterface.GetEntity( evt.activator ) ) );
				if( ( ( ScriptedPuppet )( activator ) ).IsCrowd() )
				{
					ProcessSubwayGateNPCPresence( true );
				}
				else
				{
					if( activator.IsPlayer() && GetDeviceState() == EDeviceStatus.ON )
					{
						if( ( m_linkedFastTravelPoint == NULL ) || ( m_linkedFastTravelPoint && !( m_linkedFastTravelPoint.IsValid() ) ) )
						{
							return false;
						}
						if( GetFastTravelSystem().IsFastTravelEnabledOnMap() )
						{
							return false;
						}
						SetFactValue( GetGame(), 'ue_metro_display_options', 1 );
						SendThisSubwayGateToFastTravelSystem();
						PrepareMetroAnimEntityPosition();
					}
				}
			}
		}
	}

	private function SendThisSubwayGateToFastTravelSystem()
	{
		var blackboard : IBlackboard;
		blackboard = GameInstance.GetBlackboardSystem( GetGame() ).Get( GetAllBlackboardDefs().FastTRavelSystem );
		if( blackboard != NULL )
		{
			blackboard.SetEntityID( GetAllBlackboardDefs().FastTRavelSystem.LastSubwayGateUsed, GetEntityID() );
		}
	}

	protected event OnAreaExit( evt : AreaExitedEvent )
	{
		var activator : GameObject;
		if( evt.componentName == 'SubwayGateArea' )
		{
			activator = ( ( GameObject )( EntityGameInterface.GetEntity( evt.activator ) ) );
			if( ( ( ScriptedPuppet )( activator ) ).IsCivilian() || ( ( ScriptedPuppet )( activator ) ).IsCrowd() )
			{
				ProcessSubwayGateNPCPresence( false );
			}
			else
			{
				if( activator.IsPlayer() )
				{
					SetFactValue( GetGame(), 'ue_metro_display_options', 0 );
				}
			}
		}
	}

	protected event OnChangeSubwayGateStateEvent( evt : ChangeSubwayGateStateEvent )
	{
		if( evt.open == true )
		{
			OpenSubwayGate();
		}
		else
		{
			CloseSubwayGate();
		}
	}

	public function OpenSubwayGate()
	{
		if( GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate && !( IsSubwayGateOpen() ) )
		{
			GetDevicePS().GetDeviceOperationsContainer().Execute( 'UnlockGate', this );
			GetBlackboard().SetBool( GetAllBlackboardDefs().DataTermDeviceBlackboard.subwayGateOpen, true, true );
		}
	}

	public function CloseSubwayGate()
	{
		if( GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate && IsSubwayGateOpen() )
		{
			GetDevicePS().GetDeviceOperationsContainer().Execute( 'LockGate', this );
			GetBlackboard().SetBool( GetAllBlackboardDefs().DataTermDeviceBlackboard.subwayGateOpen, false, true );
		}
	}

	private function ProcessSubwayGateNPCPresence( increase : Bool )
	{
		var NPCsPresentNumber : Int32;
		NPCsPresentNumber = GetBlackboard().GetInt( GetAllBlackboardDefs().DataTermDeviceBlackboard.passengerCount );
		if( increase )
		{
			NPCsPresentNumber += 1;
		}
		else
		{
			NPCsPresentNumber -= 1;
		}
		if( NPCsPresentNumber <= 0 )
		{
			NPCsPresentNumber = 0;
			if( IsSubwayGateOpen() )
			{
				GetDevicePS().GetDeviceOperationsContainer().Execute( 'LockGate', this );
				GetBlackboard().SetBool( GetAllBlackboardDefs().DataTermDeviceBlackboard.subwayGateOpen, false, true );
			}
		}
		else
		{
			if( !( IsSubwayGateOpen() ) )
			{
				GetDevicePS().GetDeviceOperationsContainer().Execute( 'UnlockGate', this );
				GetBlackboard().SetBool( GetAllBlackboardDefs().DataTermDeviceBlackboard.subwayGateOpen, true, true );
			}
		}
		GetBlackboard().SetInt( GetAllBlackboardDefs().DataTermDeviceBlackboard.passengerCount, NPCsPresentNumber, true );
	}

	private function PrepareMetroAnimEntityPosition() : Bool
	{
		var position : Vector4;
		var rotation : Quaternion;
		var entityID : EntityID;
		entityID = ( ( EntityID )( ResolveNodeRefWithEntityID( m_metroAnimationNode, GetEntityID() ) ) );
		if( EntityID.IsDefined( entityID ) == true )
		{
			position = GetWorldPosition();
			rotation = GetWorldOrientation();
			GameInstance.GetTeleportationFacility( GetGame() ).Teleport( ( ( GameObject )( GameInstance.FindEntityByID( GetGame(), entityID ) ) ), position, Quaternion.ToEulerAngles( rotation ) );
			return true;
		}
		return false;
	}

	public const function IsSubwayGateBroken() : Bool
	{
		return m_SubwayGateBroken;
	}

	private const function IsSubwayGateOpen() : Bool
	{
		return GetBlackboard().GetBool( GetAllBlackboardDefs().DataTermDeviceBlackboard.subwayGateOpen );
	}

	private const function AreCharacterPresentAtSubwayGate() : Bool
	{
		return GetBlackboard().GetInt( GetAllBlackboardDefs().DataTermDeviceBlackboard.passengerCount ) > 0;
	}

	private function TeleportToExitNode( activator : GameObject )
	{
		var globalRef : GlobalNodeRef;
		var position : Vector4;
		var nodeTransform : Transform;
		var rotation : EulerAngles;
		globalRef = ResolveNodeRefWithEntityID( m_exitNode, GetEntityID() );
		if( GlobalNodeRef.IsDefined( globalRef ) )
		{
			GameInstance.GetNodeTransform( GetGame(), globalRef, nodeTransform );
			position = Transform.GetPosition( nodeTransform );
			rotation = Quaternion.ToEulerAngles( Transform.GetOrientation( nodeTransform ) );
			GameInstance.GetTeleportationFacility( GetGame() ).Teleport( activator, position, rotation );
		}
		else
		{
			GameInstance.GetTeleportationFacility( GetGame() ).TeleportToNode( activator, m_linkedFastTravelPoint.GetMarkerRef() );
		}
	}

	public const function GetFastravelDeviceType() : EFastTravelDeviceType
	{
		return GetDevicePS().GetFastravelDeviceType();
	}

	private function ResolveGateApperance()
	{
		var deviceState : EDeviceStatus;
		if( GetFastravelDeviceType() == EFastTravelDeviceType.SubwayGate )
		{
			if( !( GetFastTravelSystem().IsFastTravelEnabled() ) )
			{
				if( m_lockColiderComponent && GetDevicePS().GetFastravelTriggerType() == EFastTravelTriggerType.Auto )
				{
					m_lockColiderComponent.Toggle( true );
				}
			}
			else
			{
				if( m_lockColiderComponent && GetDevicePS().GetFastravelTriggerType() == EFastTravelTriggerType.Auto )
				{
					m_lockColiderComponent.Toggle( false );
				}
			}
			deviceState = GetDevicePS().GetDeviceState();
			if( deviceState == EDeviceStatus.ON )
			{
				m_hologramMeshGreen.Toggle( true );
				m_hologramMeshRed.Toggle( false );
			}
			else
			{
				m_hologramMeshGreen.Toggle( false );
				m_hologramMeshRed.Toggle( true );
			}
		}
	}

	private function IsMappinRegistered() : Bool
	{
		var invalidID : NewMappinID;
		return m_mappinID != invalidID;
	}

	private function RegisterMappin()
	{
		var mappinData : MappinData;
		if( GetDevicePS().IsDisabled() )
		{
			return;
		}
		if( !( m_linkedFastTravelPoint.ShouldShowMappinInWorld() ) )
		{
			return;
		}
		if( !( IsMappinRegistered() ) )
		{
			mappinData.mappinType = T"Mappins.FastTravelDynamicMappin";
			if( m_linkedFastTravelPoint.IsSubway() )
			{
				mappinData.variant = gamedataMappinVariant.Zzz17_NCARTVariant;
			}
			else
			{
				mappinData.variant = gamedataMappinVariant.FastTravelVariant;
			}
			mappinData.visibleThroughWalls = false;
			m_mappinID = GetMappinSystem().RegisterMappinWithObject( mappinData, this, 'poi_mappin' );
		}
	}

	private function UnregisterMappin()
	{
		var invalidID : NewMappinID;
		if( !( m_linkedFastTravelPoint.ShouldShowMappinInWorld() ) )
		{
			return;
		}
		if( IsMappinRegistered() )
		{
			GetMappinSystem().UnregisterMappin( m_mappinID );
			m_mappinID = invalidID;
		}
	}

	private function GetMappinSystem() : MappinSystem
	{
		return GameInstance.GetMappinSystem( GetGame() );
	}

	private function RegisterFastTravelPoints()
	{
		var evt : RegisterFastTravelPointsEvent;
		if( GetDevicePS().IsDisabled() )
		{
			return;
		}
		evt = new RegisterFastTravelPointsEvent;
		evt.fastTravelNodes.PushBack( m_linkedFastTravelPoint );
		QueueEvent( evt );
		RegisterMappin();
	}

	protected event OnFastTravelPointsUpdated( evt : FastTravelPointsUpdated )
	{
		var invalidID : NewMappinID;
		if( evt.updateTrackingAlternative && m_linkedFastTravelPoint.mappinID != invalidID )
		{
			GetMappinSystem().SetMappinTrackingAlternative( m_linkedFastTravelPoint.mappinID, m_mappinID );
		}
		GetBlackboard().SetVariant( GetAllBlackboardDefs().DataTermDeviceBlackboard.fastTravelPoint, m_linkedFastTravelPoint, true );
		DetermineInteractionStateByTask();
		RefreshUI();
		ResolveGateApperance();
	}

	protected event OnOpenWorldMapAction( evt : OpenWorldMapDeviceAction )
	{
		RequestFastTravelMenu();
	}

	private function RequestFastTravelMenu()
	{
		UpdateFastTravelPointRecord();
		GameInstance.GetUISystem( GetGame() ).RequestFastTravelMenu();
	}

	private function UpdateFastTravelPointRecord()
	{
		var request : UpdateFastTravelPointRecordRequest;
		request = new UpdateFastTravelPointRecordRequest;
		if( m_linkedFastTravelPoint != NULL )
		{
			request.markerRef = m_linkedFastTravelPoint.GetMarkerRef();
		}
		GetFastTravelSystem().QueueRequest( request );
	}

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

	public const override function IsGameplayRoleValid( role : EGameplayRole ) : Bool
	{
		if( !( super.IsGameplayRoleValid( role ) ) )
		{
			return false;
		}
		else if( ( m_linkedFastTravelPoint != NULL ) && m_linkedFastTravelPoint.IsValid() )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

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

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

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

	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();
		}
	}

	public const function GetFastravelPointData() : FastTravelPointData
	{
		return m_linkedFastTravelPoint;
	}

}

