class CursorRootController extends inkLogicController
{
	editable var m_mainCursor : inkWidgetRef;
	editable var m_cursorPattern : inkWidgetRef;
	editable var m_progressBar : inkWidgetRef;
	editable var m_progressBarFrame : inkWidgetRef;
	protected var m_animProxy : inkAnimProxy;

	protected event OnInitialize()
	{
		inkWidgetRef.SetVisible( m_progressBar, false );
	}

	public function PlayAnim( context : CName, animationOverride : CName )
	{
		var animation : CName;
		if( m_animProxy )
		{
			m_animProxy.GotoEndAndStop( true );
			m_animProxy = NULL;
		}
		if( animationOverride != 'None' )
		{
			animation = GetAnimNameFromContext( animationOverride );
		}
		else
		{
			animation = GetAnimNameFromContext( context );
		}
		if( animation != 'None' )
		{
			m_animProxy = PlayLibraryAnimation( animation );
		}
		if( m_animProxy )
		{
			m_animProxy.RegisterToCallback( inkanimEventType.OnFinish, this, 'OnAnimationFinished' );
		}
	}

	protected virtual function GetAnimNameFromContext( context : CName ) : CName
	{
		return 'None';
	}

	protected event OnAnimationFinished( proxy : inkAnimProxy )
	{
		m_animProxy = NULL;
	}

}

class GamepadCursorRootController extends CursorRootController
{

	protected override function GetAnimNameFromContext( context : CName ) : CName
	{
		var animation : CName;
		switch( context )
		{
			case 'Show':
				animation = 'show';
			break;
			case 'Hide':
				animation = 'hide';
			break;
			case 'Default':
				animation = 'default';
			break;
			case 'Hover':
				animation = 'hover';
			break;
			case 'hoverOnHoldToComplete':
				animation = 'hoverOnHoldToComplete';
			break;
			case 'InvalidAction':
				animation = 'invalid';
			break;
		}
		return animation;
	}

}

class MouseCursorRootController extends CursorRootController
{

	protected override function GetAnimNameFromContext( context : CName ) : CName
	{
		var animation : CName;
		switch( context )
		{
			case 'Show':
				animation = 'show_mouse';
			break;
			case 'Hide':
				animation = 'hide_mouse';
			break;
			case 'Default':
				animation = 'default_mouse';
			break;
			case 'Hover':
				animation = 'hover_mouse';
			break;
			case 'Pan':
				animation = 'pan_mouse';
			break;
			case 'hoverOnHoldToComplete':
				animation = 'hoverOnHoldToComplete_mouse';
			break;
			case 'InvalidAction':
				animation = 'invalid_mouse';
			break;
		}
		return animation;
	}

}

class WorldMapCursorRootController extends CursorRootController
{

	protected override function GetAnimNameFromContext( context : CName ) : CName
	{
		var animation : CName;
		return animation;
	}

}

class CursorGameController extends inkGameController
{
	private var m_cursorRoot : weak< CursorRootController >;
	private var m_currentContext : CName;
	private var m_margin : inkMargin;
	private var m_data : MenuCursorUserData;
	private var m_isCursorVisible : Bool;
	default m_isCursorVisible = false;
	private var m_cursorType : CName;
	default m_cursorType = 'default';
	private var m_cursorForDevice : CName;
	private var m_dpadAnimProxy : inkAnimProxy;
	private var m_clickAnimProxy : inkAnimProxy;

