class CharacterCreationBodyMorphBaseOption extends inkLogicController
{
	protected var m_isPrevOrNextBtnHoveredOver : Bool;
	default m_isPrevOrNextBtnHoveredOver = false;

	public const function IsPrevOrNextBtnHoveredOver() : Bool
	{
		return m_isPrevOrNextBtnHoveredOver;
	}

}

class characterCreationVoiceOverSwitcher extends CharacterCreationBodyMorphBaseOption
{
	private editable var m_selectedLabel : inkTextRef;
	private editable var m_selectorNextBtn : inkWidgetRef;
	private editable var m_selectorPrevBtn : inkWidgetRef;
	private editable var m_warningLabel : inkTextRef;
	private var m_isMale : Bool;
	private editable var m_male : String;
	private editable var m_female : String;
	private editable var m_selectorTexture : inkImageRef;
	private editable var m_arrowsTexture : inkImageRef;
	private editable var m_optionSwitchHint : inkWidgetRef;
	private var translationAnimationCtrl : weak< inkTextReplaceController >;
	private var m_selector : weak< inkWidget >;
	private var m_inputDisabled : Bool;

	protected event OnInitialize()
	{
		m_selector = GetRootWidget();
		inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnRelease', this, 'OnSwitch' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnSwitch' );
		m_selector.RegisterToCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.RegisterToCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.RegisterToCallback( 'OnRelease', this, 'OnShortcutPress' );
		m_inputDisabled = false;
	}

	protected event OnUninitialize()
	{
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnRelease', this, 'OnSwitch' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnSwitch' );
		m_selector.UnregisterFromCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.UnregisterFromCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.UnregisterFromCallback( 'OnRelease', this, 'OnShortcutPress' );
	}

	protected event OnSwitch( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( e.IsAction( 'click' ) )
			{
				Switch();
			}
		}
	}

	public function Switch()
	{
		SetIsBrainGenderMale( !( m_isMale ) );
	}

	protected event OnShortcutPress( e : inkPointerEvent )
	{
		if( !( e.IsHandled() ) && !( m_inputDisabled ) )
		{
			if( e.IsAction( 'option_switch_prev' ) )
			{
				PlaySound( 'Button', 'OnPress' );
				Switch();
				e.Handle();
			}
			else if( e.IsAction( 'option_switch_next' ) )
			{
				PlaySound( 'Button', 'OnPress' );
				Switch();
				e.Handle();
			}
		}
	}

	protected event OnHoverOverWidget( e : inkPointerEvent )
	{
		if( GetRootWidget().GetState() != 'Unavailable' && !( m_inputDisabled ) )
		{
			inkWidgetRef.SetVisible( m_optionSwitchHint, true );
		}
	}

	protected event OnHoverOutWidget( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		}
	}

	protected event OnHoverOverNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_right' );
			inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_right' );
			m_isPrevOrNextBtnHoveredOver = true;
		}
	}

	protected event OnHoverOutNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
			inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

	protected event OnHoverOverPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_left' );
			inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_left' );
			m_isPrevOrNextBtnHoveredOver = true;
		}
	}

	protected event OnHoverOutPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
			inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

	public function SetInputDisabled( disabled : Bool )
	{
		if( m_inputDisabled == disabled )
		{
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
			inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		}
		m_inputDisabled = disabled;
	}

	public function SetIsBrainGenderMale( isMale : Bool )
	{
		var localizedText : String;
		m_isMale = isMale;
		inkTextRef.SetText( m_selectedLabel, ( ( m_isMale ) ? ( "LocKey#43481" ) : ( "LocKey#43482" ) ) );
		localizedText = GetLocalizedText( ( ( m_isMale ) ? ( "LocKey#42833" ) : ( "LocKey#42834" ) ) );
		translationAnimationCtrl.SetBaseText( "..." );
		translationAnimationCtrl = ( ( inkTextReplaceController )( inkTextRef.GetController( m_warningLabel ) ) );
		translationAnimationCtrl.SetTargetText( localizedText );
		translationAnimationCtrl.PlaySetAnimation();
		CallCustomCallback( 'OnVoiceOverSwitched' );
	}

	public function IsBrainGenderMale() : Bool
	{
		return m_isMale;
	}

}

