class IronsightDetail
{

	public static function PlaybackCycleInfinite() : inkAnimOptions
	{
		var playbackOptions : inkAnimOptions;
		playbackOptions.loopInfinite = true;
		playbackOptions.loopType = inkanimLoopType.Cycle;
		return playbackOptions;
	}

	public static function PlaybackCycleOnce() : inkAnimOptions
	{
		var playbackOptions : inkAnimOptions;
		playbackOptions.loopInfinite = false;
		playbackOptions.loopType = inkanimLoopType.None;
		return playbackOptions;
	}

	public static function PlaybackWithTimeDilation( timeDilation : Float ) : inkAnimOptions
	{
		var playbackOptions : inkAnimOptions;
		playbackOptions.applyCustomTimeDilation = true;
		playbackOptions.customTimeDilation = timeDilation;
		return playbackOptions;
	}

	public static function Fmodf( a : Float, b : Float ) : Float
	{
		if( b == 0.0 )
		{
			return 0.0;
		}
		return a - ( ( ( Float )( FloorF( a / b ) ) ) * b );
	}

	public static function SetSlide( widget : inkWidgetRef, startPosition : Vector2, horizontal : Float, vertical : Float )
	{
		var widgetSize : Vector2;
		var translation : Vector2;
		if( !( inkWidgetRef.IsValid( widget ) ) )
		{
			return;
		}
		widgetSize = inkWidgetRef.GetSize( widget );
		translation.X = startPosition.X + ( horizontal * widgetSize.X );
		translation.Y = startPosition.Y + ( vertical * widgetSize.Y );
		inkWidgetRef.SetTranslation( widget, translation );
	}

	public static function Pitch( forward : Vector4 ) : Float
	{
		return Rad2Deg( AtanF( SqrtF( ( forward.Y * forward.Y ) + ( forward.X * forward.X ) ), forward.Z ) );
	}

}

class ChargebarStatsListener extends ScriptStatsListener
{
	private var m_controller : weak< ChargebarController >;

	public function Init( controller : ChargebarController, stat : gamedataStatType )
	{
		m_controller = controller;
		SetStatType( stat );
	}

	public override function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		m_controller.OnStatChanged( ownerID, statType, diff, total );
	}

}

class ChargebarController extends inkLogicController
{
	protected editable var m_foreground : inkWidgetRef;
	protected editable var m_midground : inkWidgetRef;
	protected editable var m_background : inkWidgetRef;
	protected editable var m_maxChargeAnimationName : CName;
	protected editable var m_maxChargeResetAnimationName : CName;
	protected editable var m_staticChargeAnimationName : CName;
	protected editable var m_chargedShootAnimationName : CName;
	protected editable var m_radialWipe : Bool;
	protected editable var m_verticalChargeBar : Bool;
	protected editable var m_playStaticChargeAnimationInstead : Bool;
	protected var m_player : weak< GameObject >;
	protected var m_statsSystem : StatsSystem;
	protected var m_canFullyCharge : Bool;
	protected var m_canOvercharge : Bool;
	protected var m_listenerFullCharge : ChargebarStatsListener;
	protected var m_listenerOvercharge : ChargebarStatsListener;
	protected var m_animationMaxCharge : inkAnimProxy;
	protected var m_animationStaticCharge : inkAnimProxy;
	protected var m_animationChargedShoot : inkAnimProxy;
	var m_animationStaticChargePlayed : Bool;
	protected var m_isCharged : Bool;

	public function OnPlayerAttach( player : GameObject )
	{
		m_player = player;
		m_statsSystem = GameInstance.GetStatsSystem( player.GetGame() );
		m_canFullyCharge = m_statsSystem.GetStatBoolValue( player.GetEntityID(), gamedataStatType.CanFullyChargeWeapon );
		m_canOvercharge = m_statsSystem.GetStatBoolValue( player.GetEntityID(), gamedataStatType.CanOverchargeWeapon );
		m_listenerFullCharge = new ChargebarStatsListener;
		m_listenerFullCharge.Init( this, gamedataStatType.CanFullyChargeWeapon );
		m_listenerOvercharge = new ChargebarStatsListener;
		m_listenerOvercharge.Init( this, gamedataStatType.CanOverchargeWeapon );
		OnChargeValueChanged( 0.0 );
	}

	public function OnPlayerDetach( player : GameObject )
	{
		m_listenerFullCharge = NULL;
		m_listenerOvercharge = NULL;
	}

	public function OnStatChanged( ownerID : StatsObjectID, statType : gamedataStatType, diff : Float, total : Float )
	{
		switch( statType )
		{
			case gamedataStatType.CanFullyChargeWeapon:
				m_canFullyCharge = total > 0.0;
			break;
			case gamedataStatType.CanOverchargeWeapon:
				m_canOvercharge = total > 0.0;
			break;
		}
	}

	public function OnTriggerModeChanged( value : Variant )
	{
		var record : TriggerMode_Record;
		var isVisible : Bool;
		record = ( ( TriggerMode_Record )value );
		isVisible = gamedataTriggerMode.Charge == record.Type();
		inkWidgetRef.SetVisible( m_foreground, isVisible );
		inkWidgetRef.SetVisible( m_midground, isVisible );
		inkWidgetRef.SetVisible( m_background, isVisible );
	}

	public function OnChargeValueChanged( value : Float )
	{
		var threshold : Float;
		threshold = GetCurrentChargeLimit();
		if( m_playStaticChargeAnimationInstead )
		{
			PlayStaticChargeAnimation( value, threshold );
		}
		else if( m_radialWipe )
		{
			SetRadialWipe( value, threshold );
		}
		else
		{
			SetBarsSize( value, threshold );
		}
		EvalChargeAnimation( value, threshold );
	}

