class vehicleUIGameController extends inkHUDGameController
{
	private var m_vehicleBlackboard : weak< IBlackboard >;
	private var m_vehicle : weak< VehicleObject >;
	private var m_vehiclePS : VehicleComponentPS;
	private var m_vehicleBBStateConectionId : CallbackHandle;
	private var m_vehicleCollisionBBStateID : CallbackHandle;
	private var m_vehicleBBUIActivId : CallbackHandle;
	private var m_rootWidget : weak< inkWidget >;
	private var m_UIEnabled : Bool;
	private var m_startAnimProxy : inkAnimProxy;
	private var m_loopAnimProxy : inkAnimProxy;
	private var m_endAnimProxy : inkAnimProxy;
	private var m_loopingBootProxy : inkAnimProxy;
	private editable var m_speedometerWidget : inkWidgetRef;
	private editable var m_tachometerWidget : inkWidgetRef;
	private editable var m_timeWidget : inkWidgetRef;
	private editable var m_instruments : inkWidgetRef;
	private editable var m_gearBox : inkWidgetRef;
	private editable var m_radio : inkWidgetRef;
	private editable var m_analogTachWidget : inkWidgetRef;
	private editable var m_analogSpeedWidget : inkWidgetRef;
	private var m_isVehicleReady : Bool;

	private function SetupModule( widget : inkWidgetRef, vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard > )
	{
		var moduleController : weak< IVehicleModuleController >;
		if( !( inkWidgetRef.IsValid( widget ) ) )
		{
			return;
		}
		moduleController = ( ( IVehicleModuleController )( inkWidgetRef.GetController( widget ) ) );
		if( moduleController == NULL )
		{
			return;
		}
		moduleController.RegisterCallbacks( vehicle, vehBB, this );
	}

	private function UnregisterModule( widget : inkWidgetRef )
	{
		var moduleController : weak< IVehicleModuleController >;
		if( !( inkWidgetRef.IsValid( widget ) ) )
		{
			return;
		}
		moduleController = ( ( IVehicleModuleController )( inkWidgetRef.GetController( widget ) ) );
		if( moduleController == NULL )
		{
			return;
		}
		moduleController.UnregisterCallbacks();
	}

	protected event OnInitialize()
	{
		m_vehicle = ( ( VehicleObject )( GetOwnerEntity() ) );
		m_vehiclePS = m_vehicle.GetVehiclePS();
		m_rootWidget = GetRootWidget();
		m_vehicleBlackboard = m_vehicle.GetBlackboard();
		if( IsUIactive() )
		{
			ActivateUI();
		}
		if( m_vehicleBlackboard )
		{
			if( !( m_vehicleBBUIActivId ) )
			{
				m_vehicleBBUIActivId = m_vehicleBlackboard.RegisterListenerBool( GetAllBlackboardDefs().Vehicle.IsUIActive, this, 'OnActivateUI' );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehicleBBUIActivId )
		{
			m_vehicleBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().Vehicle.IsUIActive, m_vehicleBBUIActivId );
		}
		UnregisterBlackBoardCallbacks();
	}

	private function ActivateUI()
	{
		m_rootWidget.SetVisible( true );
		RegisterBlackBoardCallbacks();
		CheckIfVehicleShouldTurnOn();
	}

	private function DeactivateUI()
	{
		UnregisterBlackBoardCallbacks();
		m_rootWidget.SetVisible( false );
	}

	protected event OnActivateUI( activate : Bool )
	{
		var evt : VehicleUIactivateEvent;
		evt = new VehicleUIactivateEvent;
		if( activate )
		{
			evt.m_activate = true;
		}
		else
		{
			evt.m_activate = false;
		}
		QueueEvent( evt );
	}

	protected event OnActivateUIEvent( evt : VehicleUIactivateEvent )
	{
		if( evt.m_activate )
		{
			ActivateUI();
		}
		else
		{
			DeactivateUI();
		}
	}

	protected event OnVehicleReady( ready : Bool )
	{
		if( ready )
		{
			m_rootWidget.SetVisible( true );
		}
		else if( !( ready ) )
		{
			m_rootWidget.SetVisible( false );
		}
		m_isVehicleReady = ready;
	}

