class hubStaticSelectorController extends SelectorController
{
	protected editable var m_leftArrowWidget : inkWidgetRef;
	protected editable var m_rightArrowWidget : inkWidgetRef;
	protected editable var m_container : inkWidgetRef;
	protected editable var m_line : inkWidgetRef;
	protected var m_leftArrowController : weak< inkInputDisplayController >;
	protected var m_rightArrowController : weak< inkInputDisplayController >;
	protected var m_data : array< MenuData >;
	protected var m_widgetsControllers : array< weak< HubMenuLabelContentContainer > >;
	protected var m_currentIndex : Int32;
	protected var m_currentParent : Int32;
	private var m_currentData : array< MenuData >;
	private var m_lineTranslationAnimProxy : inkAnimProxy;
	private var m_lineSizeAnimProxy : inkAnimProxy;
	private var m_instantLineUpdateRequested : Bool;
	private var m_animationsRetryDiv : Float;
	private editable var m_debugText : inkTextRef;

	protected event OnInitialize()
	{
		m_leftArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_leftArrowWidget ) ) );
		m_rightArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_rightArrowWidget ) ) );
		RegisterToCallback( 'OnSelectionChanged', this, 'OnSelectionChanged' );
	}

	protected event OnUninitialize()
	{
		var controller : weak< HubMenuLabelContentContainer >;
		var i : Int32;
		for( i = 0; i < m_widgetsControllers.Size(); i += 1 )
		{
			controller = m_widgetsControllers[ i ];
			if( controller )
			{
				controller.UnregisterFromCallback( 'OnClick', this, 'OnMenuLabelClick' );
				controller.UnregisterFromCallback( 'OnHoverOver', this, 'OnMenuLabelHover' );
				controller.UnregisterFromCallback( 'OnHoverOut', this, 'OnMenuLabelHoverOut' );
			}
		}
	}

	public function SetupWidgets( const data : ref< array< MenuData > > )
	{
		var i : Int32;
		var widget : weak< inkWidget >;
		var controller : HubMenuLabelContentContainer;
		if( data.Size() != m_widgetsControllers.Size() )
		{
			( ( weak< weak< inkCompoundWidget > > )( inkWidgetRef.Get( m_container ) ) ).RemoveAllChildren();
			m_widgetsControllers.Clear();
			for( i = 0; i < data.Size(); i += 1 )
			{
				widget = SpawnFromLocal( inkWidgetRef.Get( m_container ), 'new_menu_element' );
				widget.SetRenderTransformPivot( 0.5, 1.0 );
				widget.SetAnchor( inkEAnchor.Centered );
				widget.SetAnchorPoint( 0.5, 0.5 );
				controller = ( ( HubMenuLabelContentContainer )( widget.GetController() ) );
				controller.RegisterToCallback( 'OnClick', this, 'OnMenuLabelClick' );
				controller.RegisterToCallback( 'OnHoverOver', this, 'OnMenuLabelHover' );
				controller.RegisterToCallback( 'OnHoverOut', this, 'OnMenuLabelHoverOut' );
				m_widgetsControllers.PushBack( controller );
			}
		}
		for( i = 0; i < data.Size(); i += 1 )
		{
			m_widgetsControllers[ i ].SetData( data[ i ] );
		}
		UpdateArrowsVisibility();
	}

	protected event OnMenuLabelClick( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		var evt : OpenMenuRequest;
		var data : MenuData;
		if( e.IsAction( 'select' ) )
		{
			controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
			PlaySound( 'TabButton', 'OnPress' );
			data = controller.GetData();
			ScrollTo( data );
			evt = new OpenMenuRequest;
			evt.m_eventData = data;
			evt.m_internal = true;
			evt.m_isMainMenu = false;
			QueueEvent( evt );
		}
	}

	protected event OnMenuLabelHover( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
		if( !( IsCurrent( controller ) ) )
		{
			controller.SetTextState( 'Hover' );
		}
	}

	protected event OnMenuLabelHoverOut( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
		if( !( IsCurrent( controller ) ) )
		{
			controller.SetTextState( 'Default' );
		}
	}

	protected function IsCurrent( const controller : weak< HubMenuLabelContentContainer > ) : Bool
	{
		var lastIndex : Int32;
		var isValid : Bool;
		lastIndex = m_widgetsControllers.Size() - 1;
		isValid = ( m_currentIndex <= lastIndex ) && ( m_currentIndex >= 0 );
		if( !( isValid ) )
		{
			return false;
		}
		return controller == m_widgetsControllers[ m_currentIndex ];
	}

	public function SetupMenuValues( const data : ref< array< MenuData > >, const currentElement : ref< MenuData > )
	{
		var i : Int32;
		var startIndex : Int32;
		Clear();
		startIndex = 0;
		for( i = 0; i < data.Size(); i += 1 )
		{
			AddValue( data[ i ].label );
			if( ( data[ i ].identifier == currentElement.identifier ) || ( data[ i ].identifier == currentElement.parentIdentifier ) )
			{
				startIndex = i;
			}
		}
		m_currentIndex = startIndex;
		SetCurrIndex( startIndex );
		UpdateArrowsVisibility();
	}

	public function SetupMenu( const data : ref< array< MenuData > >, startIdentifier : Int32 )
	{
		m_data = data;
		SetupWidgets( data );
		UpdateArrowsVisibility();
	}

	private function UpdateArrowsVisibility()
	{
		m_leftArrowController.SetVisible( m_currentData.Size() > 1 );
		m_rightArrowController.SetVisible( m_currentData.Size() > 1 );
	}

	public function GetMenusByParent( identifier : Int32 ) : array< MenuData >
	{
		var result : array< MenuData >;
		var i, limit : Int32;
		for( i = 0, limit = m_data.Size(); i < limit; i += 1 )
		{
			if( m_data[ i ].parentIdentifier == identifier )
			{
				if( !( m_data[ i ].disabled ) )
				{
					result.PushBack( m_data[ i ] );
				}
			}
		}
		return result;
	}

	public function ScrollTo( const data : ref< MenuData > )
	{
		var i, limit : Int32;
		var startIndex : Int32;
		if( data.parentIdentifier != m_currentParent )
		{
			m_currentData = GetMenusByParent( ( ( Int32 )( HubMenuItems.None ) ) );
			SetupMenuValues( m_currentData, data );
			SetupWidgets( m_currentData );
			UpdateHightlight( m_currentIndex, true );
			m_currentParent = data.parentIdentifier;
			m_instantLineUpdateRequested = true;
		}
		else
		{
			startIndex = 0;
			for( i = 0, limit = m_currentData.Size(); i < limit; i += 1 )
			{
				if( ( m_currentData[ i ].identifier == data.identifier ) || ( m_currentData[ i ].identifier == data.parentIdentifier ) )
				{
					startIndex = i;
					break;
				}
			}
			m_currentIndex = startIndex;
			SetCurrIndex( startIndex );
			m_instantLineUpdateRequested = false;
			UpdateHightlight( m_currentIndex );
			UpdateArrowsVisibility();
		}
	}

	protected event OnArrangeChildrenComplete()
	{
		QueueEvent( new hubStaticSelectorPostArrangeEvent );
	}

	protected event OnPostArrange( e : hubStaticSelectorPostArrangeEvent )
	{
		if( m_instantLineUpdateRequested )
		{
			UpdateHightlight( m_currentIndex, true );
			m_instantLineUpdateRequested = false;
		}
	}

	protected virtual function UpdateLabelsStates( currentIndex : Int32 )
	{
		var i, limit : Int32;
		for( i = 0, limit = m_widgetsControllers.Size(); i < limit; i += 1 )
		{
			m_widgetsControllers[ i ].SetTextState( ( ( i == currentIndex ) ? ( 'Selected' ) : ( 'Default' ) ) );
		}
	}

	protected virtual function UpdateHightlight( index : Int32, optional instant : Bool )
	{
		var width : Float;
		var position : Vector2;
		var time : Float;
		width = m_widgetsControllers[ index ].GetWidth();
		position = ( ( weak< weak< inkCompoundWidget > > )( inkWidgetRef.Get( m_container ) ) ).GetChildPosition( m_widgetsControllers[ index ].GetRootWidget() );
		UpdateLabelsStates( index );
		if( instant )
		{
			m_animationsRetryDiv = 0.0;
			m_lineTranslationAnimProxy.Stop();
			m_lineSizeAnimProxy.Stop();
			inkWidgetRef.SetTranslation( m_line, Vector2( position.X, 0.0 ) );
			inkWidgetRef.SetSize( m_line, Vector2( width, inkWidgetRef.GetHeight( m_line ) ) );
		}
		else
		{
			m_animationsRetryDiv += 0.5;
			time = 0.44999999 / MaxF( m_animationsRetryDiv, 1.0 );
			time = MaxF( time, 0.2 );
			AnimateLineTranslation( inkWidgetRef.Get( m_line ), position.X, time );
			AnimateLineSize( inkWidgetRef.Get( m_line ), width, time );
		}
		inkTextRef.SetText( m_debugText, FloatToString( m_animationsRetryDiv ) );
	}

	protected virtual function AnimateLineTranslation( targetWidget : weak< inkWidget >, targetX : Float, time : Float )
	{
		var translationsAnimDef : inkAnimDef;
		var translationInterpolator : inkAnimTranslation;
		var currentTranslation : Vector2;
		m_lineTranslationAnimProxy.Stop();
		currentTranslation = targetWidget.GetTranslation();
		translationsAnimDef = new inkAnimDef;
		translationInterpolator = new inkAnimTranslation;
		translationInterpolator.SetType( inkanimInterpolationType.Quartic );
		translationInterpolator.SetMode( inkanimInterpolationMode.EasyInOut );
		translationInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		translationInterpolator.SetStartTranslation( targetWidget.GetTranslation() );
		translationInterpolator.SetEndTranslation( Vector2( targetX, currentTranslation.Y ) );
		translationInterpolator.SetDuration( time );
		translationsAnimDef.AddInterpolator( translationInterpolator );
		m_lineTranslationAnimProxy = targetWidget.PlayAnimation( translationsAnimDef );
		m_lineTranslationAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnLineAnimationFinished' );
	}

	protected virtual function AnimateLineSize( targetWidget : weak< inkWidget >, targetWidth : Float, time : Float )
	{
		var sizeAnimDef : inkAnimDef;
		var sizeInterpolator : inkAnimSize;
		var currentSize : Vector2;
		m_lineSizeAnimProxy.Stop();
		currentSize = targetWidget.GetSize();
		sizeAnimDef = new inkAnimDef;
		sizeInterpolator = new inkAnimSize;
		sizeInterpolator.SetType( inkanimInterpolationType.Quartic );
		sizeInterpolator.SetMode( inkanimInterpolationMode.EasyInOut );
		sizeInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		sizeInterpolator.SetStartSize( targetWidget.GetSize() );
		sizeInterpolator.SetEndSize( Vector2( targetWidth, currentSize.Y ) );
		sizeInterpolator.SetDuration( time );
		sizeAnimDef.AddInterpolator( sizeInterpolator );
		m_lineSizeAnimProxy = targetWidget.PlayAnimation( sizeAnimDef );
	}

	protected event OnLineAnimationFinished( anim : inkAnimProxy )
	{
		m_animationsRetryDiv = 0.0;
	}

	protected event OnSelectionChanged( index : Int32, value : String )
	{
		var evt : OpenMenuRequest;
		if( m_currentIndex != index )
		{
			ScrollTo( m_currentData[ index ] );
			evt = new OpenMenuRequest;
			evt.m_eventData = m_currentData[ index ];
			evt.m_internal = true;
			evt.m_isMainMenu = false;
			QueueEvent( evt );
		}
	}

}