	protected function GetCurrentChargeLimit() : Float
	{
		if( m_canOvercharge )
		{
			return WeaponObject.GetOverchargeThreshold( GetWeaponObject() );
		}
		if( m_canFullyCharge )
		{
			return WeaponObject.GetFullyChargedThreshold( GetWeaponObject() );
		}
		return WeaponObject.GetBaseMaxChargeThreshold( GetWeaponObject() );
	}

	public function SetRadialWipe( value : Float, threshold : Float )
	{
		var perecent : Float;
		perecent = value / threshold;
		inkWidgetRef.SetVisible( m_foreground, perecent > 0.0 );
		inkWidgetRef.SetOpacity( m_foreground, perecent );
		inkWidgetRef.Get( m_foreground ).SetEffectParamValue( inkEffectType.RadialWipe, 'RadialWipe_0', 'transition', perecent );
	}

	protected function SetBarsSize( value : Float, threshold : Float )
	{
		inkWidgetRef.SetVisible( m_foreground, value > 0.0 );
		inkWidgetRef.SetVisible( m_midground, value > 0.0 );
		inkWidgetRef.SetVisible( m_background, value > 0.0 );
		if( m_verticalChargeBar )
		{
			inkWidgetRef.SetScale( m_foreground, Vector2( 1.0, value / threshold ) );
			inkWidgetRef.SetScale( m_midground, Vector2( 1.0, WeaponObject.GetFullyChargedThreshold( GetWeaponObject() ) / threshold ) );
			inkWidgetRef.SetScale( m_background, Vector2( 1.0, WeaponObject.GetBaseMaxChargeThreshold( GetWeaponObject() ) / threshold ) );
		}
		else
		{
			inkWidgetRef.SetScale( m_foreground, Vector2( value / threshold, 1.0 ) );
			inkWidgetRef.SetScale( m_midground, Vector2( WeaponObject.GetFullyChargedThreshold( GetWeaponObject() ) / threshold, 1.0 ) );
			inkWidgetRef.SetScale( m_background, Vector2( WeaponObject.GetBaseMaxChargeThreshold( GetWeaponObject() ) / threshold, 1.0 ) );
		}
	}

	protected function EvalChargeAnimation( value : Float, threshold : Float )
	{
		if( value == 0.0 )
		{
			StopChargingAnimation();
			if( m_isCharged )
			{
				PlayChargedShootAnimation();
				m_isCharged = false;
			}
		}
		if( value >= threshold )
		{
			m_animationStaticChargePlayed = false;
			m_isCharged = true;
			if( !( m_animationMaxCharge ) )
			{
				m_animationMaxCharge = PlayLibraryAnimation( m_maxChargeAnimationName, IronsightDetail.PlaybackCycleInfinite() );
			}
		}
		else if( m_animationMaxCharge )
		{
			m_animationMaxCharge.Stop();
			m_animationMaxCharge = NULL;
			PlayLibraryAnimation( m_maxChargeResetAnimationName );
		}
	}

	private function StopChargingAnimation()
	{
		m_animationStaticChargePlayed = false;
		if( m_animationStaticCharge && m_animationStaticCharge.IsPlaying() )
		{
			m_animationStaticCharge.GotoStartAndStop( true );
			m_animationStaticCharge = NULL;
		}
	}

	public function PlayStaticChargeAnimation( value : Float, threshold : Float )
	{
		var chargingRatio : Float;
		if( m_animationStaticChargePlayed || m_animationStaticCharge.IsPlaying() )
		{
			return;
		}
		if( ( value / threshold ) > 0.1 )
		{
			chargingRatio = GetWeponChargingSpeedRatio();
			m_animationStaticChargePlayed = true;
			m_animationStaticCharge = PlayLibraryAnimation( m_staticChargeAnimationName, IronsightDetail.PlaybackWithTimeDilation( chargingRatio ) );
		}
	}

	public function GetWeponChargingSpeedRatio() : Float
	{
		var weponObjectId : StatsObjectID;
		var chargeTime : Float;
		var baseChargeTime : Float;
		weponObjectId = GetWeaponObject().GetEntityID();
		chargeTime = m_statsSystem.GetStatValue( weponObjectId, gamedataStatType.ChargeTime );
		baseChargeTime = m_statsSystem.GetStatValue( weponObjectId, gamedataStatType.BaseChargeTime );
		return chargeTime / baseChargeTime;
	}

	public function PlayChargedShootAnimation()
	{
		if( m_animationChargedShoot != NULL )
		{
			m_animationChargedShoot.Stop();
			m_animationChargedShoot = NULL;
		}
		m_animationChargedShoot = PlayLibraryAnimation( m_chargedShootAnimationName );
	}

	protected function GetWeaponObject() : WeaponObject
	{
		return ScriptedPuppet.GetWeaponRight( m_player );
	}

}

class AltimeterController extends inkLogicController
{
	private editable var m_faceUp : inkWidgetRef;
	private editable var m_faceDown : inkWidgetRef;
	private editable var m_textWidget : inkTextRef;
	private editable var m_decimalPrecision : Uint32;
	default m_decimalPrecision = 2;
	private var m_faceUpStartPosition : Vector2;
	private var m_faceDownStartPosition : Vector2;
	private var m_playerPuppet : weak< GameObject >;
	private editable var m_warpDistance : Float;
	default m_warpDistance = 2.0f;
	private var m_alitimeterValue : Float;
	private var m_precisionEpsilon : Float;

	public function OnPlayerAttach( playerPuppet : GameObject )
	{
		m_playerPuppet = playerPuppet;
		m_precisionEpsilon = PowF( 10.0, -( ( ( Float )( m_decimalPrecision ) ) ) );
	}

	public function OnPlayerDetach( playerPuppet : GameObject )
	{
		m_playerPuppet = NULL;
	}

	protected event OnUpdate()
	{
		Update();
	}

