class QuestMappinController extends BaseQuestMappinController
{
	protected editable var m_arrowCanvas : inkWidgetRef;
	protected editable var m_arrowPart : inkWidgetRef;
	protected editable var m_selector : inkWidgetRef;
	protected editable var m_scanningDiamond : inkWidgetRef;
	protected editable var m_portalIcon : inkWidgetRef;
	private var m_aboveWidget : weak< inkWidget >;
	private var m_belowWidget : weak< inkWidget >;
	protected var m_mappin : weak< IMappin >;
	protected var m_questMappin : weak< QuestMappin >;
	protected var m_runtimeMappin : weak< RuntimeMappin >;
	protected var m_root : weak< inkCompoundWidget >;
	protected var m_isMainQuest : Bool;
	protected var m_shouldHideWhenClamped : Bool;
	default m_shouldHideWhenClamped = false;
	protected var m_isCompletedPhase : Bool;
	default m_isCompletedPhase = false;
	protected var m_animProxy : inkAnimProxy;
	protected var m_animOptions : inkAnimOptions;
	private var m_vehicleAlreadySummonedTime : EngineTime;
	private var m_vehiclePulseTimeSecs : Float;
	private var m_vehicleMappinComponent : VehicleMappinComponent;

	protected event OnInitialize()
	{
		m_root = ( ( inkCompoundWidget )( GetRootWidget() ) );
		m_animOptions.playReversed = false;
		m_animOptions.executionDelay = 0.0;
		m_animOptions.loopType = inkanimLoopType.Cycle;
		m_animOptions.loopInfinite = true;
		m_aboveWidget = ( ( inkWidget )( GetWidget( 'Canvas/above' ) ) );
		m_belowWidget = ( ( inkWidget )( GetWidget( 'Canvas/below' ) ) );
		inkWidgetRef.SetVisible( m_selector, false );
		inkWidgetRef.SetVisible( m_portalIcon, false );
	}

	protected event OnUninitialize()
	{
		if( m_vehicleMappinComponent )
		{
			m_vehicleMappinComponent.OnUnitialize();
		}
	}

	protected event OnIntro()
	{
		var mappinVariant : gamedataMappinVariant;
		var vehicleMappin : weak< VehicleMappin >;
		m_mappin = GetMappin();
		m_questMappin = ( ( QuestMappin )( m_mappin ) );
		m_runtimeMappin = ( ( RuntimeMappin )( m_mappin ) );
		mappinVariant = m_mappin.GetVariant();
		m_isMainQuest = mappinVariant == gamedataMappinVariant.DefaultQuestVariant;
		if( inkTextRef.IsValid( distanceText ) )
		{
			inkTextRef.SetVisible( distanceText, ShouldShowDistance() );
		}
		if( inkTextRef.IsValid( displayName ) )
		{
			inkTextRef.SetVisible( displayName, ShouldShowDisplayName() );
			inkTextRef.SetLetterCase( displayName, textLetterCase.UpperCase );
			inkTextRef.SetText( displayName, m_mappin.GetDisplayName() );
		}
		inkImageRef.SetVisible( iconWidget, true );
		vehicleMappin = ( ( weak< weak< VehicleMappin > > )( m_mappin ) );
		if( vehicleMappin )
		{
			m_vehicleMappinComponent = new VehicleMappinComponent;
			m_vehicleMappinComponent.OnInitialize( this, vehicleMappin );
		}
		OnUpdate();
	}

	protected event OnUpdate()
	{
		UpdateVisibility();
		inkWidgetRef.SetVisible( m_arrowPart, isCurrentlyClamped );
		if( inkWidgetRef.IsValid( m_portalIcon ) )
		{
			inkWidgetRef.SetVisible( m_portalIcon, IsGPSPortal() );
		}
		UpdateDistanceText();
		UpdateDisplayName();
		UpdateAboveBelowVerticalRelation();
		UpdateIcon();
		UpdateRootState();
		UpdateTrackedState();
	}