	private function RegisterBlackBoardCallbacks()
	{
		if( m_vehicleBlackboard )
		{
			SetupModule( m_speedometerWidget, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_tachometerWidget, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_timeWidget, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_instruments, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_gearBox, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_radio, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_analogTachWidget, m_vehicle, m_vehicleBlackboard );
			SetupModule( m_analogSpeedWidget, m_vehicle, m_vehicleBlackboard );
			if( !( m_vehicleBBStateConectionId ) )
			{
				m_vehicleBBStateConectionId = m_vehicleBlackboard.RegisterListenerInt( GetAllBlackboardDefs().Vehicle.VehicleState, this, 'OnVehicleStateChanged' );
			}
			if( !( m_vehicleCollisionBBStateID ) )
			{
				m_vehicleCollisionBBStateID = m_vehicleBlackboard.RegisterListenerBool( GetAllBlackboardDefs().Vehicle.Collision, this, 'OnVehicleCollision' );
			}
			InitializeWidgetStyleSheet( m_vehicle );
		}
	}

	private function UnregisterBlackBoardCallbacks()
	{
		if( m_vehicleBlackboard )
		{
			UnregisterModule( m_speedometerWidget );
			UnregisterModule( m_tachometerWidget );
			UnregisterModule( m_timeWidget );
			UnregisterModule( m_instruments );
			UnregisterModule( m_gearBox );
			UnregisterModule( m_radio );
			UnregisterModule( m_analogTachWidget );
			UnregisterModule( m_analogSpeedWidget );
			if( m_vehicleBBStateConectionId )
			{
				m_vehicleBlackboard.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.VehicleState, m_vehicleBBStateConectionId );
			}
			if( m_vehicleCollisionBBStateID )
			{
				m_vehicleBlackboard.UnregisterListenerBool( GetAllBlackboardDefs().Vehicle.Collision, m_vehicleCollisionBBStateID );
			}
		}
	}

	private function IsUIactive() : Bool
	{
		if( m_vehicleBlackboard && m_vehicleBlackboard.GetBool( GetAllBlackboardDefs().Vehicle.IsUIActive ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	private function InitializeWidgetStyleSheet( veh : weak< VehicleObject > )
	{
		var record : weak< Vehicle_Record >;
		var styleSheetPath : ResRef;
		record = veh.GetRecord();
		styleSheetPath = record.WidgetStyleSheetPath();
		m_rootWidget.SetStyle( styleSheetPath );
	}

	private function CheckIfVehicleShouldTurnOn()
	{
		if( m_vehiclePS.GetIsUiQuestModified() )
		{
			if( m_vehiclePS.GetUiQuestState() )
			{
				TurnOn();
			}
			return;
		}
		if( m_vehicleBlackboard.GetInt( GetAllBlackboardDefs().Vehicle.VehicleState ) == ( ( Int32 )( vehicleEState.On ) ) )
		{
			TurnOn();
		}
	}

	protected event OnVehicleStateChanged( state : Int32 )
	{
		if( m_vehiclePS.GetIsUiQuestModified() )
		{
			return false;
		}
		if( state == ( ( Int32 )( vehicleEState.On ) ) )
		{
			TurnOn();
		}
		if( state == ( ( Int32 )( vehicleEState.Default ) ) )
		{
			TurnOff();
		}
		if( state == ( ( Int32 )( vehicleEState.Disabled ) ) )
		{
			TurnOff();
		}
		if( state == ( ( Int32 )( vehicleEState.Destroyed ) ) )
		{
			TurnOff();
		}
	}

	private function TurnOn()
	{
		KillBootupProxy();
		if( m_UIEnabled )
		{
			PlayIdleLoop();
		}
		else
		{
			m_UIEnabled = true;
			m_startAnimProxy = PlayLibraryAnimation( 'start' );
			m_startAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnStartAnimFinished' );
			EvaluateWidgetStyle( GameInstance.GetTimeSystem( m_vehicle.GetGame() ).GetGameTime() );
		}
	}

	private function TurnOff()
	{
		m_UIEnabled = false;
		KillBootupProxy();
		if( m_startAnimProxy )
		{
			m_startAnimProxy.Stop();
		}
		if( m_loopAnimProxy )
		{
			m_loopAnimProxy.Stop();
		}
		m_endAnimProxy = PlayLibraryAnimation( 'end' );
		m_endAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnEndAnimFinished' );
	}

	protected event OnStartAnimFinished( anim : inkAnimProxy )
	{
		PlayIdleLoop();
	}

	private function PlayIdleLoop()
	{
		var animOptions : inkAnimOptions;
		animOptions.loopType = inkanimLoopType.Cycle;
		animOptions.loopInfinite = true;
		m_loopAnimProxy = PlayLibraryAnimation( 'loop', animOptions );
	}

	protected event OnEndAnimFinished( anim : inkAnimProxy )
	{
		m_rootWidget.SetState( 'inactive' );
	}

	private function PlayLibraryAnim( animName : CName )
	{
		PlayLibraryAnimation( animName );
	}

	public function EvaluateWidgetStyle( time : GameTime )
	{
		var sunSet : GameTime;
		var sunRise : GameTime;
		var currTime : GameTime;
		if( m_UIEnabled )
		{
			sunSet = GameTime.MakeGameTime( 0, 20, 0, 0 );
			sunRise = GameTime.MakeGameTime( 0, 5, 0, 0 );
			currTime = GameTime.MakeGameTime( 0, GameTime.Hours( time ), GameTime.Minutes( time ), GameTime.Seconds( time ) );
			if( ( currTime <= sunSet ) && ( currTime >= sunRise ) )
			{
				if( m_rootWidget.GetState() != 'day' )
				{
					m_rootWidget.SetState( 'day' );
				}
			}
			else
			{
				if( m_rootWidget.GetState() != 'night' )
				{
					m_rootWidget.SetState( 'night' );
				}
			}
		}
	}

	protected event OnVehicleCollision( collision : Bool )
	{
		PlayLibraryAnimation( 'glitch' );
	}

	protected event OnForwardVehicleQuestEnableUIEvent( evt : ForwardVehicleQuestEnableUIEvent )
	{
		switch( evt.mode )
		{
			case vehicleQuestUIEnable.Gameplay:
				CheckIfVehicleShouldTurnOn();
			break;
			case vehicleQuestUIEnable.ForceEnable:
				TurnOn();
			break;
			case vehicleQuestUIEnable.ForceDisable:
				TurnOff();
			break;
		}
	}

	protected event OnVehiclePanzerBootupUIQuestEvent( evt : VehiclePanzerBootupUIQuestEvent )
	{
		var animOptions : inkAnimOptions;
		m_UIEnabled = true;
		m_rootWidget.SetVisible( true );
		animOptions.loopType = inkanimLoopType.Cycle;
		animOptions.loopInfinite = true;
		switch( evt.mode )
		{
			case panzerBootupUI.UnbootedIdle:
				KillBootupProxy();
			m_loopingBootProxy = PlayLibraryAnimation( '1_unbooted_idle', animOptions );
			break;
			case panzerBootupUI.BootingAttempt:
				KillBootupProxy();
			m_loopingBootProxy = PlayLibraryAnimation( '2_booting_attempt', animOptions );
			break;
			case panzerBootupUI.BootingSuccess:
				KillBootupProxy();
			m_loopingBootProxy = PlayLibraryAnimation( '3_booting_success' );
			break;
			case panzerBootupUI.Loop:
				KillBootupProxy();
			m_loopingBootProxy = PlayLibraryAnimation( 'loop', animOptions );
			break;
		}
	}

	private function KillBootupProxy()
	{
		if( m_loopingBootProxy )
		{
			m_loopingBootProxy.Stop();
		}
	}

	protected event OnForwardVehicleQuestUIEffectEvent( evt : ForwardVehicleQuestUIEffectEvent )
	{
		if( evt.glitch )
		{
			PlayLibraryAnimation( 'glitch' );
		}
		if( evt.panamVehicleStartup )
		{
			PlayLibraryAnimation( 'start_panam' );
		}
		if( evt.panamScreenType1 )
		{
			PlayLibraryAnimation( 'panam_screen_type1' );
		}
		if( evt.panamScreenType2 )
		{
			PlayLibraryAnimation( 'panam_screen_type2' );
		}
		if( evt.panamScreenType3 )
		{
			PlayLibraryAnimation( 'panam_screen_type3' );
		}
		if( evt.panamScreenType4 )
		{
			PlayLibraryAnimation( 'panam_screen_type4' );
		}
	}

}

class IVehicleModuleController extends inkLogicController
{

	public virtual function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > ) {}

	public virtual function UnregisterCallbacks() {}
}

class speedometerLogicController extends IVehicleModuleController
{
	private editable var m_speedTextWidget : inkTextRef;
	private var m_speedBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_vehicle : weak< VehicleObject >;
	private var m_currentSpeed : Int32;
	default m_currentSpeed = -1;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_speedBBConnectionId ) )
			{
				m_speedBBConnectionId = vehBB.RegisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, this, 'OnSpeedValueChanged' );
				m_vehBB = vehBB;
				m_vehicle = vehicle;
			}
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_speedBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, m_speedBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_speedBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, m_speedBBConnectionId );
			}
		}
		m_currentSpeed = -1;
	}

	public function OnSpeedValueChanged( speed : Float )
	{
		var velocity : Float;
		var multiplier : Float;
		var speedValue : Int32;
		velocity = AbsF( speed );
		multiplier = GameInstance.GetStatsDataSystem( m_vehicle.GetGame() ).GetValueFromCurve( 'vehicle_ui', velocity, 'speed_to_multiplier' );
		speedValue = RoundMath( velocity * multiplier );
		if( speedValue != m_currentSpeed )
		{
			inkTextRef.SetText( m_speedTextWidget, IntToString( speedValue ) );
			m_currentSpeed = speedValue;
		}
	}

}