class hubRadialStaticSelectorController extends SelectorController
{
	protected editable var m_leftArrowWidget : inkWidgetRef;
	protected editable var m_rightArrowWidget : inkWidgetRef;
	protected editable var m_container : inkWidgetRef;
	protected editable var m_line : inkWidgetRef;
	protected var m_leftArrowController : weak< inkInputDisplayController >;
	protected var m_rightArrowController : weak< inkInputDisplayController >;
	protected var m_data : array< MenuData >;
	protected var m_widgetsControllers : array< weak< HubMenuLabelContentContainer > >;
	protected var m_currentIndex : Int32;
	protected var m_currentParent : Int32;
	private var m_currentData : array< MenuData >;
	private var m_lineTranslationAnimProxy : inkAnimProxy;
	private var m_lineSizeAnimProxy : inkAnimProxy;
	private var m_instantLineUpdateRequested : Bool;
	private var m_animationsRetryDiv : Float;
	private editable var m_debugText : inkTextRef;

	protected event OnInitialize()
	{
		m_leftArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_leftArrowWidget ) ) );
		m_rightArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_rightArrowWidget ) ) );
		RegisterToCallback( 'OnSelectionChanged', this, 'OnSelectionChanged' );
	}

	public function SetupWidgets( const data : ref< array< MenuData > > )
	{
		var i : Int32;
		var widget : weak< inkWidget >;
		var controller : HubMenuLabelContentContainer;
		( ( weak< weak< inkCompoundWidget > > )( inkWidgetRef.Get( m_container ) ) ).RemoveAllChildren();
		m_widgetsControllers.Clear();
		for( i = 0; i < data.Size(); i += 1 )
		{
			widget = SpawnFromLocal( inkWidgetRef.Get( m_container ), 'new_menu_element' );
			widget.SetRenderTransformPivot( 0.5, 1.0 );
			widget.SetAnchor( inkEAnchor.Centered );
			widget.SetAnchorPoint( 0.5, 0.5 );
			controller = ( ( HubMenuLabelContentContainer )( widget.GetController() ) );
			controller.RegisterToCallback( 'OnClick', this, 'OnMenuLabelClick' );
			controller.RegisterToCallback( 'OnHoverOver', this, 'OnMenuLabelHover' );
			controller.RegisterToCallback( 'OnHoverOut', this, 'OnMenuLabelHoverOut' );
			m_widgetsControllers.PushBack( controller );
		}
		for( i = 0; i < data.Size(); i += 1 )
		{
			m_widgetsControllers[ i ].SetData( data[ i ], true );
		}
		UpdateArrowsVisibility();
	}

	protected event OnMenuLabelClick( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		var evt : OpenMenuRequest;
		var data : MenuData;
		if( e.IsAction( 'select' ) )
		{
			controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
			PlaySound( 'TabButton', 'OnPress' );
			data = controller.GetData();
			ScrollTo( data );
			evt = new OpenMenuRequest;
			evt.m_eventData = data;
			evt.m_internal = true;
			evt.m_isMainMenu = false;
			QueueEvent( evt );
		}
	}

	public function SetupMenuValues( const data : ref< array< MenuData > >, const currentElement : ref< MenuData > )
	{
		var i : Int32;
		var startIndex : Int32;
		Clear();
		startIndex = 0;
		for( i = 0; i < data.Size(); i += 1 )
		{
			AddValue( data[ i ].label );
			if( ( data[ i ].identifier == currentElement.identifier ) || ( data[ i ].identifier == currentElement.parentIdentifier ) )
			{
				startIndex = i;
			}
		}
		m_currentIndex = startIndex;
		SetCurrIndex( startIndex );
		UpdateArrowsVisibility();
	}

	public function SetupMenu( const data : ref< array< MenuData > >, startIdentifier : Int32 )
	{
		m_data = data;
		SetupWidgets( data );
		UpdateArrowsVisibility();
	}

	private function UpdateArrowsVisibility()
	{
		m_leftArrowController.SetVisible( m_currentData.Size() > 1 );
		m_rightArrowController.SetVisible( m_currentData.Size() > 1 );
	}

	public function GetMenusByParent( identifier : Int32 ) : array< MenuData >
	{
		var result : array< MenuData >;
		var i, limit : Int32;
		for( i = 0, limit = m_data.Size(); i < limit; i += 1 )
		{
			if( m_data[ i ].parentIdentifier == identifier )
			{
				if( !( m_data[ i ].disabled ) )
				{
					result.PushBack( m_data[ i ] );
				}
			}
		}
		return result;
	}

	public function ScrollTo( const data : ref< MenuData > )
	{
		var i, limit : Int32;
		var startIndex : Int32;
		if( data.parentIdentifier != m_currentParent )
		{
			m_currentData = GetMenusByParent( data.parentIdentifier );
			SetupMenuValues( m_currentData, data );
			SetupWidgets( m_currentData );
			UpdateHightlight( m_currentIndex, true );
			m_currentParent = data.parentIdentifier;
			m_instantLineUpdateRequested = true;
		}
		else
		{
			startIndex = 0;
			for( i = 0, limit = m_currentData.Size(); i < limit; i += 1 )
			{
				if( ( m_currentData[ i ].identifier == data.identifier ) || ( m_currentData[ i ].parentIdentifier == data.identifier ) )
				{
					startIndex = i;
					break;
				}
			}
			m_currentIndex = startIndex;
			SetCurrIndex( startIndex );
			m_instantLineUpdateRequested = false;
			UpdateHightlight( m_currentIndex );
			UpdateArrowsVisibility();
		}
	}

	protected event OnArrangeChildrenComplete()
	{
		QueueEvent( new hubStaticSelectorPostArrangeEvent );
	}

	protected event OnPostArrange( e : hubStaticSelectorPostArrangeEvent )
	{
		if( m_instantLineUpdateRequested )
		{
			UpdateHightlight( m_currentIndex, true );
			m_instantLineUpdateRequested = false;
		}
	}

	protected virtual function UpdateLabelsStates( currentIndex : Int32 )
	{
		var i, limit : Int32;
		for( i = 0, limit = m_widgetsControllers.Size(); i < limit; i += 1 )
		{
			m_widgetsControllers[ i ].SetTextState( ( ( i == currentIndex ) ? ( 'Selected' ) : ( 'Default' ) ) );
		}
	}

	protected virtual function UpdateHightlight( index : Int32, optional instant : Bool )
	{
		var width : Float;
		var position : Vector2;
		var time : Float;
		width = m_widgetsControllers[ index ].GetWidth() + 2.0;
		position = ( ( weak< weak< inkCompoundWidget > > )( inkWidgetRef.Get( m_container ) ) ).GetChildPosition( m_widgetsControllers[ index ].GetRootWidget() );
		UpdateLabelsStates( index );
		if( instant )
		{
			m_animationsRetryDiv = 0.0;
			m_lineTranslationAnimProxy.Stop();
			m_lineSizeAnimProxy.Stop();
			inkWidgetRef.SetTranslation( m_line, Vector2( position.X, 0.0 ) );
			inkWidgetRef.SetSize( m_line, Vector2( width, inkWidgetRef.GetHeight( m_line ) ) );
		}
		else
		{
			m_animationsRetryDiv += 0.5;
			time = 0.44999999 / MaxF( m_animationsRetryDiv, 1.0 );
			time = MaxF( time, 0.2 );
			AnimateLineTranslation( inkWidgetRef.Get( m_line ), position.X, time );
			AnimateLineSize( inkWidgetRef.Get( m_line ), width, time );
		}
		inkTextRef.SetText( m_debugText, FloatToString( m_animationsRetryDiv ) );
	}

	protected virtual function AnimateLineTranslation( targetWidget : weak< inkWidget >, targetX : Float, time : Float )
	{
		var translationsAnimDef : inkAnimDef;
		var translationInterpolator : inkAnimTranslation;
		var currentTranslation : Vector2;
		m_lineTranslationAnimProxy.Stop();
		currentTranslation = targetWidget.GetTranslation();
		translationsAnimDef = new inkAnimDef;
		translationInterpolator = new inkAnimTranslation;
		translationInterpolator.SetType( inkanimInterpolationType.Quartic );
		translationInterpolator.SetMode( inkanimInterpolationMode.EasyInOut );
		translationInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		translationInterpolator.SetStartTranslation( targetWidget.GetTranslation() );
		translationInterpolator.SetEndTranslation( Vector2( targetX, currentTranslation.Y ) );
		translationInterpolator.SetDuration( time );
		translationsAnimDef.AddInterpolator( translationInterpolator );
		m_lineTranslationAnimProxy = targetWidget.PlayAnimation( translationsAnimDef );
		m_lineTranslationAnimProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnLineAnimationFinished' );
	}

	protected virtual function AnimateLineSize( targetWidget : weak< inkWidget >, targetWidth : Float, time : Float )
	{
		var sizeAnimDef : inkAnimDef;
		var sizeInterpolator : inkAnimSize;
		var currentSize : Vector2;
		m_lineSizeAnimProxy.Stop();
		currentSize = targetWidget.GetSize();
		sizeAnimDef = new inkAnimDef;
		sizeInterpolator = new inkAnimSize;
		sizeInterpolator.SetType( inkanimInterpolationType.Quartic );
		sizeInterpolator.SetMode( inkanimInterpolationMode.EasyInOut );
		sizeInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		sizeInterpolator.SetStartSize( targetWidget.GetSize() );
		sizeInterpolator.SetEndSize( Vector2( targetWidth, currentSize.Y ) );
		sizeInterpolator.SetDuration( time );
		sizeAnimDef.AddInterpolator( sizeInterpolator );
		m_lineSizeAnimProxy = targetWidget.PlayAnimation( sizeAnimDef );
	}

	protected event OnLineAnimationFinished( anim : inkAnimProxy )
	{
		m_animationsRetryDiv = 0.0;
	}

	protected event OnSelectionChanged( index : Int32, value : String )
	{
		var evt : OpenMenuRequest;
		if( m_currentIndex != index )
		{
			ScrollTo( m_currentData[ index ] );
			evt = new OpenMenuRequest;
			evt.m_eventData = m_currentData[ index ];
			evt.m_internal = true;
			evt.m_isMainMenu = false;
			QueueEvent( evt );
		}
	}

}