class characterCreationBodyMorphOption extends CharacterCreationBodyMorphBaseOption
{
	private editable var m_optionLabel : inkTextRef;
	private editable var m_selectedLabel : inkTextRef;
	private editable var m_selectorNextBtn : inkWidgetRef;
	private editable var m_selectorPrevBtn : inkWidgetRef;
	private editable var m_selectorTexture : inkImageRef;
	private editable var m_arrowsTexture : inkImageRef;
	private editable var m_optionSwitchHint : inkWidgetRef;
	private const var m_selectorOption : weak< CharacterCustomizationOption >;
	private const var m_morphInfo : weak< gameuiMorphInfo >;
	private const var m_appearanceInfo : weak< gameuiAppearanceInfo >;
	private const var m_switcherInfo : weak< gameuiSwitcherInfo >;
	private var m_currSelectorIndex : Int32;
	default m_currSelectorIndex = -1;
	private var m_selector : weak< inkWidget >;
	private var m_inputDisabled : Bool;
	var m_animationProxy : inkAnimProxy;

	protected event OnInitialize()
	{
		m_selector = GetRootWidget();
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnRelease', this, 'OnNext' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnPrev' );
		m_selector.RegisterToCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.RegisterToCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.RegisterToCallback( 'OnRelease', this, 'OnShortcutPress' );
		inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		m_inputDisabled = false;
	}

	protected event OnUninitialize()
	{
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnRelease', this, 'OnNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnPrev' );
		m_selector.UnregisterFromCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.UnregisterFromCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.UnregisterFromCallback( 'OnRelease', this, 'OnShortcutPress' );
	}

	protected event OnHoverOverNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_selectorOption && ( m_currSelectorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_right' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_right' );
				m_isPrevOrNextBtnHoveredOver = true;
			}
		}
	}

	protected event OnHoverOutNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_selectorOption && ( m_currSelectorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
			}
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

	protected event OnHoverOverPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_selectorOption && ( m_currSelectorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_left' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_left' );
				m_isPrevOrNextBtnHoveredOver = true;
			}
		}
	}

	protected event OnHoverOutPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_selectorOption && ( m_currSelectorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
			}
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

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

	protected event OnPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( e.IsAction( 'click' ) )
			{
				Previous();
			}
		}
	}

	protected event OnShortcutPress( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( !( e.IsHandled() ) )
			{
				if( e.IsAction( 'option_switch_prev' ) )
				{
					PlaySound( 'Button', 'OnPress' );
					Previous();
					e.Handle();
				}
				else if( e.IsAction( 'option_switch_next' ) )
				{
					PlaySound( 'Button', 'OnPress' );
					Next();
					e.Handle();
				}
			}
		}
	}

	protected event OnHoverOverWidget( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( GetRootWidget().GetState() != 'Unavailable' )
			{
				inkWidgetRef.SetVisible( m_optionSwitchHint, true );
			}
		}
	}

	protected event OnHoverOutWidget( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		}
	}

	public function SetInputDisabled( disabled : Bool )
	{
		if( m_inputDisabled != disabled )
		{
			if( m_selectorOption && ( m_currSelectorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrows_idle' );
				m_isPrevOrNextBtnHoveredOver = false;
			}
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
			RefreshView();
		}
		m_inputDisabled = disabled;
	}

	public function Previous()
	{
		if( m_morphInfo )
		{
			SetSelectedMorphName( m_morphInfo, m_currSelectorIndex - 1 );
		}
		else if( m_appearanceInfo )
		{
			SetSelectedAppearanceDefinition( m_appearanceInfo, m_currSelectorIndex - 1 );
		}
		else if( m_switcherInfo )
		{
			SetSelectedSwitcherOption( m_switcherInfo, m_currSelectorIndex - 1 );
		}
	}

	public function Next()
	{
		if( m_morphInfo )
		{
			SetSelectedMorphName( m_morphInfo, m_currSelectorIndex + 1 );
		}
		else if( m_appearanceInfo )
		{
			SetSelectedAppearanceDefinition( m_appearanceInfo, m_currSelectorIndex + 1 );
		}
		else if( m_switcherInfo )
		{
			SetSelectedSwitcherOption( m_switcherInfo, m_currSelectorIndex + 1 );
		}
	}

	public function GetSelectorOption() : const weak< CharacterCustomizationOption >
	{
		return m_selectorOption;
	}

	public function GetSelectorIndex() : Uint32
	{
		return ( ( Uint32 )( m_currSelectorIndex ) );
	}

	public function SetOption( const option : weak< CharacterCustomizationOption > )
	{
		const var morphInfo : gameuiMorphInfo;
		const var appearanceInfo : gameuiAppearanceInfo;
		const var switcherInfo : gameuiSwitcherInfo;
		m_morphInfo = NULL;
		m_appearanceInfo = NULL;
		m_switcherInfo = NULL;
		appearanceInfo = ( ( gameuiAppearanceInfo )( option.info ) );
		if( appearanceInfo )
		{
			m_appearanceInfo = appearanceInfo;
			SetSelectorOption( option );
			SetSelectedAppearanceDefinition( m_appearanceInfo, ( ( Int32 )( option.currIndex ) ), true );
			return;
		}
		morphInfo = ( ( gameuiMorphInfo )( option.info ) );
		if( morphInfo )
		{
			m_morphInfo = morphInfo;
			SetSelectorOption( option );
			SetSelectedMorphName( m_morphInfo, ( ( Int32 )( option.currIndex ) ), true );
			return;
		}
		switcherInfo = ( ( gameuiSwitcherInfo )( option.info ) );
		if( switcherInfo )
		{
			m_switcherInfo = switcherInfo;
			SetSelectorOption( option );
			SetSelectedSwitcherOption( m_switcherInfo, ( ( Int32 )( option.currIndex ) ), true );
		}
	}

	public function ResetOption()
	{
		m_morphInfo = NULL;
		m_appearanceInfo = NULL;
		m_switcherInfo = NULL;
		m_currSelectorIndex = -1;
	}

	public function RefreshView()
	{
		var isVisible : Bool;
		isVisible = ( m_currSelectorIndex > -1 ) && ( ( ( m_morphInfo && ( m_morphInfo.morphNames.Size() > 1 ) ) || ( m_appearanceInfo && ( m_appearanceInfo.definitions.Size() > 1 ) ) ) || ( m_switcherInfo && ( m_switcherInfo.options.Size() > 1 ) ) );
		if( isVisible )
		{
			GetRootWidget().SetState( 'Available' );
			if( !( m_isPrevOrNextBtnHoveredOver ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_idle' );
			}
			inkImageRef.SetVisible( m_arrowsTexture, true );
		}
		else
		{
			inkTextRef.SetText( m_selectedLabel, "LocKey#20482" );
			GetRootWidget().SetState( 'Unavailable' );
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_option_disabled' );
			inkImageRef.SetVisible( m_arrowsTexture, false );
		}
	}

	public function SetSelectorOption( const option : weak< CharacterCustomizationOption > )
	{
		m_selectorOption = option;
		inkTextRef.SetText( m_optionLabel, option.info.localizedName );
	}

	public function SetSelectedMorphName( const morphInfo : weak< gameuiMorphInfo >, currIndex : Int32, optional force : Bool )
	{
		var index : Int32;
		if( morphInfo.morphNames.Size() > 1 )
		{
			index = Circle( currIndex, 0, morphInfo.morphNames.Size() - 1 );
			if( force || ( m_currSelectorIndex != index ) )
			{
				m_currSelectorIndex = index;
				inkTextRef.SetText( m_selectedLabel, morphInfo.morphNames[ m_currSelectorIndex ].localizedName );
				CallCustomCallback( 'OnSliderChange' );
			}
		}
		else
		{
			m_currSelectorIndex = -1;
		}
		RefreshView();
	}

	public function SetSelectedAppearanceDefinition( const appearanceInfo : weak< gameuiAppearanceInfo >, currIndex : Int32, optional force : Bool )
	{
		var index : Int32;
		if( appearanceInfo.definitions.Size() > 1 )
		{
			index = Circle( currIndex, 0, appearanceInfo.definitions.Size() - 1 );
			if( force || ( m_currSelectorIndex != index ) )
			{
				m_currSelectorIndex = index;
				inkTextRef.SetText( m_selectedLabel, appearanceInfo.definitions[ m_currSelectorIndex ].localizedName );
				CallCustomCallback( 'OnSliderChange' );
			}
		}
		else
		{
			m_currSelectorIndex = -1;
		}
		RefreshView();
	}

	public function SetSelectedSwitcherOption( const switcherInfo : weak< gameuiSwitcherInfo >, currIndex : Int32, optional force : Bool )
	{
		var index : Int32;
		if( switcherInfo.options.Size() > 1 )
		{
			index = Circle( currIndex, 0, switcherInfo.options.Size() - 1 );
			if( force || ( m_currSelectorIndex != index ) )
			{
				m_currSelectorIndex = index;
				inkTextRef.SetText( m_selectedLabel, switcherInfo.options[ m_currSelectorIndex ].localizedName );
				CallCustomCallback( 'OnSliderChange' );
			}
		}
		else
		{
			m_currSelectorIndex = -1;
		}
		RefreshView();
	}

	public function Circle( v, min, max : Int32 ) : Int32
	{
		if( v < min )
		{
			return max;
		}
		else if( v > max )
		{
			return min;
		}
		return v;
	}

}

class characterCreationBodyMorphOptionSelectorButton extends inkLogicController
{
	private editable var m_overArrow : inkWidgetRef;

	protected event OnInitialize()
	{
		var root : weak< inkWidget >;
		inkWidgetRef.SetVisible( m_overArrow, false );
		root = GetRootWidget();
		root.RegisterToCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.RegisterToCallback( 'OnHoverOut', this, 'OnHoverOut' );
	}

	protected event OnUninitialize()
	{
		var root : weak< inkWidget >;
		root = GetRootWidget();
		root.UnregisterFromCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.UnregisterFromCallback( 'OnHoverOut', this, 'OnHoverOut' );
	}

	protected event OnHoverOver( e : inkPointerEvent )
	{
		inkWidgetRef.SetVisible( m_overArrow, true );
	}

	protected event OnHoverOut( e : inkPointerEvent )
	{
		inkWidgetRef.SetVisible( m_overArrow, false );
	}

}

class characterCreationBodyMorphColorOption extends CharacterCreationBodyMorphBaseOption
{
	private editable var m_optionLabel : inkTextRef;
	private editable var m_colorPickerBtn : inkWidgetRef;
	private editable var m_selectorNextBtn : inkWidgetRef;
	private editable var m_selectorPrevBtn : inkWidgetRef;
	private editable var m_selectorTexture : inkImageRef;
	private editable var m_arrowsTexture : inkImageRef;
	private editable var m_optionSwitchHint : inkWidgetRef;
	private const var m_colorPickerOption : weak< CharacterCustomizationOption >;
	private const var m_appearanceInfo : weak< gameuiAppearanceInfo >;
	private var m_currColorIndex : Int32;
	default m_currColorIndex = -1;
	private var m_selector : weak< inkWidget >;
	private var m_inputDisabled : Bool;

	protected event OnInitialize()
	{
		m_selector = GetRootWidget();
		inkWidgetRef.SetVisible( m_colorPickerBtn, false );
		inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.RegisterToCallback( m_colorPickerBtn, 'OnHoverOver', this, 'OnHoverOverColorPicker' );
		inkWidgetRef.RegisterToCallback( m_colorPickerBtn, 'OnHoverOut', this, 'OnHoverOutColorPicker' );
		inkWidgetRef.RegisterToCallback( m_colorPickerBtn, 'OnTriggered', this, 'OnColorPickerTriggered' );
		inkWidgetRef.RegisterToCallback( m_selectorNextBtn, 'OnRelease', this, 'OnNext' );
		inkWidgetRef.RegisterToCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnPrev' );
		m_selector.RegisterToCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.RegisterToCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.RegisterToCallback( 'OnRelease', this, 'OnShortcutPress' );
		m_isPrevOrNextBtnHoveredOver = false;
		m_inputDisabled = false;
	}

	protected event OnUninitialize()
	{
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOver', this, 'OnHoverOverNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnHoverOut', this, 'OnHoverOutNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOver', this, 'OnHoverOverPrev' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnHoverOut', this, 'OnHoverOutPrev' );
		inkWidgetRef.UnregisterFromCallback( m_colorPickerBtn, 'OnHoverOver', this, 'OnHoverOverColorPicker' );
		inkWidgetRef.UnregisterFromCallback( m_colorPickerBtn, 'OnHoverOut', this, 'OnHoverOutColorPicker' );
		inkWidgetRef.UnregisterFromCallback( m_colorPickerBtn, 'OnTriggered', this, 'OnColorPickerTriggered' );
		inkWidgetRef.UnregisterFromCallback( m_selectorNextBtn, 'OnRelease', this, 'OnNext' );
		inkWidgetRef.UnregisterFromCallback( m_selectorPrevBtn, 'OnRelease', this, 'OnPrev' );
		m_selector.UnregisterFromCallback( 'OnEnter', this, 'OnHoverOverWidget' );
		m_selector.UnregisterFromCallback( 'OnLeave', this, 'OnHoverOutWidget' );
		m_selector.UnregisterFromCallback( 'OnRelease', this, 'OnShortcutPress' );
	}

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

	protected event OnPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( e.IsAction( 'click' ) )
			{
				Previous();
			}
		}
	}

	public function Previous()
	{
		if( m_appearanceInfo )
		{
			SetSelectedAppearanceDefinitionColor( m_appearanceInfo, m_currColorIndex - 1 );
			CallCustomCallback( 'OnColorChange' );
		}
	}

	public function Next()
	{
		if( m_appearanceInfo )
		{
			SetSelectedAppearanceDefinitionColor( m_appearanceInfo, m_currColorIndex + 1 );
			CallCustomCallback( 'OnColorChange' );
		}
	}

	protected event OnShortcutPress( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( !( e.IsHandled() ) )
			{
				if( e.IsAction( 'option_switch_prev' ) )
				{
					PlaySound( 'Button', 'OnPress' );
					Previous();
					e.Handle();
				}
				else if( e.IsAction( 'option_switch_next' ) )
				{
					PlaySound( 'Button', 'OnPress' );
					Next();
					e.Handle();
				}
			}
		}
	}

	protected event OnHoverOverWidget( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( GetRootWidget().GetState() != 'Unavailable' )
			{
				inkWidgetRef.SetVisible( m_optionSwitchHint, true );
			}
		}
	}

	protected event OnHoverOutWidget( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
		}
	}

	protected event OnHoverOverNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_right' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_right' );
				m_isPrevOrNextBtnHoveredOver = true;
			}
		}
	}

	protected event OnHoverOutNext( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_idle' );
			}
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

	protected event OnHoverOverPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_left' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_left' );
				m_isPrevOrNextBtnHoveredOver = true;
			}
		}
	}

	protected event OnHoverOutPrev( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_idle' );
			}
		}
		m_isPrevOrNextBtnHoveredOver = false;
	}

	protected event OnHoverOverColorPicker( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_centre' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_centre' );
			}
		}
	}

	protected event OnHoverOutColorPicker( e : inkPointerEvent )
	{
		if( !( m_inputDisabled ) )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_idle' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_idle' );
			}
		}
	}

	protected event OnColorPickerTriggered( widget : weak< inkWidget > )
	{
		if( !( m_inputDisabled ) )
		{
			CallCustomCallback( 'OnColorPickerTriggered' );
		}
	}

	public function SetInputDisabled( disabled : Bool )
	{
		if( m_inputDisabled != disabled )
		{
			if( m_colorPickerOption && ( m_currColorIndex >= 0 ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_centre' );
				inkImageRef.SetTexturePart( m_arrowsTexture, 'arrow_gal_centre' );
				m_isPrevOrNextBtnHoveredOver = false;
			}
			inkWidgetRef.SetVisible( m_optionSwitchHint, false );
			RefreshView();
		}
		m_inputDisabled = disabled;
	}

	public function GetColorPickerOption() : const weak< CharacterCustomizationOption >
	{
		return m_colorPickerOption;
	}

	public function GetColorIndex() : Uint32
	{
		return ( ( Uint32 )( m_currColorIndex ) );
	}

	public function IsColorPickerTriggered() : Bool
	{
		var controller : characterCreationBodyMorphOptionColorPickerButton;
		controller = ( ( characterCreationBodyMorphOptionColorPickerButton )( inkWidgetRef.GetController( m_colorPickerBtn ) ) );
		return controller.IsTriggered();
	}

	public function RefreshColorPicker( index : Int32, isTriggered : Bool )
	{
		var controller : characterCreationBodyMorphOptionColorPickerButton;
		const var appearanceInfo : gameuiAppearanceInfo;
		appearanceInfo = ( ( gameuiAppearanceInfo )( m_colorPickerOption.info ) );
		SetSelectedAppearanceDefinitionColor( appearanceInfo, index );
		controller = ( ( characterCreationBodyMorphOptionColorPickerButton )( inkWidgetRef.GetController( m_colorPickerBtn ) ) );
		controller.Trigger( isTriggered );
	}

	public function SetOption( const option : weak< CharacterCustomizationOption > )
	{
		const var appearanceInfo : gameuiAppearanceInfo;
		appearanceInfo = ( ( gameuiAppearanceInfo )( option.info ) );
		if( appearanceInfo && appearanceInfo.useThumbnails )
		{
			SetColorPickerOption( appearanceInfo, option );
			SetSelectedAppearanceDefinitionColor( appearanceInfo, ( ( Int32 )( option.currIndex ) ), true );
		}
	}

	public function ResetOption()
	{
		m_appearanceInfo = NULL;
		m_currColorIndex = -1;
	}

	public function RefreshView()
	{
		var isVisible : Bool;
		isVisible = m_appearanceInfo && ( m_appearanceInfo.definitions.Size() > 1 );
		inkWidgetRef.SetVisible( m_colorPickerBtn, isVisible );
		if( isVisible )
		{
			GetRootWidget().SetState( 'Available' );
			if( !( m_isPrevOrNextBtnHoveredOver ) )
			{
				inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_idle' );
			}
			inkImageRef.SetVisible( m_arrowsTexture, true );
		}
		else
		{
			GetRootWidget().SetState( 'Unavailable' );
			inkImageRef.SetTexturePart( m_selectorTexture, 'cell_gallery_disabled' );
			inkImageRef.SetVisible( m_arrowsTexture, false );
		}
	}

	public function SetColorPickerOption( const appearanceInfo : weak< gameuiAppearanceInfo >, const option : weak< CharacterCustomizationOption > )
	{
		m_appearanceInfo = appearanceInfo;
		m_colorPickerOption = option;
		inkTextRef.SetText( m_optionLabel, appearanceInfo.localizedName );
		RefreshView();
	}

	public function SetSelectedAppearanceDefinitionColor( const appearanceInfo : weak< gameuiAppearanceInfo >, currIndex : Int32, optional force : Bool )
	{
		var controller : characterCreationBodyMorphOptionColorPickerButton;
		var index : Int32;
		if( appearanceInfo.definitions.Size() > 1 )
		{
			index = Circle( currIndex, 0, appearanceInfo.definitions.Size() - 1 );
			if( force || ( m_currColorIndex != index ) )
			{
				m_currColorIndex = index;
				controller = ( ( characterCreationBodyMorphOptionColorPickerButton )( inkWidgetRef.GetController( m_colorPickerBtn ) ) );
				controller.SetTintColor( appearanceInfo.definitions[ m_currColorIndex ].color, appearanceInfo.definitions[ m_currColorIndex ].icon );
				CallCustomCallback( 'OnColorChange' );
			}
		}
		else
		{
			m_currColorIndex = -1;
		}
		RefreshView();
	}

	public function Circle( v, min, max : Int32 ) : Int32
	{
		if( v < min )
		{
			return max;
		}
		else if( v > max )
		{
			return min;
		}
		return v;
	}

}