	protected event OnInitialize()
	{
		var cursorInfo : inkCursorInfo;
		var root : inkWidget;
		root = GetRootWidget();
		root.RegisterToCallback( 'OnSetCursorVisibility', this, 'OnSetCursorVisibility' );
		root.RegisterToCallback( 'OnSetCursorPosition', this, 'OnSetCursorPosition' );
		root.RegisterToCallback( 'OnSetCursorScale', this, 'OnSetCursorScale' );
		root.RegisterToCallback( 'OnSetCursorContext', this, 'OnSetCursorContext' );
		root.RegisterToCallback( 'OnSetCursorType', this, 'OnSetCursorType' );
		root.RegisterToCallback( 'OnSetCursorForDevice', this, 'OnSetCursorForDevice' );
		root.RegisterToCallback( 'OnDpadCursorMoved', this, 'OnDpadCursorMoved' );
		RegisterToGlobalInputCallback( 'OnPostOnHold', this, 'OnHold' );
		RegisterToGlobalInputCallback( 'OnPostOnRelease', this, 'OnRelease' );
		m_isCursorVisible = false;
		cursorInfo = ( ( inkCursorInfo )( root.GetUserData( 'inkCursorInfo' ) ) );
		if( cursorInfo && cursorInfo.cursorForDevice != '' )
		{
			OnSetCursorForDevice( cursorInfo.cursorForDevice );
		}
	}

	protected event OnUnitialize()
	{
		var root : inkWidget;
		root = GetRootWidget();
		root.UnregisterFromCallback( 'OnSetCursorVisibility', this, 'OnSetCursorVisibility' );
		root.UnregisterFromCallback( 'OnSetCursorPosition', this, 'OnSetCursorPosition' );
		root.UnregisterFromCallback( 'OnSetCursorScale', this, 'OnSetCursorScale' );
		root.UnregisterFromCallback( 'OnSetCursorContext', this, 'OnSetCursorContext' );
		root.UnregisterFromCallback( 'OnSetCursorType', this, 'OnSetCursorType' );
		root.UnregisterFromCallback( 'OnSetCursorForDevice', this, 'OnSetCursorForDevice' );
		root.UnregisterFromCallback( 'OnDpadCursorMoved', this, 'OnDpadCursorMoved' );
		UnregisterFromGlobalInputCallback( 'OnPostOnHold', this, 'OnHold' );
		UnregisterFromGlobalInputCallback( 'OnPostOnRelease', this, 'OnRelease' );
	}

	protected event OnSetCursorVisibility( isVisible : Bool )
	{
		if( m_isCursorVisible != isVisible )
		{
			m_isCursorVisible = isVisible;
			if( m_isCursorVisible )
			{
				ProcessCursorContext( 'Show', NULL );
			}
			else
			{
				ProcessCursorContext( 'Hide', NULL );
			}
		}
	}

	protected event OnSetCursorPosition( const pos : Vector2 )
	{
		m_margin.left = pos.X;
		m_margin.top = pos.Y;
		if( m_cursorRoot )
		{
			inkWidgetRef.SetMargin( m_cursorRoot.m_mainCursor, m_margin );
		}
	}

	protected event OnSetCursorScale( const scale : Vector2 )
	{
		var size, mainCursorSize, mainCursorScale : Vector2;
		var cursorVisibilityInfo : inkCursorInfo;
		var root : inkCompoundWidget;
		var type : CName;
		if( m_cursorRoot )
		{
			mainCursorScale = inkWidgetRef.GetScale( m_cursorRoot.m_mainCursor );
			if( scale != mainCursorScale )
			{
				inkWidgetRef.SetScale( m_cursorRoot.m_mainCursor, scale );
				root = ( ( inkCompoundWidget )( GetRootWidget() ) );
				cursorVisibilityInfo = ( ( inkCursorInfo )( root.GetUserData( 'inkCursorInfo' ) ) );
				if( cursorVisibilityInfo )
				{
					type = GetCursorType();
					if( type != 'mouse' && type != 'world_map' )
					{
						mainCursorSize = inkWidgetRef.GetSize( m_cursorRoot.m_mainCursor );
						size.X = mainCursorSize.X * scale.X;
						size.Y = mainCursorSize.Y * scale.Y;
					}
					cursorVisibilityInfo.SetSize( size );
				}
			}
		}
	}

	protected event OnSetCursorContext( const context : CName, data : inkUserData )
	{
		ProcessCursorContext( context, data );
	}

	protected event OnSetCursorType( const type : CName )
	{
		if( m_cursorType != type )
		{
			m_cursorType = type;
			SpawnCursor();
		}
	}