class tachometerLogicController extends IVehicleModuleController
{
	private editable var m_rpmValueWidget : inkTextRef;
	private editable var m_rpmGaugeForegroundWidget : inkRectangleRef;
	private editable var m_scaleX : Bool;
	private var m_rpmValueBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_rpmGaugeMaxSize : Vector2;
	private var m_rpmMaxValue : Float;
	private var m_rpmMinValue : Float;
	private var m_currentRPM : Int32;
	default m_currentRPM = -1;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		var record : weak< Vehicle_Record >;
		var vehEngineData : weak< VehicleEngineData_Record >;
		if( vehBB )
		{
			if( !( m_rpmValueBBConnectionId ) )
			{
				m_rpmValueBBConnectionId = vehBB.RegisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, this, 'OnRpmValueChanged' );
				m_rpmGaugeMaxSize = inkRectangleRef.GetSize( m_rpmGaugeForegroundWidget );
				record = vehicle.GetRecord();
				vehEngineData = record.VehEngineData();
				m_rpmMinValue = vehEngineData.MinRPM();
				m_rpmMaxValue = vehEngineData.MaxRPM();
				m_vehBB = vehBB;
			}
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_rpmValueBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, m_rpmValueBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		m_currentRPM = -1;
		if( m_vehBB )
		{
			if( m_rpmValueBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, m_rpmValueBBConnectionId );
			}
		}
	}

	public function SetupRPMDefaultState()
	{
		m_rpmMaxValue = m_rpmMaxValue + 1500.0;
		if( m_scaleX )
		{
			inkRectangleRef.SetSize( m_rpmGaugeForegroundWidget, Vector2( ( ( Float )( ( m_rpmMinValue * ( ( Float )( m_rpmGaugeMaxSize.X ) ) ) ) ) / m_rpmMaxValue, ( ( Float )( m_rpmGaugeMaxSize.Y ) ) ) );
		}
		else
		{
			inkRectangleRef.SetSize( m_rpmGaugeForegroundWidget, Vector2( ( ( Float )( ( ( Float )( m_rpmGaugeMaxSize.X ) ) ) ), ( m_rpmMinValue * ( ( Float )( m_rpmGaugeMaxSize.Y ) ) ) / m_rpmMaxValue ) );
		}
	}

	public function OnRpmValueChanged( rpmValue : Float )
	{
		var newRPM : Int32;
		newRPM = RoundMath( rpmValue );
		if( newRPM == m_currentRPM )
		{
			return;
		}
		if( m_scaleX )
		{
			inkRectangleRef.SetSize( m_rpmGaugeForegroundWidget, Vector2( ( ( Float )( ( rpmValue * ( ( Float )( m_rpmGaugeMaxSize.X ) ) ) ) ) / m_rpmMaxValue, ( ( Float )( m_rpmGaugeMaxSize.Y ) ) ) );
		}
		else
		{
			inkRectangleRef.SetSize( m_rpmGaugeForegroundWidget, Vector2( ( ( Float )( ( ( Float )( m_rpmGaugeMaxSize.X ) ) ) ), ( rpmValue * ( ( Float )( m_rpmGaugeMaxSize.Y ) ) ) / m_rpmMaxValue ) );
		}
		inkTextRef.SetText( m_rpmValueWidget, IntToString( newRPM ) );
		m_currentRPM = newRPM;
	}

}