class hubStaticSelectorPostArrangeEvent extends Event
{
}

class hubSelectorSingleSmallCarouselController extends hubSelectorSingleCarouselController
{
	default NUMBER_OF_WIDGETS = 5;
}

class hubSelectorSingleCarouselController extends SelectorController
{
	protected var NUMBER_OF_WIDGETS : Int32;
	default NUMBER_OF_WIDGETS = 7;
	protected var WIDGETS_PADDING : Float;
	default WIDGETS_PADDING = 10.0f;
	protected var SMALL_WIDGET_SCALE : Float;
	default SMALL_WIDGET_SCALE = 0.8f;
	protected var SMALL_WIDGET_OPACITY : Float;
	default SMALL_WIDGET_OPACITY = 1.0f;
	protected var ANIMATION_TIME : Float;
	default ANIMATION_TIME = 0.2f;
	protected var DEFAULT_WIDGET_COLOR : HDRColor;
	protected var SELECTED_WIDGET_COLOR : HDRColor;
	protected editable var m_leftArrowWidget : inkWidgetRef;
	protected editable var m_rightArrowWidget : inkWidgetRef;
	protected editable var m_container : inkWidgetRef;
	protected editable var m_defaultColorDummy : inkWidgetRef;
	protected editable var m_activeColorDummy : inkWidgetRef;
	var m_leftArrowController : weak< inkInputDisplayController >;
	var m_rightArrowController : weak< inkInputDisplayController >;
	protected var m_elements : array< MenuData >;
	protected var m_centerElementIndex : Int32;
	protected var m_widgetsControllers : array< weak< HubMenuLabelContentContainer > >;
	protected var m_waitForSizes : Bool;
	protected var m_translationOnce : Bool;
	protected var m_currentIndex : Int32;
	protected var m_activeAnimations : array< inkAnimProxy >;