	protected function UpdateAboveBelowVerticalRelation()
	{
		var vertRelation : gamemappinsVerticalPositioning;
		var distance : Float;
		var shouldShowVertRelation : Bool;
		var isAbove, isBelow : Bool;
		var animPlayer : animationPlayer;
		if( ( m_aboveWidget == NULL ) && ( m_belowWidget == NULL ) )
		{
			return;
		}
		vertRelation = GetVerticalRelationToPlayer();
		distance = GetDistanceToPlayer();
		shouldShowVertRelation = ( ( ( GetRootWidget().IsVisible() && !( isCurrentlyClamped ) ) && !( nameplateVisible ) ) && ( distance >= MappinUtils.GetGlobalProfile().VerticalRelationVisibleRangeMin() ) ) && ( distance <= MappinUtils.GetGlobalProfile().VerticalRelationVisibleRangeMax() );
		isAbove = shouldShowVertRelation && vertRelation == gamemappinsVerticalPositioning.Above;
		isBelow = shouldShowVertRelation && vertRelation == gamemappinsVerticalPositioning.Below;
		m_aboveWidget.SetVisible( isAbove );
		m_belowWidget.SetVisible( isBelow );
		animPlayer = GetAnimPlayer_AboveBelow();
		if( animPlayer != NULL )
		{
			animPlayer.PlayOrStop( isAbove || isBelow );
		}
	}

	protected event OnNameplate( isNameplateVisible : Bool, nameplateController : weak< NpcNameplateGameController > )
	{
		nameplateVisible = isNameplateVisible;
		if( isNameplateVisible )
		{
			OverrideScaleByDistance( false );
			SetProjectToScreenSpace( false );
		}
		else
		{
			OverrideScaleByDistance( true );
			SetProjectToScreenSpace( true );
		}
		OnUpdate();
	}

	public function OnVehicleAreadySummoned()
	{
		var animOptions : inkAnimOptions;
		animOptions.loopType = inkanimLoopType.Cycle;
		animOptions.loopInfinite = false;
		animOptions.loopCounter = 3;
		PlayLibraryAnimation( 'blink', animOptions );
	}

	private export function SetShouldHideWhenClamped( flag : Bool )
	{
		m_shouldHideWhenClamped = flag;
		UpdateVisibility();
	}

	private virtual function UpdateVisibility()
	{
		var isInQuestArea : Bool;
		var showWhenClamped : Bool;
		var shouldBeVisible : Bool;
		isInQuestArea = ( m_questMappin != NULL ) && m_questMappin.IsInsideTrigger();
		showWhenClamped = ( ( isCurrentlyClamped ) ? ( !( m_shouldHideWhenClamped ) ) : ( true ) );
		shouldBeVisible = ( m_mappin.IsVisible() && showWhenClamped ) && !( isInQuestArea );
		SetRootVisible( shouldBeVisible );
	}

	protected virtual function UpdateIcon()
	{
		var mappinVariant : gamedataMappinVariant;
		var mappinPhase : gamedataMappinPhase;
		var texturePart : CName;
		var interactionMappin : InteractionMappin;
		var opacity, scale : Float;
		var grenadeData : GrenadeMappinData;
		mappinVariant = m_mappin.GetVariant();
		mappinPhase = m_mappin.GetPhase();
		interactionMappin = ( ( InteractionMappin )( m_mappin ) );
		if( mappinVariant == gamedataMappinVariant.GrenadeVariant )
		{
			grenadeData = ( ( GrenadeMappinData )( m_mappin.GetScriptData() ) );
			if( grenadeData && TDBID.IsValid( grenadeData.m_iconID ) )
			{
				SetTexture( iconWidget, grenadeData.m_iconID );
			}
			else
			{
				texturePart = MappinUIUtils.MappinToTexturePart( mappinVariant, mappinPhase );
				inkImageRef.SetTexturePart( iconWidget, texturePart );
			}
		}
		else if( mappinVariant == gamedataMappinVariant.Zzz10_RemoteControlDrivingVariant )
		{
			inkImageRef.SetTexturePart( iconWidget, MappinUIUtils.MappinToTexturePart( mappinVariant, mappinPhase ) );
		}
		else if( interactionMappin )
		{
			SetTexture( iconWidget, interactionMappin.GetIconRecordID() );
		}
		else
		{
			texturePart = MappinUIUtils.MappinToTexturePart( mappinVariant, mappinPhase );
			inkImageRef.SetTexturePart( iconWidget, texturePart );
		}
		m_isCompletedPhase = mappinPhase == gamedataMappinPhase.CompletedPhase;
		opacity = ( ( m_isCompletedPhase ) ? ( MappinUtils.GetGlobalProfile().CompletedPOIOpacity() ) : ( 1.0 ) );
		scale = ( ( IsGPSPortal() ) ? ( MappinUtils.GetGlobalProfile().GpsPortalIconScale() ) : ( 1.0 ) );
		inkImageRef.SetOpacity( iconWidget, opacity );
		inkImageRef.SetScale( iconWidget, Vector2( scale, scale ) );
	}