class characterCreationBodyMorphOptionColorPickerButton extends inkLogicController
{
	private editable var m_background : inkWidgetRef;
	private editable var m_icon : inkImageRef;
	private var m_isTriggered : Bool;
	default m_isTriggered = false;

	protected event OnInitialize()
	{
		var root : weak< inkWidget >;
		root = GetRootWidget();
		root.RegisterToCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.RegisterToCallback( 'OnHoverOut', this, 'OnHoverOut' );
		root.RegisterToCallback( 'OnRelease', this, 'OnTrigger' );
		inkImageRef.SetVisible( m_icon, false );
	}

	protected event OnUninitialize()
	{
		var root : weak< inkWidget >;
		root = GetRootWidget();
		root.UnregisterFromCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.UnregisterFromCallback( 'OnHoverOut', this, 'OnHoverOut' );
		root.UnregisterFromCallback( 'OnRelease', this, 'OnTrigger' );
	}

	protected event OnHoverOver( e : inkPointerEvent )
	{
		GetRootWidget().SetState( 'Hover' );
	}

	protected event OnHoverOut( e : inkPointerEvent )
	{
		GetRootWidget().SetState( 'Default' );
	}

	protected event OnTrigger( e : inkPointerEvent )
	{
		if( e.IsAction( 'click' ) )
		{
			Trigger( !( m_isTriggered ) );
			CallCustomCallback( 'OnTriggered' );
		}
	}