	protected event OnInitialize()
	{
		var i : Int32;
		var widget : weak< inkWidget >;
		var controller : HubMenuLabelContentContainer;
		m_leftArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_leftArrowWidget ) ) );
		m_rightArrowController = ( ( inkInputDisplayController )( inkWidgetRef.GetController( m_rightArrowWidget ) ) );
		DEFAULT_WIDGET_COLOR = inkWidgetRef.GetTintColor( m_defaultColorDummy );
		SELECTED_WIDGET_COLOR = inkWidgetRef.GetTintColor( m_activeColorDummy );
		m_centerElementIndex = NUMBER_OF_WIDGETS / 2;
		for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
		{
			widget = SpawnFromLocal( inkWidgetRef.Get( m_container ), 'menu_element' );
			widget.SetRenderTransformPivot( 0.5, 1.0 );
			widget.SetAnchor( inkEAnchor.Centered );
			widget.SetAnchorPoint( 0.5, 0.5 );
			controller = ( ( HubMenuLabelContentContainer )( widget.GetController() ) );
			controller.RegisterToCallback( 'OnRelease', this, 'OnMenuLabelClick' );
			controller.RegisterToCallback( 'OnHoverOver', this, 'OnMenuLabelHover' );
			controller.RegisterToCallback( 'OnHoverOut', this, 'OnMenuLabelHoverOut' );
			controller.SetCarouselPosition( i );
			if( i == m_centerElementIndex )
			{
				controller.SetInteractive( false );
			}
			m_widgetsControllers.PushBack( controller );
		}
		UpdateArrowsVisibility();
	}

	protected event OnMenuLabelClick( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		var direction : inkSelectorChangeDirection;
		var targetPosition : Int32;
		if( e.IsAction( 'select' ) )
		{
			controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
			if( controller.GetCarouselPosition() != m_centerElementIndex )
			{
				PlaySound( 'TabButton', 'OnPress' );
				targetPosition = m_centerElementIndex - controller.GetCarouselPosition();
				if( controller.GetCarouselPosition() < m_centerElementIndex )
				{
					direction = inkSelectorChangeDirection.Prior;
				}
				else if( controller.GetCarouselPosition() > m_centerElementIndex )
				{
					direction = inkSelectorChangeDirection.Next;
				}
				SetCurrIndexWithDirection( GetLoopedValue( m_currentIndex - targetPosition, GetValuesCount() ), direction );
			}
		}
	}

	protected event OnMenuLabelHover( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
		controller.SetTintColor( SELECTED_WIDGET_COLOR );
	}

	protected event OnMenuLabelHoverOut( e : inkPointerEvent )
	{
		var controller : HubMenuLabelContentContainer;
		controller = ( ( HubMenuLabelContentContainer )( e.GetCurrentTarget().GetController() ) );
		controller.SetTintColor( DEFAULT_WIDGET_COLOR );
	}

	public function SetupMenu( const data : ref< array< MenuData > >, startIdentifier : Int32 )
	{
		var i : Int32;
		var startIndex : Int32;
		Clear();
		m_elements.Clear();
		startIndex = 0;
		for( i = 0; i < data.Size(); i += 1 )
		{
			m_elements.PushBack( data[ i ] );
			AddValue( data[ i ].label );
			if( data[ i ].identifier == startIdentifier )
			{
				startIndex = i;
			}
		}
		SetCurrIndex( startIndex );
		SetFinishedValues( startIndex );
		m_currentIndex = startIndex;
		UpdateArrowsVisibility();
	}

	public function ScrollTo( const data : ref< MenuData > )
	{
		var i : Int32;
		var startIndex : Int32;
		startIndex = 0;
		for( i = 0; i < m_elements.Size(); i += 1 )
		{
			if( m_elements[ i ].identifier == data.identifier )
			{
				startIndex = i;
			}
		}
		SetCurrIndex( startIndex );
		SetFinishedValues( startIndex );
		m_currentIndex = startIndex;
		UpdateArrowsVisibility();
	}

	private function UpdateArrowsVisibility()
	{
		m_leftArrowController.SetVisible( m_elements.Size() > 1 );
		m_rightArrowController.SetVisible( m_elements.Size() > 1 );
	}

	protected event OnUpdateValue( value : String, index : Int32, changeDirection : inkSelectorChangeDirection )
	{
		m_currentIndex = index;
		Animate( index, changeDirection );
	}

	protected function GetLoopedValue( value : Int32, limit : Int32 ) : Int32
	{
		return ( ( value >= 0 ) ? ( value % limit ) : ( ( limit + ( ( value + 1 ) % limit ) ) - 1 ) );
	}

	protected function SetFinishedValues( selectedIndex : Int32 )
	{
		var i, limit, realIndex : Int32;
		var emptyData : MenuData;
		limit = GetValuesCount();
		if( limit == 1 )
		{
			m_widgetsControllers[ m_centerElementIndex ].SetData( m_elements[ 0 ] );
			for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
			{
				if( i != m_centerElementIndex )
				{
					m_widgetsControllers[ i ].SetData( emptyData );
				}
			}
		}
		else
		{
			for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
			{
				realIndex = GetLoopedValue( ( selectedIndex - ( NUMBER_OF_WIDGETS / 2 ) ) + i, limit );
				m_widgetsControllers[ i ].SetData( m_elements[ realIndex ] );
			}
		}
		m_waitForSizes = true;
	}

	protected event OnArrangeChildrenComplete()
	{
		var i : Int32;
		if( m_waitForSizes )
		{
			for( i = 0; i < m_activeAnimations.Size(); i += 1 )
			{
				m_activeAnimations[ i ].Stop();
			}
			m_activeAnimations.Clear();
			ResetAnimatedStates();
			m_waitForSizes = false;
		}
	}

	protected function GetTranslations( targetIndex : Int32 ) : array< Float >
	{
		var resultTranslations : array< Float >;
		var i : Int32;
		var prevSidesMaxWidth, sidesMaxWidth, sidesTranslation : Float;
		for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
		{
			resultTranslations.PushBack( 0.0 );
		}
		targetIndex = m_centerElementIndex + ( ( ( targetIndex != 0 ) ) ? ( ( ( ( targetIndex > 0 ) ) ? ( 1 ) : ( -1 ) ) ) : ( 0 ) );
		sidesTranslation = ( m_widgetsControllers[ targetIndex ].GetRealDesiredWidth() / 2.0 ) + WIDGETS_PADDING;
		for( i = 1; i <= ( ( NUMBER_OF_WIDGETS / 2 ) + 1 ); i += 1 )
		{
			sidesMaxWidth = MaxF( m_widgetsControllers[ targetIndex - i ].GetRealDesiredWidth(), m_widgetsControllers[ targetIndex + i ].GetRealDesiredWidth() );
			sidesTranslation += ( ( ( prevSidesMaxWidth / 2.0 ) + ( sidesMaxWidth / 2.0 ) ) + WIDGETS_PADDING );
			resultTranslations[ targetIndex - i ] = -( sidesTranslation );
			resultTranslations[ targetIndex + i ] = sidesTranslation;
			prevSidesMaxWidth = sidesMaxWidth;
		}
		return resultTranslations;
	}

	protected function GetMaskTargetWidth( targetIndex : Int32 ) : Float
	{
		var i : Int32;
		var nonZeroPrevSidesMaxWidth, prevSidesMaxWidth, sidesMaxWidth, sidesTranslation : Float;
		targetIndex = m_centerElementIndex + ( ( ( targetIndex != 0 ) ) ? ( ( ( ( targetIndex > 0 ) ) ? ( 1 ) : ( -1 ) ) ) : ( 0 ) );
		sidesTranslation = ( m_widgetsControllers[ targetIndex ].GetRealDesiredWidth() / 2.0 ) + WIDGETS_PADDING;
		for( i = 1; i <= ( ( NUMBER_OF_WIDGETS / 2 ) + 1 ); i += 1 )
		{
			sidesMaxWidth = MaxF( m_widgetsControllers[ targetIndex - i ].GetRealDesiredWidth(), m_widgetsControllers[ targetIndex + i ].GetRealDesiredWidth() );
			sidesTranslation += ( ( ( prevSidesMaxWidth / 2.0 ) + ( sidesMaxWidth / 2.0 ) ) + WIDGETS_PADDING );
			nonZeroPrevSidesMaxWidth = prevSidesMaxWidth;
			prevSidesMaxWidth = sidesMaxWidth;
		}
		return ( sidesTranslation - ( nonZeroPrevSidesMaxWidth + ( WIDGETS_PADDING * 2.0 ) ) ) * 2.0;
	}

	protected function ResetAnimatedStates()
	{
		var i : Int32;
		var translations : array< Float >;
		var widgetLeft, widgetRight : HubMenuLabelContentContainer;
		var sidesMaxWidth : Float;
		for( i = 1; i <= ( NUMBER_OF_WIDGETS / 2 ); i += 1 )
		{
			widgetLeft = m_widgetsControllers[ m_centerElementIndex - i ];
			widgetRight = m_widgetsControllers[ m_centerElementIndex + i ];
			sidesMaxWidth = MaxF( widgetLeft.GetRealDesiredWidth(), widgetRight.GetRealDesiredWidth() );
			widgetLeft.GetRootWidget().SetWidth( sidesMaxWidth );
			widgetRight.GetRootWidget().SetWidth( sidesMaxWidth );
		}
		inkWidgetRef.Get( m_container ).SetSize( Vector2( GetMaskTargetWidth( 0 ), inkWidgetRef.GetHeight( m_container ) ) );
		for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
		{
			if( i == m_centerElementIndex )
			{
				m_widgetsControllers[ i ].GetRootWidget().SetScale( Vector2( 1.0, 1.0 ) );
				m_widgetsControllers[ i ].GetRootWidget().SetOpacity( 1.0 );
				m_widgetsControllers[ i ].SetTintColor( SELECTED_WIDGET_COLOR );
			}
			else
			{
				m_widgetsControllers[ i ].GetRootWidget().SetScale( Vector2( SMALL_WIDGET_SCALE, SMALL_WIDGET_SCALE ) );
				m_widgetsControllers[ i ].GetRootWidget().SetOpacity( SMALL_WIDGET_OPACITY );
				m_widgetsControllers[ i ].SetTintColor( DEFAULT_WIDGET_COLOR );
			}
		}
		translations = GetTranslations( 0 );
		for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
		{
			m_widgetsControllers[ i ].GetRootWidget().SetTranslation( translations[ i ], 0.0 );
		}
	}

	protected function Animate( targetIndex : Int32, direction : inkSelectorChangeDirection )
	{
		var i, offset : Int32;
		var prevVector : Vector2;
		var translations : array< Float >;
		offset = 0;
		if( direction == inkSelectorChangeDirection.Prior )
		{
			offset = -1;
		}
		else if( direction == inkSelectorChangeDirection.Next )
		{
			offset = 1;
		}
		translations = GetTranslations( offset );
		for( i = 0; i < NUMBER_OF_WIDGETS; i += 1 )
		{
			prevVector = m_widgetsControllers[ i ].GetRootWidget().GetTranslation();
			AddActiveProxy( TranslationAnimation( m_widgetsControllers[ i ].GetRootWidget(), prevVector.X, translations[ i ] ) );
		}
		AddActiveProxy( ScaleAnimation( m_widgetsControllers[ m_centerElementIndex ].GetRootWidget(), 1.0, SMALL_WIDGET_SCALE ) );
		AddActiveProxy( OpacityAnimation( m_widgetsControllers[ m_centerElementIndex ].GetRootWidget(), 1.0, SMALL_WIDGET_OPACITY ) );
		AddActiveProxy( ColorAnimation( m_widgetsControllers[ m_centerElementIndex ].GetTintedWidgets(), SELECTED_WIDGET_COLOR, DEFAULT_WIDGET_COLOR ) );
		AddActiveProxy( ScaleAnimation( m_widgetsControllers[ m_centerElementIndex + offset ].GetRootWidget(), SMALL_WIDGET_SCALE, 1.0 ) );
		AddActiveProxy( OpacityAnimation( m_widgetsControllers[ m_centerElementIndex + offset ].GetRootWidget(), SMALL_WIDGET_OPACITY, 1.0 ) );
		AddActiveProxy( ColorAnimation( m_widgetsControllers[ m_centerElementIndex + offset ].GetTintedWidgets(), DEFAULT_WIDGET_COLOR, SELECTED_WIDGET_COLOR ) );
		m_translationOnce = true;
	}

	protected function AddActiveProxy( proxy : inkAnimProxy )
	{
		m_activeAnimations.PushBack( proxy );
	}

	protected function AddActiveProxy( const proxies : ref< array< inkAnimProxy > > )
	{
		var i : Int32;
		for( i = 0; i < proxies.Size(); i += 1 )
		{
			m_activeAnimations.PushBack( proxies[ i ] );
		}
	}

	protected virtual function ScaleAnimation( targetWidget : weak< inkWidget >, startScale : Float, endScale : Float ) : inkAnimProxy
	{
		var proxy : inkAnimProxy;
		var moveElementsAnimDef : inkAnimDef;
		var scaleInterpolator : inkAnimScale;
		moveElementsAnimDef = new inkAnimDef;
		scaleInterpolator = new inkAnimScale;
		scaleInterpolator.SetType( inkanimInterpolationType.Linear );
		scaleInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		scaleInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		scaleInterpolator.SetStartScale( Vector2( startScale, startScale ) );
		scaleInterpolator.SetEndScale( Vector2( endScale, endScale ) );
		scaleInterpolator.SetDuration( ANIMATION_TIME );
		moveElementsAnimDef.AddInterpolator( scaleInterpolator );
		proxy = targetWidget.PlayAnimation( moveElementsAnimDef );
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnScaleCompleted' );
		return proxy;
	}

	protected virtual function TranslationAnimation( targetWidget : weak< inkWidget >, startTranslation : Float, endTranslation : Float ) : inkAnimProxy
	{
		var proxy : inkAnimProxy;
		var moveElementsAnimDef : inkAnimDef;
		var translationInterpolator : inkAnimTranslation;
		moveElementsAnimDef = new inkAnimDef;
		translationInterpolator = new inkAnimTranslation;
		translationInterpolator.SetType( inkanimInterpolationType.Linear );
		translationInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		translationInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		translationInterpolator.SetStartTranslation( Vector2( startTranslation, 0.0 ) );
		translationInterpolator.SetEndTranslation( Vector2( endTranslation, 0.0 ) );
		translationInterpolator.SetDuration( ANIMATION_TIME );
		moveElementsAnimDef.AddInterpolator( translationInterpolator );
		proxy = targetWidget.PlayAnimation( moveElementsAnimDef );
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnTranslationCompleted' );
		return proxy;
	}

	protected virtual function SizeAnimation( targetWidget : weak< inkWidget >, startSize : Vector2, endSize : Vector2 ) : inkAnimProxy
	{
		var proxy : inkAnimProxy;
		var moveElementsAnimDef : inkAnimDef;
		var sizeInterpolator : inkAnimSize;
		moveElementsAnimDef = new inkAnimDef;
		sizeInterpolator = new inkAnimSize;
		sizeInterpolator.SetType( inkanimInterpolationType.Linear );
		sizeInterpolator.SetMode( inkanimInterpolationMode.EasyOut );
		sizeInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		sizeInterpolator.SetStartSize( startSize );
		sizeInterpolator.SetEndSize( endSize );
		sizeInterpolator.SetDuration( ANIMATION_TIME * 1.5 );
		moveElementsAnimDef.AddInterpolator( sizeInterpolator );
		proxy = targetWidget.PlayAnimation( moveElementsAnimDef );
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnSizeCompleted' );
		return proxy;
	}

	protected virtual function OpacityAnimation( targetWidget : weak< inkWidget >, startOpacity : Float, endOpacity : Float ) : inkAnimProxy
	{
		var proxy : inkAnimProxy;
		var moveElementsAnimDef : inkAnimDef;
		var transparencyInterpolator : inkAnimTransparency;
		moveElementsAnimDef = new inkAnimDef;
		transparencyInterpolator = new inkAnimTransparency;
		transparencyInterpolator.SetType( inkanimInterpolationType.Linear );
		transparencyInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		transparencyInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		transparencyInterpolator.SetStartTransparency( startOpacity );
		transparencyInterpolator.SetEndTransparency( endOpacity );
		transparencyInterpolator.SetDuration( ANIMATION_TIME );
		moveElementsAnimDef.AddInterpolator( transparencyInterpolator );
		proxy = targetWidget.PlayAnimation( moveElementsAnimDef );
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnOpacityCompleted' );
		return proxy;
	}

	protected virtual function ColorAnimation( const targetWidgets : ref< array< weak< inkWidget > > >, startColor : HDRColor, endColor : HDRColor ) : array< inkAnimProxy >
	{
		var i : Int32;
		var proxies : array< inkAnimProxy >;
		var proxy : inkAnimProxy;
		var colorElementsAnimDef : inkAnimDef;
		var colorInterpolator : inkAnimColor;
		colorElementsAnimDef = new inkAnimDef;
		colorInterpolator = new inkAnimColor;
		colorInterpolator.SetType( inkanimInterpolationType.Linear );
		colorInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		colorInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		colorInterpolator.SetStartColor( startColor );
		colorInterpolator.SetEndColor( endColor );
		colorInterpolator.SetDuration( ANIMATION_TIME );
		colorElementsAnimDef.AddInterpolator( colorInterpolator );
		for( i = 0; i < targetWidgets.Size(); i += 1 )
		{
			proxy = targetWidgets[ i ].PlayAnimation( colorElementsAnimDef );
			proxies.PushBack( proxy );
		}
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnColorCompleted' );
		return proxies;
	}

	protected event OnTranslationCompleted( anim : inkAnimProxy )
	{
		if( m_translationOnce )
		{
			SetFinishedValues( m_currentIndex );
			ResetAnimatedStates();
			m_translationOnce = false;
		}
	}

}