	private override function ComputeRootState() : CName
	{
		var mappinsGroup : weak< MappinsGroup_Record >;
		var stateName : CName;
		var grenadeData : GrenadeMappinData;
		var grenadeType : EGrenadeType;
		var variant : gamedataMappinVariant;
		if( m_isCompletedPhase )
		{
			stateName = 'QuestComplete';
		}
		else if( m_mappin != NULL )
		{
			variant = m_mappin.GetVariant();
			if( m_mappin.IsExactlyA( 'gamemappinsGrenadeMappin' ) )
			{
				grenadeData = ( ( GrenadeMappinData )( m_mappin.GetScriptData() ) );
				grenadeType = grenadeData.m_grenadeType;
				switch( grenadeType )
				{
					case EGrenadeType.Frag:
						stateName = 'FragGrenade';
					break;
					case EGrenadeType.Flash:
						stateName = 'FlashGrenade';
					break;
					case EGrenadeType.Piercing:
						stateName = 'PiercingGrenade';
					break;
					case EGrenadeType.EMP:
						stateName = 'EMPGrenade';
					break;
					case EGrenadeType.Biohazard:
						stateName = 'BiohazardGrenade';
					break;
					case EGrenadeType.Incendiary:
						stateName = 'IncendiaryGrenade';
					break;
					case EGrenadeType.Recon:
						stateName = 'ReconGrenade';
					break;
					case EGrenadeType.Cutting:
						stateName = 'CuttingGrenade';
					break;
					case EGrenadeType.Sonic:
						stateName = 'SonicGrenade';
					break;
					default:
						stateName = 'FragGrenade';
					break;
				}
			}
			else if( m_mappin.IsExactlyA( 'gamemappinsInteractionMappin' ) )
			{
				stateName = ( ( m_mappin.IsQuestImportant() ) ? ( 'Quest' ) : ( 'InteractionDefault' ) );
			}
			else if( variant == gamedataMappinVariant.Zzz10_RemoteControlDrivingVariant )
			{
				stateName = 'RemoteControlDriving';
			}
			else if( variant == gamedataMappinVariant.Zzz17_NCARTVariant )
			{
				stateName = 'FastTravelMetro';
			}
			else if( variant == gamedataMappinVariant.Zzz16_RelicDeviceBasicVariant )
			{
				stateName = 'Relic';
			}
			else
			{
				mappinsGroup = MappinUtils.GetMappinsGroup( m_mappin.GetVariant() );
				if( mappinsGroup )
				{
					stateName = mappinsGroup.WidgetState();
				}
			}
		}
		if( stateName == '' )
		{
			stateName = 'Quest';
		}
		return stateName;
	}

	protected function IsTagged() : Bool
	{
		return GetVisualData() && GetVisualData().m_isTagged;
	}

	protected function IsQuest() : Bool
	{
		return GetVisualData() && GetVisualData().m_isQuest;
	}

	protected function IsVisibleThruWalls() : Bool
	{
		return GetVisualData() && GetVisualData().m_visibleThroughWalls;
	}

	protected function GetMappinVisualState() : EMappinVisualState
	{
		return ( ( GetVisualData() ) ? ( GetVisualData().m_mappinVisualState ) : ( EMappinVisualState.Default ) );
	}