	public function SetTintColor( color : Color, icon : TweakDBID )
	{
		var iconRecord : UIIcon_Record;
		inkWidgetRef.SetTintColor( m_background, color );
		if( TDBID.IsValid( icon ) )
		{
			inkImageRef.SetVisible( m_icon, true );
			iconRecord = TweakDBInterface.GetUIIconRecord( icon );
			inkImageRef.SetAtlasResource( m_icon, iconRecord.AtlasResourcePath() );
			inkImageRef.SetTexturePart( m_icon, iconRecord.AtlasPartName() );
		}
		else
		{
			inkImageRef.SetVisible( m_icon, false );
		}
	}

	public function IsTriggered() : Bool
	{
		return m_isTriggered;
	}

	public function Trigger( enable : Bool )
	{
		m_isTriggered = enable;
	}

}

class characterCreationBodyMorphOptionColorPickerItem extends inkLogicController
{
	private editable var m_background : inkWidgetRef;
	private editable var m_icon : inkImageRef;
	private editable var m_foreground : inkWidgetRef;
	private editable var m_selectionMark : inkWidgetRef;

	protected event OnInitialize()
	{
		var root : weak< inkWidget >;
		inkImageRef.SetVisible( m_icon, false );
		inkWidgetRef.SetVisible( m_foreground, false );
		inkWidgetRef.SetVisible( m_selectionMark, false );
		root = GetRootWidget();
		root.RegisterToCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.RegisterToCallback( 'OnHoverOut', this, 'OnHoverOut' );
		root.RegisterToCallback( 'OnRelease', this, 'OnSelect' );
	}