class hubSelectorController extends SelectorController
{
	editable var m_leftArrowWidget : inkWidgetRef;
	editable var m_rightArrowWidget : inkWidgetRef;
	editable var m_menuLabelHolder : inkHorizontalPanelRef;
	var m_selectedMenuLabel : weak< HubMenuLabelController >;
	private var m_previouslySelectedMenuLabel : weak< HubMenuLabelController >;
	private var hubElementsData : array< MenuData >;
	private var m_previousIndex : Int32;

	protected event OnInitialize()
	{
		inkWidgetRef.RegisterToCallback( m_leftArrowWidget, 'OnRelease', this, 'OnLeft' );
		inkWidgetRef.RegisterToCallback( m_rightArrowWidget, 'OnRelease', this, 'OnRight' );
	}

	public function AddMenuTab( const data : ref< MenuData > )
	{
		var childrenNum, selectedIndex : Int32;
		var menuLabelController : HubMenuLabelController;
		var widget : weak< inkWidget >;
		childrenNum = inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder );
		if( inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder ) < 3 )
		{
			widget = SpawnFromLocal( inkHorizontalPanelRef.Get( m_menuLabelHolder ), 'menu_label' );
			if( !( widget ) )
			{
				widget = SpawnFromExternal( inkHorizontalPanelRef.Get( m_menuLabelHolder ), R"base\gameplay\gui\fullscreen\hub_menu\new_hub.inkwidget", 'menu_label' );
			}
			menuLabelController = ( ( HubMenuLabelController )( widget.GetController() ) );
			menuLabelController.SetData( data );
			menuLabelController.RegisterToCallback( 'OnRelease', this, 'OnMenuLabelClick' );
		}
		m_selectedMenuLabel.SetActive( false );
		selectedIndex = CeilF( ( ( Float )( ( childrenNum - 1 ) ) ) / 2.0 );
		m_selectedMenuLabel = ( ( HubMenuLabelController )( inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, selectedIndex ).GetController() ) );
		m_selectedMenuLabel.SetActive( true );
		hubElementsData.PushBack( data );
	}

	public function RemoveOldTabs()
	{
		hubElementsData.Clear();
		inkHorizontalPanelRef.RemoveAllChildren( m_menuLabelHolder );
	}

	public function RegisterToMenuTabCallback( eventName : CName, object : IScriptable, functionName : CName )
	{
		var widget : weak< inkWidget >;
		var i : Int32;
		for( i = 0; i < inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder ); i += 1 )
		{
			widget = inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, i );
			widget.RegisterToCallback( eventName, object, functionName );
		}
	}

	protected function CycleInRange( index : Int32, range : Int32 ) : Int32
	{
		if( index > 0 )
		{
			return index % range;
		}
		else if( index < 0 )
		{
			return range + ( index % range );
		}
		return 0;
	}

	protected function GetNearestWidgetsData( index : Int32 ) : array< MenuData >
	{
		var result : array< MenuData >;
		var i : Int32;
		for( i = -1; i <= 1; i += 1 )
		{
			result.PushBack( hubElementsData[ CycleInRange( index + i, GetValuesCount() ) ] );
		}
		return result;
	}

	protected event OnUpdateValue( value : String, index : Int32, changeDirection : inkSelectorChangeDirection )
	{
		var menuLabelController : HubMenuLabelController;
		var i, idx, valuesSize, relativeChange : Int32;
		idx = GetCurrIndex();
		valuesSize = GetValuesCount();
		relativeChange = idx - m_previousIndex;
		m_previousIndex = idx;
		if( ( relativeChange == ( valuesSize - 1 ) ) || ( ( relativeChange * -1 ) == ( valuesSize - 1 ) ) )
		{
			relativeChange *= -1;
		}
		if( valuesSize <= 1 )
		{
			( ( HubMenuLabelController )( inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, i + 1 ).GetController() ) ).SetData( hubElementsData[ idx ] );
		}
		else
		{
			for( i = ( ( valuesSize > 2 ) ? ( -1 ) : ( 0 ) ); i <= 1; i += 1 )
			{
				menuLabelController = ( ( HubMenuLabelController )( inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, i + ( ( ( valuesSize > 2 ) ) ? ( 1 ) : ( 0 ) ) ).GetController() ) );
				menuLabelController.SetTargetData( hubElementsData[ CycleInRange( idx + i, valuesSize ) ], relativeChange );
				if( i == 0 )
				{
					menuLabelController.SetActive( true );
				}
			}
		}
	}

	protected event OnLeft( e : inkPointerEvent )
	{
		if( e.IsAction( 'click' ) )
		{
			Prior();
		}
	}

	protected event OnRight( e : inkPointerEvent )
	{
		if( e.IsAction( 'click' ) )
		{
			Next();
		}
	}

	protected event OnMenuLabelClick( e : inkPointerEvent )
	{
		var target : weak< inkWidget >;
		var logicController : HubMenuLabelController;
		var clickedIndex, visibleElements : Int32;
		if( e.IsAction( 'click' ) )
		{
			target = e.GetTarget();
			logicController = ( ( HubMenuLabelController )( target.GetController() ) );
			clickedIndex = DetermineIndex( logicController );
			visibleElements = inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder );
			if( visibleElements > 1 )
			{
				if( clickedIndex == ( visibleElements - 1 ) )
				{
					Next();
				}
				else if( clickedIndex == 0 )
				{
					Prior();
				}
			}
		}
	}

	private function FindLabel( const label : ref< String > ) : HubMenuLabelController
	{
		var selectedLabel : weak< HubMenuLabelController >;
		var i : Int32;
		for( i = 0; i < inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder ); i += 1 )
		{
			selectedLabel = ( ( HubMenuLabelController )( inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, i ).GetController() ) );
			if( selectedLabel.m_data.label == label )
			{
				return selectedLabel;
				break;
			}
		}
		return NULL;
	}

	private function DetermineIndex( controller : HubMenuLabelController ) : Int32
	{
		var selectedLabel : weak< HubMenuLabelController >;
		var i : Int32;
		for( i = 0; i < inkHorizontalPanelRef.GetNumChildren( m_menuLabelHolder ); i += 1 )
		{
			selectedLabel = ( ( HubMenuLabelController )( inkHorizontalPanelRef.GetWidgetByIndex( m_menuLabelHolder, i ).GetController() ) );
			if( selectedLabel == controller )
			{
				return i;
			}
		}
		return i;
	}

}