	protected function GetQuality() : gamedataQuality
	{
		return ( ( GetVisualData() ) ? ( GetVisualData().m_quality ) : ( gamedataQuality.Invalid ) );
	}

	protected function IsIconic() : Bool
	{
		return GetVisualData().m_isIconic;
	}

	protected function IsBroken() : Bool
	{
		return GetVisualData().m_isBroken;
	}

	protected function IsShardRead() : Bool
	{
		return GetVisualData() && GetVisualData().m_isShardRead;
	}

	public const override function GetVisualData() : GameplayRoleMappinData
	{
		var dat : GameplayRoleMappinData;
		if( m_mappin )
		{
			dat = ( ( GameplayRoleMappinData )( m_mappin.GetScriptData() ) );
		}
		return dat;
	}

	protected function GetMappinVarient() : gamedataMappinVariant
	{
		return m_mappin.GetVariant();
	}

}

class QuestAnimationMappinController extends BaseQuestMappinController
{
	private var m_mappin : weak< QuestMappin >;
	private var m_animationRecord : UIAnimation_Record;
	private var m_animProxy : inkAnimProxy;
	private var m_playing : Bool;
	default m_playing = false;

	protected event OnInitialize()
	{
		SetRootVisible( m_playing );
	}

	protected event OnIntro()
	{
		m_mappin = ( ( QuestMappin )( GetMappin() ) );
		m_animationRecord = TweakDBInterface.GetUIAnimationRecord( m_mappin.GetUIAnimationRecordID() );
		OnUpdate();
	}

	protected event OnUpdate()
	{
		var animOptions : inkAnimOptions;
		var isVisible : Bool;
		isVisible = m_mappin.IsVisible();
		if( m_playing != isVisible )
		{
			m_playing = isVisible;
			if( m_animProxy )
			{
				m_animProxy.Stop();
			}
			if( m_playing )
			{
				if( m_animationRecord.Loop() )
				{
					animOptions.loopType = inkanimLoopType.Cycle;
					animOptions.loopInfinite = true;
				}
				m_animProxy = PlayLibraryAnimation( m_animationRecord.AnimationName(), animOptions );
			}
			SetRootVisible( m_playing );
		}
	}

}

class VehicleMappinComponent
{
	private var m_questMappinController : weak< QuestMappinController >;
	private var m_vehicleMappin : weak< VehicleMappin >;
	private var m_vehicle : weak< VehicleObject >;
	private var m_vehicleEntityID : EntityID;
	private var m_playerMounted : Bool;
	default m_playerMounted = false;
	private var m_vehicleEnRoute : Bool;
	default m_vehicleEnRoute = false;
	private var m_scheduleDiscreteModeDelayID : DelayID;
	private var m_invalidDelayID : DelayID;
	private var m_init : Bool;
	default m_init = false;
	private var m_vehicleSummonDataDef : VehicleSummonDataDef;
	private var m_vehicleSummonDataBB : weak< IBlackboard >;
	private var m_vehicleSummonStateCallback : CallbackHandle;
	private var m_uiActiveVehicleDataDef : UI_ActiveVehicleDataDef;
	private var m_uiActiveVehicleDataBB : weak< IBlackboard >;
	private var m_vehPlayerStateDataCallback : CallbackHandle;

	public function OnInitialize( questMappinController : weak< QuestMappinController >, vehicleMappin : weak< VehicleMappin > )
	{
		m_questMappinController = questMappinController;
		m_vehicleMappin = vehicleMappin;
		m_vehicle = m_vehicleMappin.GetVehicle();
		m_vehicleEntityID = m_vehicle.GetEntityID();
		m_vehicleSummonDataDef = GetAllBlackboardDefs().VehicleSummonData;
		m_vehicleSummonDataBB = GameInstance.GetBlackboardSystem( m_vehicle.GetGame() ).Get( m_vehicleSummonDataDef );
		m_vehicleSummonStateCallback = m_vehicleSummonDataBB.RegisterListenerUint( m_vehicleSummonDataDef.SummonState, this, 'OnVehicleSummonStateChanged' );
		m_uiActiveVehicleDataDef = GetAllBlackboardDefs().UI_ActiveVehicleData;
		m_uiActiveVehicleDataBB = GameInstance.GetBlackboardSystem( m_vehicle.GetGame() ).Get( m_uiActiveVehicleDataDef );
		m_vehPlayerStateDataCallback = m_uiActiveVehicleDataBB.RegisterListenerVariant( m_uiActiveVehicleDataDef.VehPlayerStateData, this, 'OnActiveVechicleDataChanged' );
		OnActiveVechicleDataChanged( m_uiActiveVehicleDataBB.GetVariant( m_uiActiveVehicleDataDef.VehPlayerStateData ) );
		OnVehicleSummonStateChanged( m_vehicleSummonDataBB.GetUint( m_vehicleSummonDataDef.SummonState ) );
	}