	protected event OnUninitialize()
	{
		var root : weak< inkWidget >;
		root = GetRootWidget();
		root.UnregisterFromCallback( 'OnHoverOver', this, 'OnHoverOver' );
		root.UnregisterFromCallback( 'OnHoverOut', this, 'OnHoverOut' );
		root.UnregisterFromCallback( 'OnRelease', this, 'OnSelect' );
	}

	protected event OnHoverOver( e : inkPointerEvent )
	{
		inkWidgetRef.SetVisible( m_foreground, true );
	}

	protected event OnHoverOut( e : inkPointerEvent )
	{
		inkWidgetRef.SetVisible( m_foreground, false );
	}

	protected event OnSelect( e : inkPointerEvent )
	{
		if( e.IsAction( 'click' ) )
		{
			CallCustomCallback( 'OnSelect' );
		}
	}

	public function SetTintColor( color : Color, icon : TweakDBID )
	{
		var iconRecord : UIIcon_Record;
		inkWidgetRef.SetTintColor( m_background, color );
		if( TDBID.IsValid( icon ) )
		{
			inkImageRef.SetVisible( m_icon, true );
			iconRecord = TweakDBInterface.GetUIIconRecord( icon );
			inkImageRef.SetAtlasResource( m_icon, iconRecord.AtlasResourcePath() );
			inkImageRef.SetTexturePart( m_icon, iconRecord.AtlasPartName() );
		}
		else
		{
			inkImageRef.SetVisible( m_icon, false );
		}
	}