	protected event OnSetCursorForDevice( const type : CName )
	{
		if( m_cursorForDevice != type )
		{
			m_cursorForDevice = type;
			SpawnCursor();
		}
	}

	protected event OnDpadCursorMoved( angle : Float )
	{
		var size, scale : Vector2;
		var root : inkCompoundWidget;
		var cursorVisibilityInfo : inkCursorInfo;
		root = ( ( inkCompoundWidget )( GetRootWidget() ) );
		if( m_dpadAnimProxy )
		{
			m_dpadAnimProxy.Stop( true );
			m_dpadAnimProxy = NULL;
		}
		if( m_cursorRoot )
		{
			cursorVisibilityInfo = ( ( inkCursorInfo )( root.GetUserData( 'inkCursorInfo' ) ) );
			if( cursorVisibilityInfo )
			{
				scale = inkWidgetRef.GetScale( m_cursorRoot.m_mainCursor );
				size = inkWidgetRef.GetSize( m_cursorRoot.m_mainCursor );
				size.X *= scale.X;
				size.Y *= scale.Y;
				cursorVisibilityInfo.SetSize( size );
			}
			inkWidgetRef.SetRotation( m_cursorRoot.m_cursorPattern, angle );
			m_dpadAnimProxy = m_cursorRoot.PlayLibraryAnimation( 'wipe' );
		}
	}

	protected virtual function GetCursorType() : CName
	{
		if( m_cursorType == 'default' || m_cursorForDevice == 'mouse' )
		{
			if( m_cursorType != 'dpad' )
			{
				return m_cursorForDevice;
			}
		}
		return m_cursorType;
	}

	public function SpawnCursor()
	{
		var type : CName;
		type = GetCursorType();
		AsyncSpawnFromLocal( GetRootWidget(), type, this, 'OnCursorSpawned' );
	}

	protected event OnCursorSpawned( widget : inkWidget, userData : IScriptable )
	{
		var cursorVisibilityInfo : inkCursorInfo;
		var root : inkCompoundWidget;
		var size : Vector2;
		var type : CName;
		root = ( ( inkCompoundWidget )( GetRootWidget() ) );
		if( m_cursorRoot )
		{
			root.RemoveChild( m_cursorRoot.GetRootWidget() );
		}
		m_cursorRoot = ( ( CursorRootController )( widget.GetController() ) );
		cursorVisibilityInfo = ( ( inkCursorInfo )( root.GetUserData( 'inkCursorInfo' ) ) );
		if( cursorVisibilityInfo )
		{
			cursorVisibilityInfo.SetResizableWidget( inkWidgetRef.Get( m_cursorRoot.m_mainCursor ) );
			type = GetCursorType();
			if( m_cursorRoot && type == 'dpad' )
			{
				inkWidgetRef.SetSize( m_cursorRoot.m_mainCursor, cursorVisibilityInfo.GetResizableWidgetSize() );
			}
			if( ( m_cursorRoot && type != 'mouse' ) && type != 'world_map' )
			{
				size = inkWidgetRef.GetSize( m_cursorRoot.m_mainCursor );
			}
			cursorVisibilityInfo.SetSize( size );
			OnSetCursorVisibility( cursorVisibilityInfo.isVisible );
			OnSetCursorPosition( cursorVisibilityInfo.pos );
		}
		ProcessCursorContext( m_currentContext, NULL, true );
	}

	protected event OnHold( evt : inkPointerEvent )
	{
		var progress : Float;
		progress = evt.GetHoldProgress();
		if( progress >= 1.0 )
		{
			return false;
		}
		if( m_data == NULL )
		{
			return false;
		}
		if( DoesActionMatch( evt, m_data.GetActions() ) )
		{
			PlaySound( 'Attributes', 'OnStart' );
			UpdateFillPercent( progress );
		}
	}