	public function Update()
	{
		var valueVertical : Float;
		var worldPosition : Vector4;
		if( !( m_playerPuppet ) )
		{
			return;
		}
		worldPosition = m_playerPuppet.GetWorldPosition();
		valueVertical = IronsightDetail.Fmodf( worldPosition.Z / m_warpDistance, 0.5 );
		IronsightDetail.SetSlide( m_faceUp, m_faceUpStartPosition, 0.0, valueVertical );
		IronsightDetail.SetSlide( m_faceDown, m_faceDownStartPosition, 0.0, valueVertical );
		if( ( AbsF( worldPosition.Z - m_alitimeterValue ) > m_precisionEpsilon ) && inkTextRef.IsValid( m_textWidget ) )
		{
			inkTextRef.SetText( m_textWidget, FloatToStringPrec( worldPosition.Z, ( ( Int32 )( m_decimalPrecision ) ) ) );
			m_alitimeterValue = worldPosition.Z;
		}
	}

}

struct AnimationElement
{
	var m_animation : CName;
	var m_animOptions : inkAnimOptions;
}

class AnimationChain
{
	var m_data : array< AnimationElement >;
	var m_name : CName;

	public function AddAnimation( name : CName, const options : ref< inkAnimOptions > )
	{
		var element : AnimationElement;
		element.m_animation = name;
		element.m_animOptions = options;
		m_data.PushBack( element );
	}

}

class AnimationChainPlayer
{
	var m_animationProxy : inkAnimProxy;
	var m_current : AnimationChain;
	var m_current_stage : Int32;
	var m_next : AnimationChain;
	var m_owner : weak< inkLogicController >;

	public function Play( animationChain : AnimationChain )
	{
		if( m_current && ( m_current.m_data.Size() > 0 ) )
		{
			m_next = animationChain;
			return;
		}
		BeginAnimation( animationChain );
	}

	public function PlayNow( animationChain : AnimationChain )
	{
		Clean();
		BeginAnimation( animationChain );
		if( m_next )
		{
			m_next.m_data.Clear();
			m_next.m_name = '';
		}
	}

	private function BeginAnimation( animationChain : AnimationChain )
	{
		m_current = animationChain;
		PlayAnimationStage( 0 );
	}

	private function PlayAnimationStage( stage : Int32 )
	{
		m_animationProxy = m_owner.PlayLibraryAnimation( m_current.m_data[ stage ].m_animation, m_current.m_data[ stage ].m_animOptions );
		m_current_stage = stage;
		if( m_animationProxy )
		{
			m_animationProxy.RegisterToCallback( GetEndEvent( m_current.m_data[ stage ].m_animOptions ), this, 'OnNextAnimation' );
		}
		else if( m_current.m_data.Size() > stage )
		{
			OnNextAnimation();
		}
	}

	protected event OnNextAnimation( optional anim : inkAnimProxy )
	{
		if( ( m_next && ( m_next.m_data.Size() > 0 ) ) && m_next.m_name != m_current.m_name )
		{
			HandleInteruption();
		}
		else if( !( m_current.m_data[ m_current_stage ].m_animOptions.loopInfinite ) )
		{
			Clean();
			if( ( m_current_stage + 1 ) == m_current.m_data.Size() )
			{
				m_current.m_data.Clear();
			}
			else
			{
				PlayAnimationStage( m_current_stage + 1 );
			}
		}
	}

	private function HandleInteruption()
	{
		Clean();
		BeginAnimation( m_next );
	}

	private function Clean()
	{
		if( m_animationProxy )
		{
			m_animationProxy.UnregisterFromAllCallbacks( GetEndEvent( m_current.m_data[ m_current_stage ].m_animOptions ) );
			m_animationProxy.Stop();
			m_animationProxy = NULL;
		}
	}

	private function GetEndEvent( const animOptions : ref< inkAnimOptions > ) : inkanimEventType
	{
		return ( ( animOptions.loopInfinite ) ? ( inkanimEventType.OnEndLoop ) : ( inkanimEventType.OnFinish ) );
	}

}

class BasicAnimationController extends inkLogicController
{
	protected editable var m_showAnimation : CName;
	protected editable var m_idleAnimation : CName;
	protected editable var m_hideAnimation : CName;
	protected var m_animationPlayer : AnimationChainPlayer;
	protected var m_currentAnimation : CName;

	protected event OnInitialize()
	{
		m_animationPlayer = new AnimationChainPlayer;
		m_animationPlayer.m_owner = this;
	}

	public function PlayShow( optional immediately : Bool )
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'Show';
		animations.AddAnimation( m_showAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_idleAnimation, IronsightDetail.PlaybackCycleInfinite() );
		if( immediately )
		{
			m_animationPlayer.PlayNow( animations );
		}
		else
		{
			m_animationPlayer.Play( animations );
		}
	}

	public function PlayHide( optional immediately : Bool )
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'Hide';
		animations.AddAnimation( m_hideAnimation, IronsightDetail.PlaybackCycleOnce() );
		if( immediately )
		{
			m_animationPlayer.PlayNow( animations );
		}
		else
		{
			m_animationPlayer.Play( animations );
		}
	}

}

class TargetAttitudeAnimationController extends BasicAnimationController
{
	private editable var m_hostileShowAnimation : CName;
	private editable var m_hostileIdleAnimation : CName;
	private editable var m_hostileHideAnimation : CName;
	private var m_attitude : EAIAttitude;
	default m_attitude = EAIAttitude.AIA_Neutral;

	public function OnPlayerAttach( playerPuppet : GameObject )
	{
		m_attitude = EAIAttitude.AIA_Neutral;
	}

	public function OnPlayerDetach( playerPuppet : GameObject )
	{
		m_attitude = EAIAttitude.AIA_Neutral;
	}