	public function SetSelected( selected : Bool, moveCursor : Bool )
	{
		inkWidgetRef.SetVisible( m_selectionMark, selected );
		if( selected && moveCursor )
		{
			MoveCursorToItem();
		}
	}

	public function MoveCursorToItem()
	{
		SetCursorOverWidget( GetRootWidget() );
	}

}

class characterCreationBodyMorphOptionColorPicker extends inkLogicController
{
	private editable var m_grid : inkUniformGridRef;
	private editable var m_title : inkTextRef;
	private const var m_option : weak< CharacterCustomizationOption >;
	private var m_selectedIndex : Int32;
	default m_selectedIndex = -1;

	protected event OnColorSelected( widget : weak< inkWidget > )
	{
		var i, count : Int32;
		var colorPickerItem : weak< inkWidget >;
		var colorPickerItemController : weak< characterCreationBodyMorphOptionColorPickerItem >;
		count = inkUniformGridRef.GetNumChildren( m_grid );
		for( i = 0; i < count; i += 1 )
		{
			colorPickerItem = inkUniformGridRef.GetWidgetByIndex( m_grid, i );
			colorPickerItemController = ( ( characterCreationBodyMorphOptionColorPickerItem )( colorPickerItem.GetController() ) );
			if( colorPickerItem == widget )
			{
				colorPickerItemController.SetSelected( true, false );
				m_selectedIndex = i;
			}
			else
			{
				colorPickerItemController.SetSelected( false, false );
			}
		}
		CallCustomCallback( 'OnColorSelected' );
	}