class gametimeLogicController extends IVehicleModuleController
{
	private editable var m_gametimeTextWidget : inkTextRef;
	private var m_gametimeBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_vehicle : weak< VehicleObject >;
	private var m_parent : weak< vehicleUIGameController >;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_gametimeBBConnectionId ) )
			{
				m_gametimeBBConnectionId = vehBB.RegisterListenerString( GetAllBlackboardDefs().Vehicle.GameTime, this, 'OnGameTimeChanged' );
				m_vehBB = vehBB;
				m_vehicle = vehicle;
				m_parent = gameController;
			}
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_gametimeBBConnectionId )
			{
				m_vehBB.UnregisterListenerString( GetAllBlackboardDefs().Vehicle.GameTime, m_gametimeBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_gametimeBBConnectionId )
			{
				m_vehBB.UnregisterListenerString( GetAllBlackboardDefs().Vehicle.GameTime, m_gametimeBBConnectionId );
			}
		}
	}

	public function OnGameTimeChanged( time : String )
	{
		var currenTtime : GameTime;
		inkTextRef.SetText( m_gametimeTextWidget, time );
		currenTtime = GameInstance.GetTimeSystem( m_vehicle.GetGame() ).GetGameTime();
		m_parent.EvaluateWidgetStyle( currenTtime );
	}

}