class HubMenuLabelContentContainer extends inkLogicController
{
	protected editable var m_label : inkTextRef;
	protected editable var m_icon : inkImageRef;
	protected editable var m_desiredSizeWrapper : inkWidgetRef;
	protected editable var m_border : inkBorderRef;
	protected editable var m_line : inkWidgetRef;
	protected var m_carouselPosition : Int32;
	var m_labelName : String;
	var m_data : MenuData;
	var m_isRadialVariant : Bool;

	protected event OnInitialize()
	{
		inkBorderRef.SetVisible( m_border, false );
		inkImageRef.SetVisible( m_icon, false );
	}

	public function SetData( const data : ref< MenuData >, isRadialVariant : Bool )
	{
		m_isRadialVariant = isRadialVariant;
		SetData( data );
	}

	public virtual function SetData( const data : ref< MenuData > )
	{
		m_data = data;
		m_labelName = m_data.label;
		inkTextRef.SetText( m_label, m_labelName );
		inkTextRef.SetLocalizedTextScript( m_label, StringToName( m_labelName ) );
		if( m_data.icon != '' )
		{
			inkImageRef.SetVisible( m_icon, true );
			inkImageRef.SetTexturePart( m_icon, m_data.icon );
		}
		else
		{
			inkImageRef.SetVisible( m_icon, false );
		}
	}