	public function GetOption() : const weak< CharacterCustomizationOption >
	{
		return m_option;
	}

	public function SetTitle( const title : ref< String > )
	{
		inkTextRef.SetText( m_title, title );
	}

	public function GetSelectedIndex() : Int32
	{
		return m_selectedIndex;
	}

	public function FillGrid( const option : weak< CharacterCustomizationOption > )
	{
		const var appearanceInfo : gameuiAppearanceInfo;
		var i : Int32;
		var colorPickerItem : weak< inkWidget >;
		var colorPickerItemController : weak< characterCreationBodyMorphOptionColorPickerItem >;
		inkUniformGridRef.RemoveAllChildren( m_grid );
		appearanceInfo = ( ( gameuiAppearanceInfo )( option.info ) );
		if( appearanceInfo && appearanceInfo.useThumbnails )
		{
			m_option = option;
			m_selectedIndex = ( ( Int32 )( option.currIndex ) );
			for( i = 0; i < appearanceInfo.definitions.Size(); i += 1 )
			{
				colorPickerItem = SpawnFromLocal( inkUniformGridRef.Get( m_grid ), 'ColorPickerItem' );
				colorPickerItemController = ( ( characterCreationBodyMorphOptionColorPickerItem )( colorPickerItem.GetController() ) );
				colorPickerItemController.SetTintColor( appearanceInfo.definitions[ i ].color, appearanceInfo.definitions[ i ].icon );
				colorPickerItemController.SetSelected( i == m_selectedIndex, false );
				colorPickerItemController.RegisterToCallback( 'OnSelect', this, 'OnColorSelected' );
			}
		}
		else
		{
			m_option = NULL;
			m_selectedIndex = -1;
		}
	}

	public function MoveCursorToSelected()
	{
		var colorPickerItem : weak< inkWidget >;
		var colorPickerItemController : weak< characterCreationBodyMorphOptionColorPickerItem >;
		if( ( m_selectedIndex == -1 ) || ( inkUniformGridRef.GetNumChildren( m_grid ) <= m_selectedIndex ) )
		{
			return;
		}
		colorPickerItem = inkUniformGridRef.GetWidgetByIndex( m_grid, m_selectedIndex );
		colorPickerItemController = ( ( characterCreationBodyMorphOptionColorPickerItem )( colorPickerItem.GetController() ) );
		colorPickerItemController.MoveCursorToItem();
	}

}