class instrumentPanelLogicController extends IVehicleModuleController
{
	private editable var m_lightStateImageWidget : inkImageRef;
	private editable var m_cautionStateImageWidget : inkImageRef;
	private var m_lightStateBBConnectionId : CallbackHandle;
	private var m_cautionStateBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_lightStateBBConnectionId ) )
			{
				m_lightStateBBConnectionId = vehBB.RegisterListenerInt( GetAllBlackboardDefs().Vehicle.HeadLightMode, this, 'OnHeadLightModeChanged' );
			}
			if( !( m_cautionStateBBConnectionId ) )
			{
				m_cautionStateBBConnectionId = vehBB.RegisterListenerInt( GetAllBlackboardDefs().Vehicle.DamageState, this, 'OnCautionStateChanged' );
			}
			m_vehBB = vehBB;
			ForceUpdate();
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_lightStateBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.HeadLightMode, m_lightStateBBConnectionId );
			}
			if( m_cautionStateBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.DamageState, m_cautionStateBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_lightStateBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.HeadLightMode, m_lightStateBBConnectionId );
			}
			if( m_cautionStateBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.DamageState, m_cautionStateBBConnectionId );
			}
		}
	}

	protected function ForceUpdate()
	{
		OnHeadLightModeChanged( m_vehBB.GetInt( GetAllBlackboardDefs().Vehicle.HeadLightMode ) );
		OnCautionStateChanged( m_vehBB.GetInt( GetAllBlackboardDefs().Vehicle.DamageState ) );
	}

	public function OnHeadLightModeChanged( state : Int32 )
	{
		if( state == ( ( Int32 )( vehicleELightMode.Off ) ) )
		{
			inkImageRef.SetOpacity( m_lightStateImageWidget, 0.0 );
		}
		else if( state == ( ( Int32 )( vehicleELightMode.On ) ) )
		{
			inkImageRef.SetOpacity( m_lightStateImageWidget, 0.5 );
		}
		else if( state == ( ( Int32 )( vehicleELightMode.HighBeams ) ) )
		{
			inkImageRef.SetOpacity( m_lightStateImageWidget, 1.0 );
		}
	}

	public function OnCautionStateChanged( state : Int32 )
	{
		if( state == 2 )
		{
			inkImageRef.SetOpacity( m_cautionStateImageWidget, 1.0 );
		}
	}

}