	public function PlayShowHostile()
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'ShowH';
		animations.AddAnimation( m_hostileShowAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_hostileIdleAnimation, IronsightDetail.PlaybackCycleInfinite() );
		m_animationPlayer.Play( animations );
	}

	private function PlayHideHostile()
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'HideH';
		animations.AddAnimation( m_hostileHideAnimation, IronsightDetail.PlaybackCycleOnce() );
		m_animationPlayer.Play( animations );
	}

	public function PlayHideToHostile()
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'HideToF';
		animations.AddAnimation( m_hostileHideAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_showAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_idleAnimation, IronsightDetail.PlaybackCycleInfinite() );
		m_animationPlayer.Play( animations );
	}

	public function PlayHideToFriendly()
	{
		var animations : AnimationChain;
		animations = new AnimationChain;
		animations.m_name = 'HideToH';
		animations.AddAnimation( m_hideAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_hostileShowAnimation, IronsightDetail.PlaybackCycleOnce() );
		animations.AddAnimation( m_hostileIdleAnimation, IronsightDetail.PlaybackCycleInfinite() );
		m_animationPlayer.Play( animations );
	}

	public function OnAttitudeChanged( arg : Int32 )
	{
		var attitude : EAIAttitude;
		var previousAttitude : EAIAttitude;
		attitude = ( ( EAIAttitude )( arg ) );
		if( attitude == m_attitude )
		{
			return;
		}
		previousAttitude = m_attitude;
		m_attitude = attitude;
		switch( attitude )
		{
			case EAIAttitude.AIA_Hostile:
				if( previousAttitude == EAIAttitude.AIA_Friendly )
				{
					PlayHideToHostile();
				}
				else
				{
					PlayShowHostile();
				}
			break;
			case EAIAttitude.AIA_Friendly:
				if( previousAttitude == EAIAttitude.AIA_Hostile )
				{
					PlayHideToFriendly();
				}
				else
				{
					PlayShow();
				}
			break;
			default:
				if( previousAttitude == EAIAttitude.AIA_Friendly )
				{
					PlayHide();
				}
				else if( previousAttitude == EAIAttitude.AIA_Hostile )
				{
					PlayHideHostile();
				}
		}
	}

}

class AimDownSightController extends BasicAnimationController
{
	private var m_isAiming : Bool;
	default m_isAiming = false;

	public function OnPlayerAttach( playerPuppet : GameObject )
	{
		PlayHide( true );
		m_isAiming = false;
	}

	public function OnPlayerDetach( playerPuppet : GameObject )
	{
		PlayHide( true );
		m_isAiming = false;
	}

	public function OnAim( isAiming : Bool )
	{
		if( isAiming == m_isAiming )
		{
			return;
		}
		m_isAiming = isAiming;
		if( m_isAiming )
		{
			PlayShow( true );
		}
		else
		{
			PlayHide( true );
		}
	}

}

class CompassController extends inkLogicController
{
	private editable var m_faceLeft : inkWidgetRef;
	private editable var m_faceRight : inkWidgetRef;
	private editable var m_textWidget : inkTextRef;
	private editable var m_decimalPrecision : Uint32;
	default m_decimalPrecision = 2;
	private var m_faceRightStartPosition : Vector2;
	private var m_faceLeftStartPosition : Vector2;
	private editable var m_isVertical : Bool;
	default m_isVertical = false;
	private var m_valueFloat : Float;
	private var m_playerPuppet : weak< GameObject >;
	private var m_precisionEpsilon : Float;

	public function OnPlayerAttach( playerPuppet : GameObject )
	{
		m_playerPuppet = playerPuppet;
		m_precisionEpsilon = PowF( 10.0, -( ( ( Float )( m_decimalPrecision ) ) ) );
	}

	public function OnPlayerDetach( playerPuppet : GameObject )
	{
		m_playerPuppet = NULL;
	}

	public function Update()
	{
		var valueFloat : Float;
		var valueVertical : Float;
		var valueHorizontal : Float;
		var cameraForward : Vector4;
		if( !( m_playerPuppet ) )
		{
			return;
		}
		if( m_isVertical )
		{
			cameraForward = Matrix.GetDirectionVector( ( ( PlayerPuppet )( m_playerPuppet ) ).GetFPPCameraComponent().GetLocalToWorld() );
			valueHorizontal = 0.0;
			valueFloat = 90.0 - IronsightDetail.Pitch( cameraForward );
			valueVertical = valueFloat / 180.0;
		}
		else
		{
			valueVertical = 0.0;
			valueHorizontal = m_playerPuppet.GetWorldYaw() / 360.0;
			valueFloat = m_playerPuppet.GetWorldYaw() + 180.0;
		}
		IronsightDetail.SetSlide( m_faceLeft, m_faceLeftStartPosition, valueHorizontal, valueVertical );
		IronsightDetail.SetSlide( m_faceRight, m_faceRightStartPosition, valueHorizontal, valueVertical );
		if( ( AbsF( valueFloat - m_valueFloat ) > m_precisionEpsilon ) && inkTextRef.IsValid( m_textWidget ) )
		{
			inkTextRef.SetText( m_textWidget, FloatToStringPrec( valueFloat, ( ( Int32 )( m_decimalPrecision ) ) ) );
			m_valueFloat = valueFloat;
		}
	}

}

class IronsightTargetHealthUpdateEvent extends Event
{
}

class IronsightTargetHealthChangeListener extends ScriptStatPoolsListener
{
	private var m_parentIronsight : weak< IronsightGameController >;

	public static function Create( parentIronsight : IronsightGameController ) : IronsightTargetHealthChangeListener
	{
		var instance : IronsightTargetHealthChangeListener;
		instance = new IronsightTargetHealthChangeListener;
		instance.m_parentIronsight = parentIronsight;
		return instance;
	}

	public export override function OnStatPoolValueChanged( oldValue : Float, newValue : Float, percToPoints : Float )
	{
		var healthEvent : IronsightTargetHealthUpdateEvent;
		if( m_parentIronsight )
		{
			healthEvent = new IronsightTargetHealthUpdateEvent;
			m_parentIronsight.QueueEvent( healthEvent );
		}
	}

}

import class gameuiIronsightGameController extends inkGameController
{
	public import function EnableUpdate( enable : Bool );
}