	public function OnUnitialize()
	{
		m_vehicleSummonDataBB.UnregisterListenerUint( m_vehicleSummonDataDef.SummonState, m_vehicleSummonStateCallback );
		m_uiActiveVehicleDataBB.UnregisterListenerVariant( m_uiActiveVehicleDataDef.VehPlayerStateData, m_vehPlayerStateDataCallback );
	}

	private function VehicleIsLatestSummoned() : Bool
	{
		return m_vehicleEntityID == m_vehicleSummonDataBB.GetEntityID( m_vehicleSummonDataDef.SummonedVehicleEntityID );
	}

	private function SetActive( active : Bool )
	{
		m_vehicleMappin.SetActive( active );
		TryScheduleDiscreteMode();
	}

	private function TryScheduleDiscreteMode()
	{
		var vehicleDelayCallback : VehicleMappinDelayedDiscreteModeCallback;
		SetDiscreteMode( false );
		if( m_scheduleDiscreteModeDelayID != m_invalidDelayID )
		{
			GameInstance.GetDelaySystem( m_vehicle.GetGame() ).CancelCallback( m_scheduleDiscreteModeDelayID );
		}
		if( !( m_vehicleEnRoute ) )
		{
			vehicleDelayCallback = new VehicleMappinDelayedDiscreteModeCallback;
			vehicleDelayCallback.m_vehicleMappinComponent = this;
			m_scheduleDiscreteModeDelayID = GameInstance.GetDelaySystem( m_vehicle.GetGame() ).DelayCallback( vehicleDelayCallback, 10.0 );
		}
	}

	public function SetDiscreteMode( discrete : Bool )
	{
		m_questMappinController.OverrideClamp( !( discrete ) );
		m_vehicleMappin.EnableVisibilityThroughWalls( !( discrete ) );
	}

	protected event OnVehicleSummonStateChanged( value : Uint32 )
	{
		var summonState : vehicleSummonState;
		summonState = ( ( vehicleSummonState )( value ) );
		if( m_vehicleMappin.IsActive() && ( !( m_init ) || VehicleIsLatestSummoned() ) )
		{
			if( m_vehicleEnRoute && summonState != vehicleSummonState.AlreadySummoned )
			{
				m_vehicleEnRoute = false;
			}
			else if( summonState == vehicleSummonState.EnRoute )
			{
				m_vehicleEnRoute = true;
			}
			if( summonState == vehicleSummonState.AlreadySummoned )
			{
				m_questMappinController.OnVehicleAreadySummoned();
			}
			SetActive( true );
		}
		m_init = true;
	}

	protected event OnActiveVechicleDataChanged( vehPlayerStateData : Variant )
	{
		var vehData : VehEntityPlayerStateData;
		var playerMounted : Bool;
		vehData = ( ( VehEntityPlayerStateData )vehPlayerStateData );
		if( m_vehicleEntityID == vehData.entID )
		{
			playerMounted = vehData.state > 0;
			if( m_playerMounted != playerMounted )
			{
				m_playerMounted = playerMounted;
				SetActive( !( m_playerMounted ) );
			}
		}
	}

}

class VehicleMappinDelayedDiscreteModeCallback extends DelayCallback
{
	var m_vehicleMappinComponent : weak< VehicleMappinComponent >;

	public override function Call()
	{
		if( m_vehicleMappinComponent )
		{
			m_vehicleMappinComponent.SetDiscreteMode( true );
		}
	}

}