class gearboxLogicController extends IVehicleModuleController
{
	private editable var m_gearboxRImageWidget : inkImageRef;
	private editable var m_gearboxNImageWidget : inkImageRef;
	private editable var m_gearboxDImageWidget : inkImageRef;
	private var m_gearboxBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_gearboxBBConnectionId ) )
			{
				m_gearboxBBConnectionId = vehBB.RegisterListenerInt( GetAllBlackboardDefs().Vehicle.GearValue, this, 'OnGearBoxChanged' );
				m_vehBB = vehBB;
			}
			ForceUpdate();
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_gearboxBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.GearValue, m_gearboxBBConnectionId );
			}
		}
	}

	protected function ForceUpdate()
	{
		OnGearBoxChanged( m_vehBB.GetInt( GetAllBlackboardDefs().Vehicle.GearValue ) );
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_gearboxBBConnectionId )
			{
				m_vehBB.UnregisterListenerInt( GetAllBlackboardDefs().Vehicle.GearValue, m_gearboxBBConnectionId );
			}
		}
	}

	public function OnGearBoxChanged( gear : Int32 )
	{
		if( gear == 0 )
		{
			inkImageRef.SetOpacity( m_gearboxRImageWidget, 1.0 );
			inkImageRef.SetOpacity( m_gearboxNImageWidget, 0.0 );
			inkImageRef.SetOpacity( m_gearboxDImageWidget, 0.1 );
		}
		else if( gear >= 0 )
		{
			inkImageRef.SetOpacity( m_gearboxRImageWidget, 0.1 );
			inkImageRef.SetOpacity( m_gearboxNImageWidget, 0.1 );
			inkImageRef.SetOpacity( m_gearboxDImageWidget, 1.0 );
		}
		else if( gear <= 0 )
		{
			inkImageRef.SetOpacity( m_gearboxRImageWidget, 0.1 );
			inkImageRef.SetOpacity( m_gearboxNImageWidget, 1.10000002 );
			inkImageRef.SetOpacity( m_gearboxDImageWidget, 0.1 );
		}
	}

}

class RadioLogicController extends IVehicleModuleController
{
	private editable var radioTextWidget : inkTextRef;
	private editable var radioEQWidget : inkCanvasRef;
	private var m_radioStateBBConnectionId : CallbackHandle;
	private var m_radioNameBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_eqLoopAnimProxy : inkAnimProxy;
	private var m_radioTextWidgetSize : Vector2;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_radioStateBBConnectionId ) )
			{
				m_radioStateBBConnectionId = vehBB.RegisterListenerBool( GetAllBlackboardDefs().Vehicle.VehRadioState, this, 'OnRadioStateChanged', true );
			}
			if( !( m_radioNameBBConnectionId ) )
			{
				m_radioNameBBConnectionId = vehBB.RegisterListenerName( GetAllBlackboardDefs().Vehicle.VehRadioStationName, this, 'OnRadioNameChanged', true );
			}
			m_vehBB = vehBB;
		}
	}

	public override function UnregisterCallbacks()
	{
		InternalUnregisterCallbacks();
	}

	protected event OnUninitialize()
	{
		InternalUnregisterCallbacks();
	}

	private function InternalUnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_radioStateBBConnectionId )
			{
				m_vehBB.UnregisterListenerBool( GetAllBlackboardDefs().Vehicle.VehRadioState, m_radioStateBBConnectionId );
			}
			if( m_radioNameBBConnectionId )
			{
				m_vehBB.UnregisterListenerName( GetAllBlackboardDefs().Vehicle.VehRadioStationName, m_radioNameBBConnectionId );
			}
		}
	}

	public function OnRadioStateChanged( state : Bool )
	{
		var playbackOptions : inkAnimOptions;
		if( state )
		{
			inkTextRef.SetVisible( radioTextWidget, true );
			inkCanvasRef.SetVisible( radioEQWidget, true );
			playbackOptions.loopInfinite = true;
			playbackOptions.loopType = inkanimLoopType.Cycle;
			m_eqLoopAnimProxy = PlayLibraryAnimation( 'eq_loop', playbackOptions );
		}
		else
		{
			if( m_eqLoopAnimProxy )
			{
				m_eqLoopAnimProxy.Stop();
			}
			inkTextRef.SetVisible( radioTextWidget, false );
			inkCanvasRef.SetVisible( radioEQWidget, false );
		}
	}

	public function OnRadioNameChanged( station : CName )
	{
		inkTextRef.SetText( radioTextWidget, NameToString( station ) );
		m_radioTextWidgetSize = inkTextRef.GetSize( radioTextWidget );
	}

}