	public virtual function GetData() : MenuData
	{
		return m_data;
	}

	public virtual function SetCarouselPosition( carouselPosition : Int32 )
	{
		m_carouselPosition = carouselPosition;
	}

	public virtual function SetInteractive( value : Bool )
	{
		inkWidgetRef.SetInteractive( m_desiredSizeWrapper, value );
	}

	public virtual function GetCarouselPosition() : Int32
	{
		return m_carouselPosition;
	}

	public virtual function GetIdentifier() : Int32
	{
		return m_data.identifier;
	}

	public virtual function GetSize() : Float
	{
		return GetRootWidget().GetWidth();
	}

	public virtual function GetRealDesiredSize() : Vector2
	{
		return inkWidgetRef.GetDesiredSize( m_desiredSizeWrapper );
	}

	public virtual function GetRealDesiredWidth() : Float
	{
		return inkWidgetRef.GetDesiredWidth( m_desiredSizeWrapper );
	}

	public virtual function GetTintedWidgets() : array< weak< inkWidget > >
	{
		var result : array< weak< inkWidget > >;
		result.PushBack( inkTextRef.Get( m_label ) );
		result.PushBack( inkImageRef.Get( m_icon ) );
		return result;
	}

	public virtual function SetTintColor( color : HDRColor )
	{
		inkTextRef.SetTintColor( m_label, color );
		inkImageRef.SetTintColor( m_icon, color );
	}