class IronsightGameController extends gameuiIronsightGameController
{
	private var m_playerPuppet : weak< GameObject >;
	protected editable var m_dot : inkWidgetRef;
	protected editable var m_ammo : inkTextRef;
	protected editable var m_ammoSpareCount : inkTextRef;
	protected editable var m_range : inkTextRef;
	protected editable var m_seeThroughWalls : Bool;
	protected editable var m_targetAttitudeFriendly : inkWidgetRef;
	protected editable var m_targetAttitudeHostile : inkWidgetRef;
	protected editable var m_targetAttitudeEnemyNonHostile : inkWidgetRef;
	protected var m_weaponDataBB : weak< IBlackboard >;
	protected editable var m_targetHitAnimationName : CName;
	private var m_targetHitAnimation : inkAnimProxy;
	private var m_weaponDataTargetHitBBID : CallbackHandle;
	protected editable var m_shootAnimationName : CName;
	protected editable var m_firstEquipAnimationName : CName;
	private var m_shootAnimation : inkAnimProxy;
	private var m_weaponDataShootBBID : CallbackHandle;
	protected var m_currentAmmo : Int32;
	default m_currentAmmo = -1;
	private var m_animIntro : inkAnimProxy;
	private var m_animLoop : inkAnimProxy;
	private var m_animReload : inkAnimProxy;
	private var m_animPerfectCharge : inkAnimProxy;
	private var m_ActiveWeapon : SlotWeaponData;
	private var m_weaponItemData : InventoryItemData;
	private var m_originalWeapon : weak< WeaponObject >;
	private var m_InventoryManager : InventoryDataManagerV2;
	private var bb : weak< IBlackboard >;
	private var bbID : CallbackHandle;
	private var m_target : weak< GameObject >;
	private var m_targetBB : weak< IBlackboard >;
	private var m_targetRange : Float;
	private var m_targetRangeBBID : CallbackHandle;
	private var m_targetAttitudeBBID : CallbackHandle;
	private var m_targetAcquiredBBID : CallbackHandle;
	private var m_targetRangeObstructedBBID : CallbackHandle;
	private var m_targetAcquiredObstructedBBID : CallbackHandle;
	protected editable var m_targetRangeDecimalPrecision : Uint32;
	default m_targetRangeDecimalPrecision = 2;
	private var m_targetAttitudeAnimator : weak< TargetAttitudeAnimationController >;
	protected editable var m_targetAttitudeContainer : inkWidgetRef;
	private var m_targetHealthListener : IronsightTargetHealthChangeListener;
	private var m_compass : weak< CompassController >;
	protected editable var m_compassContainer : inkWidgetRef;
	private var m_compass2 : weak< CompassController >;
	protected editable var m_compassContainer2 : inkWidgetRef;
	private var m_altimeter : weak< AltimeterController >;
	protected editable var m_altimeterContainer : inkWidgetRef;
	private var m_weaponBB : weak< IBlackboard >;
	private var m_chargebar : weak< ChargebarController >;
	protected editable var m_chargebarContainer : inkWidgetRef;
	private var m_chargebarValueChanged : CallbackHandle;
	private var m_chargebarTriggerModeChanged : CallbackHandle;
	protected editable var m_ADSContainer : inkWidgetRef;
	private var m_ADSAnimator : weak< AimDownSightController >;
	private var m_playerStateMachineBB : weak< IBlackboard >;
	private var m_playerStateMachineUpperBodyBBID : CallbackHandle;
	private var m_crosshairStateChanged : CallbackHandle;
	private editable var m_perfectChargeIndicator : inkWidgetRef;
	private var m_crosshairState : gamePSMCrosshairStates;
	private var m_isTargetEnemy : Bool;
	private var m_attitude : EAIAttitude;
	protected var m_upperBodyState : gamePSMUpperBodyStates;

	protected function StopAnimation( anim : inkAnimProxy )
	{
		if( anim )
		{
			anim.Stop();
		}
	}

	private function ResetTargetData()
	{
		m_target = NULL;
		m_isTargetEnemy = false;
		OnTargetDistanceChanged( 0.0 );
		OnTargetAttitudeChanged( ( ( Int32 )( EAIAttitude.AIA_Neutral ) ) );
	}

	private function RegisterTargetCallbacks( register : Bool )
	{
		if( ( !( m_target ) || !( m_targetHealthListener ) ) || !( m_playerPuppet ) )
		{
			return;
		}
		if( register )
		{
			GameInstance.GetStatPoolsSystem( m_playerPuppet.GetGame() ).RequestRegisteringListener( m_target.GetEntityID(), gamedataStatPoolType.Health, m_targetHealthListener );
		}
		else
		{
			GameInstance.GetStatPoolsSystem( m_playerPuppet.GetGame() ).RequestUnregisteringListener( m_target.GetEntityID(), gamedataStatPoolType.Health, m_targetHealthListener );
		}
	}

	protected function UpdateTargetAttitudeVisibility()
	{
		if( inkWidgetRef.IsValid( m_targetAttitudeFriendly ) )
		{
			inkWidgetRef.SetVisible( m_targetAttitudeFriendly, m_attitude == EAIAttitude.AIA_Friendly );
		}
		if( ( inkWidgetRef.IsValid( m_targetAttitudeHostile ) && inkWidgetRef.IsValid( m_targetAttitudeEnemyNonHostile ) ) && ( m_targetAttitudeHostile == m_targetAttitudeEnemyNonHostile ) )
		{
			inkWidgetRef.SetVisible( m_targetAttitudeHostile, m_attitude == EAIAttitude.AIA_Hostile || m_isTargetEnemy );
		}
		else
		{
			if( inkWidgetRef.IsValid( m_targetAttitudeHostile ) )
			{
				inkWidgetRef.SetVisible( m_targetAttitudeHostile, m_attitude == EAIAttitude.AIA_Hostile );
			}
			if( inkWidgetRef.IsValid( m_targetAttitudeEnemyNonHostile ) )
			{
				inkWidgetRef.SetVisible( m_targetAttitudeEnemyNonHostile, m_isTargetEnemy && m_attitude != EAIAttitude.AIA_Hostile );
			}
		}
	}