	protected event OnRelease( evt : inkPointerEvent )
	{
		var actionsList : array< CName >;
		if( ( m_currentContext == 'HoldToComplete' || m_currentContext == 'Hover' ) && isClickAction( evt ) )
		{
			if( m_clickAnimProxy )
			{
				m_clickAnimProxy.GotoStartAndStop();
				m_clickAnimProxy = NULL;
			}
			if( m_cursorType == 'dpad' )
			{
				m_clickAnimProxy = m_cursorRoot.PlayLibraryAnimation( 'click_feedback_dpad' );
			}
			else
			{
				m_clickAnimProxy = m_cursorRoot.PlayLibraryAnimation( 'click_feedback_' + m_cursorForDevice );
			}
		}
		if( m_data )
		{
			actionsList = m_data.GetActions();
		}
		if( DoesActionMatch( evt, actionsList ) )
		{
			UpdateFillPercent( 0.0 );
		}
	}

	public function UpdateFillPercent( percent : Float )
	{
		var newScale : Vector2;
		newScale.X = percent;
		newScale.Y = 1.0;
		if( m_cursorRoot )
		{
			if( inkWidgetRef.IsValid( m_cursorRoot.m_progressBarFrame ) )
			{
				inkWidgetRef.SetVisible( m_cursorRoot.m_progressBarFrame, percent > 0.0 );
			}
			inkWidgetRef.SetVisible( m_cursorRoot.m_progressBar, percent > 0.0 );
			if( m_cursorType == 'dpad' )
			{
				inkWidgetRef.Get( m_cursorRoot.m_progressBar ).SetEffectParamValue( inkEffectType.LinearWipe, 'LinearWipe_0', 'transition', percent );
			}
			else
			{
				inkWidgetRef.SetScale( m_cursorRoot.m_progressBar, newScale );
			}
		}
	}

	private function ProcessCursorContext( const context : CName, data : inkUserData, optional force : Bool )
	{
		var animationOverride : CName;
		if( ( m_currentContext != context || ( m_data != data ) ) || force )
		{
			m_currentContext = context;
			m_data = ( ( MenuCursorUserData )( data ) );
			if( m_data )
			{
				animationOverride = m_data.GetAnimationOverride();
			}
			UpdateFillPercent( 0.0 );
			if( m_cursorRoot && m_cursorType != 'dpad' )
			{
				m_cursorRoot.PlayAnim( context, animationOverride );
			}
			if( context == 'Hover' )
			{
				GameInstance.GetAudioSystem( GetPlayerControlledObject().GetGame() ).Play( 'ui_menu_hover' );
			}
		}
	}

	public function isClickAction( evt : inkPointerEvent ) : Bool
	{
		if( ( ( evt.IsAction( 'click' ) || evt.IsAction( 'activate' ) ) || evt.IsAction( 'proceed' ) ) || evt.IsAction( 'one_click_confirm' ) )
		{
			return true;
		}
		return false;
	}

	private function DoesActionMatch( evt : inkPointerEvent, const actionsList : ref< array< CName > > ) : Bool
	{
		var i, count : Int32;
		count = actionsList.Size();
		if( count )
		{
			for( i = 0; i < count; i += 1 )
			{
				if( evt.IsAction( actionsList[ i ] ) )
				{
					return true;
				}
			}
		}
		return false;
	}

}

class MenuCursorUserData extends inkUserData
{
	private var animationOverride : CName;
	private var actions : array< CName >;

	public function SetAnimationOverride( anim : CName )
	{
		animationOverride = anim;
	}

	public function GetAnimationOverride() : CName
	{
		return animationOverride;
	}

	public function AddAction( action : CName )
	{
		actions.PushBack( action );
	}

	public function AddUniqueAction( action : CName )
	{
		if( !( actions.Contains( action ) ) )
		{
			actions.PushBack( action );
		}
	}

	public function RemoveAction( action : CName )
	{
		actions.Remove( action );
	}

	public function GetActions() : array< CName >
	{
		return actions;
	}

	public function GetActionsListSize() : Int32
	{
		return actions.Size();
	}

}