	public virtual function GetWidth() : Float
	{
		if( m_isRadialVariant )
		{
			return inkTextRef.GetDesiredWidth( m_label );
		}
		return GetRootWidget().GetDesiredWidth();
	}

	public function SetTextState( state : CName )
	{
		GetRootWidget().SetState( state );
	}

}

class HubMenuLabelController extends inkLogicController
{
	protected editable var m_container : inkCompoundRef;
	protected var m_wrapper : weak< inkWidget >;
	protected var m_wrapperNext : weak< inkWidget >;
	protected var m_wrapperController : weak< HubMenuLabelContentContainer >;
	protected var m_wrapperNextController : weak< HubMenuLabelContentContainer >;
	var m_data : MenuData;
	protected var m_watchForSize : Bool;
	protected var m_watchForInstatnSize : Bool;
	protected var m_once : Bool;
	protected var m_direction : Int32;
	protected var m_isRadialVariant : Bool;

	protected event OnInitialize()
	{
		m_wrapper = SpawnFromLocal( inkCompoundRef.Get( m_container ), 'menu_label_content' );
		if( !( m_wrapper ) )
		{
			m_wrapper = SpawnFromExternal( inkCompoundRef.Get( m_container ), R"base\gameplay\gui\fullscreen\hub_menu\new_hub.inkwidget", 'menu_label_content' );
		}
		m_wrapper.SetAnchor( inkEAnchor.Fill );
		m_wrapper.SetTranslation( 0.0, 0.0 );
		m_wrapperNext = SpawnFromLocal( inkCompoundRef.Get( m_container ), 'menu_label_content' );
		if( !( m_wrapperNext ) )
		{
			m_wrapperNext = SpawnFromExternal( inkCompoundRef.Get( m_container ), R"base\gameplay\gui\fullscreen\hub_menu\new_hub.inkwidget", 'menu_label_content' );
		}
		m_wrapperNext.SetAnchor( inkEAnchor.Fill );
		m_wrapperNext.SetTranslation( 400.0, 0.0 );
		m_wrapperController = ( ( HubMenuLabelContentContainer )( m_wrapper.GetController() ) );
		m_wrapperNextController = ( ( HubMenuLabelContentContainer )( m_wrapperNext.GetController() ) );
	}

	public function SetData( const data : ref< MenuData >, isRadialVariant : Bool )
	{
		m_isRadialVariant = isRadialVariant;
		SetData( data );
	}

	public virtual function SetData( const data : ref< MenuData > )
	{
		m_data = data;
		m_watchForInstatnSize = true;
		m_wrapperController.SetData( data, m_isRadialVariant );
	}

	public virtual function SetTargetData( const data : ref< MenuData >, direction : Int32 )
	{
		m_data = data;
		m_direction = direction;
		if( direction != 0 )
		{
			m_watchForSize = true;
			m_wrapperNextController.SetData( data );
		}
		else
		{
			m_watchForInstatnSize = true;
			m_wrapperController.SetData( data );
		}
	}

	public virtual function SetActive( active : Bool )
	{
		inkCompoundRef.SetState( m_container, ( ( active ) ? ( 'Selected' ) : ( 'Unselected' ) ) );
	}

	protected event OnArrangeChildrenComplete()
	{
		var desiredWidth : Float;
		if( m_watchForSize )
		{
			desiredWidth = m_wrapperNext.GetDesiredWidth();
			ResizeAnimation( inkCompoundRef.Get( m_container ), desiredWidth );
			SwipeAnimation( m_wrapper, 0.0, ( ( m_direction > 0 ) ? ( -( desiredWidth ) ) : ( desiredWidth ) ) );
			SwipeAnimation( m_wrapperNext, ( ( m_direction > 0 ) ? ( desiredWidth ) : ( -( desiredWidth ) ) ), 0.0 );
			m_once = true;
			m_watchForSize = false;
		}
		if( m_watchForInstatnSize )
		{
			inkCompoundRef.SetSize( m_container, m_wrapper.GetDesiredSize() );
			m_watchForInstatnSize = false;
		}
	}

	protected event OnSwipeCompleted( anim : inkAnimProxy )
	{
		if( m_once )
		{
			m_wrapperController.SetData( m_data );
			m_once = false;
		}
	}

	protected virtual function SwipeAnimation( targetWidget : weak< inkWidget >, startTranslation : Float, endTranslation : Float ) : inkAnimDef
	{
		var proxy : inkAnimProxy;
		var moveElementsAnimDef : inkAnimDef;
		var translationInterpolator : inkAnimTranslation;
		moveElementsAnimDef = new inkAnimDef;
		translationInterpolator = new inkAnimTranslation;
		translationInterpolator.SetType( inkanimInterpolationType.Linear );
		translationInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		translationInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		translationInterpolator.SetStartTranslation( Vector2( startTranslation, 0.0 ) );
		translationInterpolator.SetEndTranslation( Vector2( endTranslation, 0.0 ) );
		translationInterpolator.SetDuration( 0.30000001 );
		moveElementsAnimDef.AddInterpolator( translationInterpolator );
		proxy = targetWidget.PlayAnimation( moveElementsAnimDef );
		proxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnSwipeCompleted' );
		return moveElementsAnimDef;
	}

	protected virtual function ResizeAnimation( targetWidget : weak< inkWidget >, width : Float ) : inkAnimDef
	{
		var resizeElementsAnimDef : inkAnimDef;
		var sizeInterpolator : inkAnimSize;
		resizeElementsAnimDef = new inkAnimDef;
		sizeInterpolator = new inkAnimSize;
		sizeInterpolator.SetType( inkanimInterpolationType.Linear );
		sizeInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		sizeInterpolator.SetDirection( inkanimInterpolationDirection.FromTo );
		sizeInterpolator.SetStartSize( inkCompoundRef.GetSize( m_container ) );
		sizeInterpolator.SetEndSize( Vector2( width, m_wrapper.GetDesiredHeight() ) );
		sizeInterpolator.SetDuration( 0.075 );
		resizeElementsAnimDef.AddInterpolator( sizeInterpolator );
		inkCompoundRef.PlayAnimation( m_container, resizeElementsAnimDef );
		return resizeElementsAnimDef;
	}

}