	protected function RefreshTargetDistance()
	{
		if( inkTextRef.IsValid( m_range ) )
		{
			inkTextRef.SetText( m_range, ( ( m_target ) ? ( FloatToStringPrec( m_targetRange, ( ( Int32 )( m_targetRangeDecimalPrecision ) ) ) ) : ( "---" ) ) );
		}
	}

	private function RefreshTargetHealth()
	{
		if( !( m_target ) || IsDead( m_target ) )
		{
			ResetTargetData();
		}
	}

	protected static function IsDead( obj : GameObject ) : Bool
	{
		var puppet : ScriptedPuppet;
		var device : Device;
		puppet = ( ( ScriptedPuppet )( obj ) );
		device = ( ( Device )( obj ) );
		return ( puppet && puppet.IsDead() ) || ( device && device.GetDevicePS().IsBroken() );
	}

	protected event OnIronsightTargetHealthUpdateEvent( evt : IronsightTargetHealthUpdateEvent )
	{
		RefreshTargetHealth();
	}

	protected event OnTargetDistanceChanged( distance : Float )
	{
		m_targetRange = distance;
		RefreshTargetDistance();
	}

	protected event OnTargetAcquired( targetID : EntityID )
	{
		var target : GameObject;
		var puppet : ScriptedPuppet;
		RegisterTargetCallbacks( false );
		target = ( ( GameObject )( GameInstance.FindEntityByID( m_playerPuppet.GetGame(), targetID ) ) );
		if( target && !( IsDead( target ) ) )
		{
			m_target = target;
			RegisterTargetCallbacks( true );
			RefreshTargetDistance();
			puppet = ( ( ScriptedPuppet )( m_target ) );
			m_isTargetEnemy = puppet && puppet.IsAggressive();
			UpdateTargetAttitudeVisibility();
		}
		else
		{
			ResetTargetData();
		}
	}

	protected event OnTargetAttitudeChanged( attitude : Int32 )
	{
		m_attitude = ( ( EAIAttitude )( attitude ) );
		UpdateTargetAttitudeVisibility();
		if( m_targetAttitudeAnimator )
		{
			m_targetAttitudeAnimator.OnAttitudeChanged( attitude );
		}
	}