class analogTachLogicController extends IVehicleModuleController
{
	private editable var m_analogTachNeedleWidget : inkWidgetRef;
	private editable var m_analogTachNeedleMinRotation : Float;
	private editable var m_analogTachNeedleMaxRotation : Float;
	private var m_rpmValueBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_rpmMaxValue : Float;
	private var m_rpmMinValue : Float;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		var record : weak< Vehicle_Record >;
		var vehEngineData : weak< VehicleEngineData_Record >;
		if( vehBB )
		{
			if( !( m_rpmValueBBConnectionId ) )
			{
				m_rpmValueBBConnectionId = vehBB.RegisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, this, 'OnRpmValueChanged' );
				record = vehicle.GetRecord();
				vehEngineData = record.VehEngineData();
				m_rpmMinValue = vehEngineData.MinRPM();
				m_rpmMaxValue = vehEngineData.MaxRPM();
				m_vehBB = vehBB;
			}
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_rpmValueBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, m_rpmValueBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_rpmValueBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.RPMValue, m_rpmValueBBConnectionId );
			}
		}
	}

	public function OnRpmValueChanged( rpmValue : Float )
	{
		var desiredRotation : Float;
		var normalizedRPM : Float;
		var minimumIdle : Float;
		normalizedRPM = ( rpmValue / m_rpmMaxValue ) - 0.13;
		minimumIdle = 0.045;
		if( rpmValue < 500.0 )
		{
			normalizedRPM = 0.0;
		}
		else if( normalizedRPM < minimumIdle )
		{
			normalizedRPM = minimumIdle;
		}
		desiredRotation = normalizedRPM * ( m_analogTachNeedleMaxRotation + AbsF( m_analogTachNeedleMinRotation ) );
		desiredRotation = desiredRotation - AbsF( m_analogTachNeedleMinRotation );
		inkWidgetRef.SetRotation( m_analogTachNeedleWidget, desiredRotation );
	}

}

class analogSpeedometerLogicController extends IVehicleModuleController
{
	private editable var m_analogSpeedNeedleWidget : inkWidgetRef;
	private editable var m_analogSpeedNeedleMinRotation : Float;
	private editable var m_analogSpeedNeedleMaxRotation : Float;
	private editable var m_analogSpeedNeedleMaxValue : Float;
	private var m_speedBBConnectionId : CallbackHandle;
	private var m_vehBB : weak< IBlackboard >;
	private var m_vehicle : weak< VehicleObject >;

	public override function RegisterCallbacks( vehicle : weak< VehicleObject >, vehBB : weak< IBlackboard >, gameController : weak< vehicleUIGameController > )
	{
		if( vehBB )
		{
			if( !( m_speedBBConnectionId ) )
			{
				m_speedBBConnectionId = vehBB.RegisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, this, 'OnSpeedValueChanged' );
				m_vehBB = vehBB;
				m_vehicle = vehicle;
			}
		}
	}

	public override function UnregisterCallbacks()
	{
		if( m_vehBB )
		{
			if( m_speedBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, m_speedBBConnectionId );
			}
		}
	}

	protected event OnUninitialize()
	{
		if( m_vehBB )
		{
			if( m_speedBBConnectionId )
			{
				m_vehBB.UnregisterListenerFloat( GetAllBlackboardDefs().Vehicle.SpeedValue, m_speedBBConnectionId );
			}
		}
	}

	public function OnSpeedValueChanged( speed : Float )
	{
		var desiredRotation : Float;
		var normalizedSpeed : Float;
		var speedMPH : Float;
		var multiplier : Float;
		multiplier = GameInstance.GetStatsDataSystem( m_vehicle.GetGame() ).GetValueFromCurve( 'vehicle_ui', speed, 'speed_to_multiplier' );
		speedMPH = ( ( Float )( RoundMath( speed * multiplier ) ) );
		normalizedSpeed = ClampF( speedMPH / m_analogSpeedNeedleMaxValue, 0.0, 1.0 );
		desiredRotation = normalizedSpeed * ( m_analogSpeedNeedleMaxRotation + AbsF( m_analogSpeedNeedleMinRotation ) );
		desiredRotation = desiredRotation - AbsF( m_analogSpeedNeedleMinRotation );
		inkWidgetRef.SetRotation( m_analogSpeedNeedleWidget, desiredRotation );
	}

}