	protected event OnPlayerAttach( playerPuppet : GameObject )
	{
		var owner : Entity;
		var enableUpdate : Bool;
		var firstEquip : Bool;
		enableUpdate = false;
		firstEquip = false;
		m_playerPuppet = playerPuppet;
		owner = GetOwnerEntity();
		m_originalWeapon = ( ( WeaponObject )( owner ) );
		m_weaponBB = m_originalWeapon.GetSharedData();
		m_InventoryManager = new InventoryDataManagerV2;
		m_InventoryManager.Initialize( ( ( PlayerPuppet )( playerPuppet ) ) );
		bb = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_EquipmentData );
		bbID = bb.RegisterListenerVariant( GetAllBlackboardDefs().UI_EquipmentData.EquipmentData, this, 'OnWeaponDataChanged' );
		m_targetBB = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_TargetingInfo );
		m_targetAttitudeBBID = m_targetBB.RegisterDelayedListenerInt( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetAttitude, this, 'OnTargetAttitudeChanged' );
		m_targetRangeBBID = m_targetBB.RegisterDelayedListenerFloat( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetDistance, this, 'OnTargetDistanceChanged' );
		m_targetAcquiredBBID = m_targetBB.RegisterDelayedListenerEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget, this, 'OnTargetAcquired' );
		if( m_seeThroughWalls )
		{
			m_targetRangeObstructedBBID = m_targetBB.RegisterDelayedListenerFloat( GetAllBlackboardDefs().UI_TargetingInfo.ObstructedTargetDistance, this, 'OnTargetDistanceChanged' );
			m_targetAcquiredObstructedBBID = m_targetBB.RegisterDelayedListenerEntityID( GetAllBlackboardDefs().UI_TargetingInfo.CurrentObstructedTarget, this, 'OnTargetAcquired' );
		}
		m_weaponDataBB = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_ActiveWeaponData );
		m_weaponDataTargetHitBBID = m_weaponDataBB.RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_ActiveWeaponData.TargetHitEvent, this, 'OnTargetHit' );
		m_weaponDataShootBBID = m_weaponDataBB.RegisterDelayedListenerVariant( GetAllBlackboardDefs().UI_ActiveWeaponData.ShootEvent, this, 'OnShoot' );
		ResetTargetData();
		inkTextRef.SetText( m_ammo, "" );
		m_animIntro = PlayLibraryAnimation( 'intro', IronsightDetail.PlaybackCycleOnce() );
		if( m_animIntro )
		{
			m_animIntro.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnAnimationIntroFinished' );
		}
		else if( m_targetAttitudeAnimator )
		{
			m_targetAttitudeAnimator.OnAttitudeChanged( m_targetBB.GetInt( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetAttitude ) );
		}
		m_animLoop = PlayLibraryAnimation( 'loop', IronsightDetail.PlaybackCycleInfinite() );
		if( m_compass )
		{
			m_compass.OnPlayerAttach( playerPuppet );
		}
		if( m_compass2 )
		{
			m_compass2.OnPlayerAttach( playerPuppet );
		}
		if( m_altimeter )
		{
			m_altimeter.OnPlayerAttach( playerPuppet );
		}
		if( m_chargebar )
		{
			m_chargebar.OnPlayerAttach( playerPuppet );
		}
		if( m_weaponBB )
		{
			m_chargebarValueChanged = m_weaponBB.RegisterDelayedListenerFloat( GetAllBlackboardDefs().Weapon.Charge, this, 'OnChargeValueChanged' );
			m_chargebarTriggerModeChanged = m_weaponBB.RegisterDelayedListenerVariant( GetAllBlackboardDefs().Weapon.TriggerMode, this, 'OnTriggerModeChanged' );
		}
		if( m_targetAttitudeAnimator )
		{
			m_targetAttitudeAnimator.OnPlayerAttach( playerPuppet );
		}
		if( m_ADSAnimator )
		{
			m_ADSAnimator.OnPlayerAttach( playerPuppet );
		}
		m_playerStateMachineBB = GetBlackboardSystem().GetLocalInstanced( playerPuppet.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		if( m_playerStateMachineBB )
		{
			m_playerStateMachineUpperBodyBBID = m_playerStateMachineBB.RegisterDelayedListenerInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody, this, 'OnUpperBodyChanged' );
			enableUpdate = m_playerStateMachineBB.GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) );
			EnableUpdate( enableUpdate );
			m_crosshairStateChanged = m_playerStateMachineBB.RegisterDelayedListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Crosshair, this, 'OnCrosshairStatStateeChanged' );
		}
		inkWidgetRef.SetVisible( m_perfectChargeIndicator, false );
		m_targetHealthListener = IronsightTargetHealthChangeListener.Create( this );
		firstEquip = m_playerStateMachineBB.GetBool( GetAllBlackboardDefs().PlayerStateMachine.IsWeaponFirstEquip );
		if( firstEquip )
		{
			PlayLibraryAnimation( m_firstEquipAnimationName );
		}
	}

	protected event OnPerfectChargeUIEvent( evt : PerfectChargeUIEvent )
	{
		var animOptions : inkAnimOptions;
		animOptions.loopType = inkanimLoopType.Cycle;
		animOptions.loopInfinite = true;
		if( evt.type == 'perfectChargeCharged' )
		{
			inkWidgetRef.SetVisible( m_perfectChargeIndicator, true );
			m_animPerfectCharge = PlayLibraryAnimation( 'release', animOptions );
		}
		else if( evt.type == 'perfectChargeShot' )
		{
			if( m_animPerfectCharge && m_animPerfectCharge.IsPlaying() )
			{
				m_animPerfectCharge.Stop();
			}
			inkWidgetRef.SetVisible( m_perfectChargeIndicator, false );
			PlayLibraryAnimation( 'splash' );
		}
		else if( evt.type == 'perfectChargeFailed' )
		{
			if( m_animPerfectCharge && m_animPerfectCharge.IsPlaying() )
			{
				m_animPerfectCharge.Stop();
			}
			inkWidgetRef.SetVisible( m_perfectChargeIndicator, false );
			PlayLibraryAnimation( 'miss' );
		}
	}

	protected function OnAnimationIntroFinished( anim : inkAnimProxy )
	{
		m_animIntro = NULL;
		m_targetAttitudeAnimator.OnAttitudeChanged( m_targetBB.GetInt( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetAttitude ) );
	}

	protected event OnInitialize()
	{
		if( inkWidgetRef.IsValid( m_compassContainer ) )
		{
			m_compass = ( ( CompassController )( inkWidgetRef.GetController( m_compassContainer ) ) );
		}
		if( inkWidgetRef.IsValid( m_compassContainer2 ) )
		{
			m_compass2 = ( ( CompassController )( inkWidgetRef.GetController( m_compassContainer2 ) ) );
		}
		if( inkWidgetRef.IsValid( m_altimeterContainer ) )
		{
			m_altimeter = ( ( AltimeterController )( inkWidgetRef.GetController( m_altimeterContainer ) ) );
		}
		if( inkWidgetRef.IsValid( m_chargebarContainer ) )
		{
			m_chargebar = ( ( ChargebarController )( inkWidgetRef.GetController( m_chargebarContainer ) ) );
		}
		if( inkWidgetRef.IsValid( m_targetAttitudeContainer ) )
		{
			m_targetAttitudeAnimator = ( ( TargetAttitudeAnimationController )( inkWidgetRef.GetController( m_targetAttitudeContainer ) ) );
		}
		if( inkWidgetRef.IsValid( m_ADSContainer ) )
		{
			m_ADSAnimator = ( ( AimDownSightController )( inkWidgetRef.GetController( m_ADSContainer ) ) );
		}
		EnableUpdate( false );
	}

	protected event OnUninitialize() {}

	protected event OnCompassUpdate()
	{
		if( m_compass )
		{
			m_compass.Update();
		}
		if( m_compass2 )
		{
			m_compass2.Update();
		}
		if( m_altimeter )
		{
			m_altimeter.Update();
		}
	}

	protected event OnPlayerDetach( playerPuppet : GameObject )
	{
		m_InventoryManager.UnInitialize();
		bb.UnregisterListenerVariant( GetAllBlackboardDefs().UI_EquipmentData.EquipmentData, bbID );
		m_targetBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetAttitude, m_targetAttitudeBBID );
		m_targetBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_TargetingInfo.VisibleTargetDistance, m_targetRangeBBID );
		m_targetBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_TargetingInfo.CurrentVisibleTarget, m_targetAcquiredBBID );
		if( m_targetRangeObstructedBBID )
		{
			m_targetBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_TargetingInfo.ObstructedTargetDistance, m_targetRangeObstructedBBID );
		}
		if( m_targetAcquiredObstructedBBID )
		{
			m_targetBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_TargetingInfo.CurrentObstructedTarget, m_targetAcquiredObstructedBBID );
		}
		m_weaponDataBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_ActiveWeaponData.TargetHitEvent, m_weaponDataTargetHitBBID );
		m_weaponDataBB.UnregisterDelayedListener( GetAllBlackboardDefs().UI_ActiveWeaponData.ShootEvent, m_weaponDataShootBBID );
		StopAnimation( m_animIntro );
		StopAnimation( m_animLoop );
		StopAnimation( m_animReload );
		if( m_compass )
		{
			m_compass.OnPlayerDetach( playerPuppet );
		}
		if( m_compass2 )
		{
			m_compass2.OnPlayerDetach( playerPuppet );
		}
		if( m_altimeter )
		{
			m_altimeter.OnPlayerDetach( playerPuppet );
		}
		if( m_chargebar )
		{
			m_chargebar.OnPlayerDetach( playerPuppet );
		}
		if( m_weaponBB )
		{
			m_weaponBB.UnregisterDelayedListener( GetAllBlackboardDefs().Weapon.Charge, m_chargebarValueChanged );
			m_weaponBB.UnregisterDelayedListener( GetAllBlackboardDefs().Weapon.TriggerMode, m_chargebarTriggerModeChanged );
		}
		if( m_targetAttitudeAnimator )
		{
			m_targetAttitudeAnimator.OnPlayerDetach( playerPuppet );
		}
		if( m_playerStateMachineBB )
		{
			m_playerStateMachineBB.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.UpperBody, m_playerStateMachineUpperBodyBBID );
			m_playerStateMachineBB.UnregisterDelayedListener( GetAllBlackboardDefs().PlayerStateMachine.Crosshair, m_crosshairStateChanged );
		}
		if( m_ADSAnimator )
		{
			m_ADSAnimator.OnPlayerDetach( playerPuppet );
		}
		RegisterTargetCallbacks( false );
		m_targetHealthListener = NULL;
		bb = NULL;
	}

	protected function OnAmmoCountChanged( value : Int32 )
	{
		if( value == m_currentAmmo )
		{
			return;
		}
		m_currentAmmo = value;
		inkTextRef.SetText( m_ammo, IntToString( m_currentAmmo ) );
		if( ( 0 == m_currentAmmo ) && ( !( m_animReload ) || !( m_animReload.IsPlaying() ) ) )
		{
			m_animReload = PlayLibraryAnimation( 'reload', IronsightDetail.PlaybackCycleInfinite() );
		}
	}

	protected export function OnReloadEndLoop( anim : inkAnimProxy )
	{
		anim.Stop();
		anim.UnregisterFromAllCallbacks( inkanimEventType.OnEndLoop );
	}

	protected event OnWeaponDataChanged( value : Variant )
	{
		var slotDataHolder : SlotDataHolder;
		var currentData : SlotWeaponData;
		var item : gameItemData;
		slotDataHolder = ( ( SlotDataHolder )value );
		currentData = slotDataHolder.weapon;
		if( ItemID.IsValid( currentData.weaponID ) && IsOriginalWeapon( currentData.weaponID ) )
		{
			m_ActiveWeapon = currentData;
			if( m_ActiveWeapon.weaponID != currentData.weaponID )
			{
				item = m_InventoryManager.GetPlayerItemData( m_ActiveWeapon.weaponID );
				m_weaponItemData = m_InventoryManager.GetInventoryItemData( item );
			}
			SetRosterSlotData();
		}
	}

	protected event OnShoot( arg : Variant )
	{
		if( m_shootAnimation )
		{
			m_shootAnimation.Stop();
		}
		m_shootAnimation = PlayLibraryAnimation( m_shootAnimationName, IronsightDetail.PlaybackCycleOnce() );
	}

	protected event OnTargetHit( arg : Variant )
	{
		if( m_targetHitAnimation )
		{
			m_targetHitAnimation.Stop();
		}
		m_targetHitAnimation = PlayLibraryAnimation( m_targetHitAnimationName, IronsightDetail.PlaybackCycleOnce() );
	}

	public function OnAmmoSpareCountChanged()
	{
		var spareAmmo : Int32;
		spareAmmo = RPGManager.GetAmmoCountValue( m_playerPuppet, m_ActiveWeapon.weaponID );
		spareAmmo -= m_ActiveWeapon.ammoCurrent;
		inkTextRef.SetText( m_ammoSpareCount, IntToString( Max( spareAmmo, 0 ) ) );
	}

	private function SetRosterSlotData()
	{
		OnAmmoCountChanged( m_ActiveWeapon.ammoCurrent );
		OnAmmoSpareCountChanged();
	}

	protected event OnChargeValueChanged( value : Float )
	{
		m_chargebar.OnChargeValueChanged( value );
	}

	protected event OnTriggerModeChanged( triggerMode : Variant )
	{
		if( m_chargebar )
		{
			m_chargebar.OnTriggerModeChanged( triggerMode );
		}
	}

	protected event OnCrosshairStatStateeChanged( state : Int32 )
	{
		var newState : gamePSMCrosshairStates;
		newState = ( ( gamePSMCrosshairStates )( state ) );
		switch( newState )
		{
			case gamePSMCrosshairStates.Reload:
				if( ( m_animReload == NULL ) || !( m_animReload.IsPlaying() ) )
				{
					m_animReload = PlayLibraryAnimation( 'reload', IronsightDetail.PlaybackCycleInfinite() );
				}
			break;
			default:
				if( m_animReload && m_crosshairState == gamePSMCrosshairStates.Reload )
				{
					m_animReload.RegisterToCallback( inkanimEventType.OnEndLoop, this, 'OnReloadEndLoop' );
					m_animReload = NULL;
				}
		}
		m_crosshairState = newState;
	}

	protected event OnUpperBodyChanged( state : Int32 )
	{
		var isAiming : Bool;
		isAiming = isAiming = state == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) );
		m_upperBodyState = ( ( gamePSMUpperBodyStates )( state ) );
		EnableUpdate( isAiming );
		if( m_ADSAnimator )
		{
			m_ADSAnimator.OnAim( isAiming );
		}
	}

	protected function IsOriginalWeapon( weaponID : ItemID ) : Bool
	{
		return m_originalWeapon.GetItemID() == weaponID;
	}

	protected function IsWeaponActive() : Bool
	{
		return ItemID.GetTDBID( m_ActiveWeapon.weaponID ) == ItemID.GetTDBID( m_originalWeapon.GetItemID() );
	}

}

class PerfectChargeUIEvent extends Event
{
	var type : CName;
}

