enum CharacterScreenType
{
	Attributes = 0,
	Perks = 1,
}

class PerksMainGameController extends gameuiMenuGameController
{
	private editable var m_tooltipsManagerRef : inkWidgetRef;
	private editable var m_buttonHintsManagerRef : inkWidgetRef;
	private editable var m_playerLevel : inkTextRef;
	private const editable var m_centerHiglightParts : array< inkWidgetRef >;
	private editable var m_attributeSelectorsContainer : inkWidgetRef;
	private editable var m_perksScreen : inkWidgetRef;
	private editable var m_pointsDisplay : inkWidgetRef;
	private editable var m_johnnyConnectorRef : inkWidgetRef;
	private editable var m_attributeTooltipHolderRight : inkWidgetRef;
	private editable var m_attributeTooltipHolderLeft : inkWidgetRef;
	private editable var m_respecButtonContainer : inkWidgetRef;
	private editable var m_cantRespecNotificationContainer : inkWidgetRef;
	private editable var m_resetPrice : inkTextRef;
	private editable var m_spentPerks : inkTextRef;
	private var m_activeScreen : CharacterScreenType;
	private var m_tooltipsManager : weak< gameuiTooltipsManager >;
	private var m_buttonHintsController : weak< ButtonHints >;
	private var m_dataManager : PlayerDevelopmentDataManager;
	private var m_menuEventDispatcher : weak< inkMenuEventDispatcher >;
	private var m_perksMenuItemCreatedQueue : array< PerksMenuAttributeItemCreated >;
	private var m_attributesControllersList : array< weak< PerksMenuAttributeItemController > >;
	private var m_playerStatsBlackboard : weak< IBlackboard >;
	private var m_characterLevelListener : CallbackHandle;
	private var m_perksScreenController : weak< PerkScreenController >;
	private var m_pointsDisplayController : weak< PerksPointsDisplayController >;
	private var m_questSystem : weak< QuestsSystem >;
	private var m_equipmentSystem : weak< EquipmentSystem >;
	private var m_resetConfirmationToken : inkGameNotificationToken;
	private var m_inCombat : Bool;
	private var enoughMoneyForRespec : Bool;
	private var m_cantRespecAnim : inkAnimProxy;

	protected event OnInitialize()
	{
		var refreshAreas : RefreshPerkAreas;
		m_perksScreenController = ( ( PerkScreenController )( inkWidgetRef.GetController( m_perksScreen ) ) );
		m_pointsDisplayController = ( ( PerksPointsDisplayController )( inkWidgetRef.GetController( m_pointsDisplay ) ) );
		m_dataManager = new PlayerDevelopmentDataManager;
		m_dataManager.Initialize( ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( GetPlayerControlledObject().GetGame() ).GetLocalPlayerMainGameObject() ) ), this );
		m_buttonHintsController = ( ( ButtonHints )( SpawnFromExternal( inkWidgetRef.Get( m_buttonHintsManagerRef ), R"base\gameplay\gui\common\buttonhints.inkwidget", 'Root' ).GetController() ) );
		m_buttonHintsController.AddButtonHint( 'back', GetLocalizedText( "Common-Access-Close" ) );
		m_playerStatsBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_PlayerStats );
		m_characterLevelListener = m_playerStatsBlackboard.RegisterDelayedListenerInt( GetAllBlackboardDefs().UI_PlayerStats.Level, this, 'OnCharacterLevelUpdated', true );
		m_inCombat = m_dataManager.GetPlayer().GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat ) == ( ( Int32 )( gamePSMCombat.InCombat ) );
		PrepareTooltips();
		SetupLayout();
		ResetData();
		HandleEventQueue();
		ResetHighlightPartsVisibility();
		refreshAreas = new RefreshPerkAreas;
		refreshAreas.Set( m_dataManager.GetPlayer() );
		m_dataManager.GetPlayerDevelopmentSystem().QueueRequest( refreshAreas );
		inkWidgetRef.RegisterToCallback( m_respecButtonContainer, 'OnRelease', this, 'OnResetPerksClick' );
		inkWidgetRef.RegisterToCallback( m_respecButtonContainer, 'OnHoverOver', this, 'OnResetPerksHoverOver' );
		inkWidgetRef.RegisterToCallback( m_respecButtonContainer, 'OnHoverOut', this, 'OnResetPerksHoverOut' );
	}

	protected event OnUnitialize()
	{
		m_playerStatsBlackboard.UnregisterDelayedListener( GetAllBlackboardDefs().UI_PlayerStats.Level, m_characterLevelListener );
		inkWidgetRef.UnregisterFromCallback( m_respecButtonContainer, 'OnRelease', this, 'OnResetPerksClick' );
		inkWidgetRef.UnregisterFromCallback( m_respecButtonContainer, 'OnHoverOver', this, 'OnResetPerksHoverOver' );
		inkWidgetRef.UnregisterFromCallback( m_respecButtonContainer, 'OnHoverOut', this, 'OnResetPerksHoverOut' );
	}

	protected event OnCharacterLevelUpdated( value : Int32 )
	{
		inkTextRef.SetText( m_playerLevel, IntToString( value ) );
	}

	protected function ResetHighlightPartsVisibility()
	{
		var i : Int32;
		for( i = 0; i < m_centerHiglightParts.Size(); i += 1 )
		{
			inkWidgetRef.SetOpacity( m_centerHiglightParts[ i ], 0.002 );
		}
	}

	protected function HandleEventQueue()
	{
		var i : Int32;
		for( i = 0; i < m_perksMenuItemCreatedQueue.Size(); i += 1 )
		{
			OnPerksMenuAttributeItemCreated( m_perksMenuItemCreatedQueue[ i ] );
		}
	}

	protected event OnPlayerAttach( playerPuppet : GameObject )
	{
		m_questSystem = GameInstance.GetQuestsSystem( playerPuppet.GetGame() );
		m_equipmentSystem = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( playerPuppet.GetGame() ).Get( 'EquipmentSystem' ) ) );
		CheckJohnnyFact();
	}

	private function CheckJohnnyFact()
	{
		var i : Int32;
		var hasJohnny : Bool;
		var attributeController : PerksMenuAttributeItemController;
		if( m_questSystem )
		{
			hasJohnny = m_questSystem.GetFact( 'q005_johnny_chip_acquired' ) == 1;
			for( i = 0; i < m_attributesControllersList.Size(); i += 1 )
			{
				attributeController = m_attributesControllersList[ i ];
				if( attributeController.GetAttributeType() == PerkMenuAttribute.Johnny )
				{
					attributeController.GetRootWidget().SetVisible( hasJohnny );
					inkWidgetRef.SetVisible( m_johnnyConnectorRef, !( hasJohnny ) );
				}
			}
		}
	}

	protected event OnPerksMenuAttributeItemCreated( evt : PerksMenuAttributeItemCreated )
	{
		if( m_dataManager )
		{
			evt.perksMenuAttributeItem.Setup( m_dataManager );
			m_attributesControllersList.PushBack( evt.perksMenuAttributeItem );
		}
		else
		{
			m_perksMenuItemCreatedQueue.PushBack( evt );
		}
		CheckJohnnyFact();
	}

	protected event OnUninitialize()
	{
		m_menuEventDispatcher.UnregisterFromEvent( 'OnBack', this, 'OnBack' );
	}

	protected event OnSetMenuEventDispatcher( menuEventDispatcher : weak< inkMenuEventDispatcher > )
	{
		m_menuEventDispatcher = menuEventDispatcher;
		m_menuEventDispatcher.RegisterToEvent( 'OnBack', this, 'OnBack' );
	}

	protected event OnBack( userData : IScriptable )
	{
		switch( m_activeScreen )
		{
			case CharacterScreenType.Perks:
				SetActiveScreen( CharacterScreenType.Attributes );
			break;
			case CharacterScreenType.Attributes:
				if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayerControlledObject(), 'LockInHubMenu' ) ) )
				{
					m_menuEventDispatcher.SpawnEvent( 'OnCloseHubMenu' );
					if( GetFact( m_dataManager.GetPlayer().GetGame(), 'PendingRespec' ) == 1 )
					{
						EquipmentSystem.UnequipPrereqItems( m_dataManager.GetPlayer() );
						SetFactValue( m_dataManager.GetPlayer().GetGame(), 'PendingRespec', 0 );
					}
				}
			break;
		}
	}

	private function SetupLayout()
	{
		SetActiveScreen( CharacterScreenType.Attributes );
	}


	private var m_lastHoveredAttribute : PerkMenuAttribute;

	protected event OnAttributeHoverOver( evt : PerksMenuAttributeItemHoverOver )
	{
		var tooltipHolder : inkWidgetRef;
		switch( evt.attributeType )
		{
			case PerkMenuAttribute.Reflex:
			case PerkMenuAttribute.Body:
			case PerkMenuAttribute.Intelligence:
				tooltipHolder = m_attributeTooltipHolderRight;
			break;
			case PerkMenuAttribute.Technical_Ability:
			case PerkMenuAttribute.Cool:
				tooltipHolder = m_attributeTooltipHolderLeft;
			break;
			default:
				tooltipHolder = m_attributeTooltipHolderRight;
			break;
		}
		PlayHoverAnimation( true );
		SetAttributeBuyButtonHintHoverOver( evt.attributeData );
		if( evt.attributeType != PerkMenuAttribute.Johnny )
		{
			ShowTooltip( inkWidgetRef.Get( tooltipHolder ), evt.attributeData, gameuiETooltipPlacement.RightCenter );
		}
		else
		{
			HideTooltip();
		}
		m_lastHoveredAttribute = evt.attributeType;
	}

	protected event OnAttributeHoverOut( evt : PerksMenuAttributeItemHoverOut )
	{
		if( m_lastHoveredAttribute == evt.attributeType )
		{
			PlayHoverAnimation( false );
			SetAttributeBuyButtonHintHoverOut();
			HideTooltip();
		}
	}

	protected event OnAttributeHoldStart( evt : PerksMenuAttributeItemHoldStart )
	{
		if( evt.actionName.IsAction( 'upgrade_attribute' ) )
		{
			PlaySound( 'Attributes', 'OnStart' );
		}
		if( evt.actionName.IsAction( 'upgrade_attribute' ) && !( m_dataManager.HasAvailableAttributePoints( true ) ) )
		{
		}
	}

	protected event OnAttributeClicked( evt : PerksMenuAttributeItemClicked )
	{
		var data : AttributeDisplayData;
		if( evt.attributeType != PerkMenuAttribute.Johnny )
		{
			data = m_dataManager.GetAttributeData( evt.attributeData.id );
			m_perksScreenController.Setup( data, m_dataManager, 0 );
			SetActiveScreen( CharacterScreenType.Perks );
		}
	}

	protected event OnProficiencyClicked( evt : PerksMenuProficiencyItemClicked )
	{
		var data : AttributeDisplayData;
		if( evt.attributeType != PerkMenuAttribute.Johnny )
		{
			data = m_dataManager.GetAttributeData( evt.attributeData.id );
			m_perksScreenController.Setup( data, m_dataManager, evt.index );
			SetActiveScreen( CharacterScreenType.Perks );
		}
	}

	protected event OnAttributePurchaseRequest( evt : AttributeUpgradePurchased )
	{
		m_dataManager.UpgradeAttribute( evt.attributeData );
	}

	protected event OnAttributePurchased( evt : AttributeBoughtEvent )
	{
		if( evt.attributeType != gamedataStatType.Invalid )
		{
			PlaySound( 'Attributes', 'OnDone' );
		}
		else
		{
			PlaySound( 'Attributes', 'OnFail' );
		}
	}

	protected event OnPerkHoverOver( evt : PerkHoverOverEvent )
	{
		SetPerksButtonHintHoverOver( evt.perkData );
		ShowTooltip( evt.widget, evt.perkData );
	}

	protected event OnPerkHoverOut( evt : PerkHoverOutEvent )
	{
		SetPerksButtonHintHoverOut();
		HideTooltip();
	}

	protected event OnPerkHoldStart( evt : PerksItemHoldStart )
	{
		PlaySound( 'Attributes', 'OnStart' );
		if( ( ( evt.actionName.IsAction( 'upgrade_attribute' ) || evt.actionName.IsAction( 'use_item' ) ) || evt.actionName.IsAction( 'click' ) ) && !( m_dataManager.IsPerkUpgradeable( evt.perkData, true ) ) )
		{
			PlaySound( 'Attributes', 'OnFail' );
		}
	}

	protected event OnPerkPurchased( evt : PerkBoughtEvent )
	{
		if( evt.perkType != gamedataPerkType.Invalid )
		{
			PlaySound( 'Attributes', 'OnDone' );
		}
		else
		{
			PlaySound( 'Attributes', 'OnFail' );
		}
	}

	protected event OnPlayerDevUpdateData( evt : PlayerDevUpdateDataEvent )
	{
		var i, j : Int32;
		var attributes : array< AttributeData >;
		UpdateAvailablePoints();
		attributes = m_dataManager.GetAttributes();
		for( i = 0; i < attributes.Size(); i += 1 )
		{
			for( j = 0; j < m_attributesControllersList.Size(); j += 1 )
			{
				if( m_attributesControllersList[ j ].GetStatType() == attributes[ i ].type )
				{
					m_attributesControllersList[ j ].UpdateData( attributes[ i ] );
					break;
				}
			}
		}
		m_tooltipsManager.RefreshTooltip( 0 );
		m_tooltipsManager.RefreshTooltip( 'perkTooltip' );
	}

	protected event OnActiveSkillScreenChanged( e : ActiveSkillScreenChangedEvent )
	{
		UpdateAvailablePoints();
	}

	protected event OnBackClick( controller : weak< inkButtonController > )
	{
		SetActiveScreen( CharacterScreenType.Attributes );
	}

	public function SetRespecButton( visible : Bool )
	{
		var spentPerkPoints : Int32;
		if( !( visible ) )
		{
			inkWidgetRef.SetVisible( m_respecButtonContainer, false );
			return;
		}
		spentPerkPoints = m_dataManager.GetSpentPerkPoints() + m_dataManager.GetSpentTraitPoints();
		inkWidgetRef.SetVisible( m_respecButtonContainer, spentPerkPoints > 0 );
		inkTextRef.SetText( m_spentPerks, IntToString( spentPerkPoints ) );
		inkTextRef.SetText( m_resetPrice, IntToString( m_dataManager.GetTotalRespecCost() ) );
		enoughMoneyForRespec = m_dataManager.CheckRespecCost();
		if( m_inCombat || !( enoughMoneyForRespec ) )
		{
			inkWidgetRef.SetState( m_respecButtonContainer, 'Disable' );
		}
		else
		{
			inkWidgetRef.SetState( m_respecButtonContainer, 'Default' );
		}
	}

	protected event OnResetPerksHoverOver( evt : inkPointerEvent )
	{
		if( !( m_inCombat ) && enoughMoneyForRespec )
		{
			inkWidgetRef.SetState( m_respecButtonContainer, 'Hover' );
		}
		else
		{
			inkWidgetRef.SetVisible( m_cantRespecNotificationContainer, true );
			if( m_cantRespecAnim )
			{
				m_cantRespecAnim.Stop();
			}
			m_cantRespecAnim = PlayLibraryAnimationOnTargets( 'tooltip_in', SelectWidgets( inkWidgetRef.Get( m_cantRespecNotificationContainer ) ) );
		}
	}

	protected event OnResetPerksHoverOut( evt : inkPointerEvent )
	{
		if( !( m_inCombat ) && enoughMoneyForRespec )
		{
			inkWidgetRef.SetState( m_respecButtonContainer, 'Default' );
		}
		else
		{
			inkWidgetRef.SetVisible( m_cantRespecNotificationContainer, false );
		}
	}

	protected event OnResetPerksClick( evt : inkPointerEvent )
	{
		var vendorNotification : UIMenuNotificationEvent;
		if( evt.IsAction( 'click' ) )
		{
			if( m_inCombat )
			{
				vendorNotification = new UIMenuNotificationEvent;
				vendorNotification.m_notificationType = UIMenuNotificationType.InventoryActionBlocked;
				QueueEvent( vendorNotification );
			}
			else if( m_dataManager.CheckRespecCost() )
			{
				m_resetConfirmationToken = GenericMessageNotification.Show( this, "UI-Menus-Perks-ResetPerks", "UI-Menus-Perks-ResetConfirmation", GenericMessageNotificationType.YesNo );
				m_resetConfirmationToken.RegisterListener( this, 'OnResetConfirmed' );
			}
			else
			{
				vendorNotification = new UIMenuNotificationEvent;
				vendorNotification.m_notificationType = UIMenuNotificationType.VNotEnoughMoney;
				QueueEvent( vendorNotification );
			}
		}
	}

	protected event OnResetConfirmed( data : inkGameNotificationData )
	{
		var resultData : GenericMessageNotificationCloseData;
		var removeAllPerks : RemoveAllPerks;
		resultData = ( ( GenericMessageNotificationCloseData )( data ) );
		m_resetConfirmationToken = NULL;
		if( resultData && resultData.result == GenericMessageNotificationResult.Yes )
		{
			removeAllPerks = new RemoveAllPerks;
			removeAllPerks.Set( m_dataManager.GetPlayer(), false, false );
			PlayerDevelopmentSystem.GetInstance( m_dataManager.GetPlayer() ).QueueRequest( removeAllPerks );
			SetFactValue( m_dataManager.GetPlayer().GetGame(), 'PendingRespec', 1 );
		}
	}

	protected event OnPerkResetEvent( evt : PerkResetEvent )
	{
		UpdateAvailablePoints();
	}

	private function ResetData()
	{
		SetupLayout();
		UpdateAvailablePoints();
	}

	private function SetActiveScreen( screenType : CharacterScreenType )
	{
		var isPerksScreen : Bool;
		m_activeScreen = screenType;
		isPerksScreen = m_activeScreen == CharacterScreenType.Perks;
		m_pointsDisplayController.Setup( m_activeScreen );
		UpdateAvailablePoints();
		inkWidgetRef.SetVisible( m_attributeSelectorsContainer, !( isPerksScreen ) );
		inkWidgetRef.SetVisible( m_perksScreen, isPerksScreen );
		GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_TopbarHubMenu ).SetBool( GetAllBlackboardDefs().UI_TopbarHubMenu.IsSubmenuHidden, isPerksScreen, true );
		m_perksScreenController.PlayLibraryAnimation( 'start_perk_screen' );
		if( !( isPerksScreen ) )
		{
			PlayLibraryAnimation( 'panel_intro' );
		}
	}

	private function UpdateAvailablePoints()
	{
		var developmentData : PlayerDevelopmentData;
		var proficiencyType : gamedataProficiencyType;
		switch( m_activeScreen )
		{
			case CharacterScreenType.Attributes:
				m_pointsDisplayController.SetValues( m_dataManager.GetAttributePoints(), m_dataManager.GetPerkPoints() );
			SetRespecButton( true );
			break;
			case CharacterScreenType.Perks:
				developmentData = PlayerDevelopmentSystem.GetData( m_dataManager.GetPlayer() );
			proficiencyType = m_perksScreenController.GetProficiencyDisplayData().m_proficiency;
			m_pointsDisplayController.SetValues( developmentData.GetInvestedPerkPoints( proficiencyType ), m_dataManager.GetPerkPoints() );
			SetRespecButton( false );
			break;
		}
	}

	private function PrepareTooltips()
	{
		m_tooltipsManager = ( ( gameuiTooltipsManager )( inkWidgetRef.GetControllerByType( m_tooltipsManagerRef, 'gameuiTooltipsManager' ) ) );
		m_tooltipsManager.Setup( ETooltipsStyle.Menus );
	}

	private function ShowTooltip( widget : weak< inkWidget >, data : IDisplayData, optional placement : gameuiETooltipPlacement )
	{
		var tooltipData : BasePerksMenuTooltipData;
		tooltipData = data.CreateTooltipData( m_dataManager );
		if( tooltipData == NULL )
		{
			return;
		}
		if( data.IsA( 'PerkDisplayData' ) || data.IsA( 'TraitDisplayData' ) )
		{
			m_tooltipsManager.ShowTooltipAtWidget( 'perkTooltip', widget, tooltipData, placement );
		}
		else
		{
			m_tooltipsManager.ShowTooltipAtWidget( 0, widget, tooltipData, placement );
		}
	}

	private function HideTooltip()
	{
		m_tooltipsManager.HideTooltips();
	}

	private function SetAttributeHintsHoverOver()
	{
		m_buttonHintsController.AddButtonHint( 'select', GetLocalizedText( "Common-Access-Select" ) );
	}

	private function SetAttributeHintsHoverOut()
	{
		m_buttonHintsController.RemoveButtonHint( 'select' );
	}

	private function SetPerksButtonHintHoverOver( data : BasePerkDisplayData )
	{
		var cursorData : MenuCursorUserData;
		cursorData = new MenuCursorUserData;
		cursorData.SetAnimationOverride( 'hoverOnHoldToComplete' );
		if( m_dataManager.IsPerkUpgradeable( data ) )
		{
			m_buttonHintsController.AddButtonHint( 'upgrade_perk', ( ( "[" + GetLocalizedText( "Gameplay-Devices-Interactions-Helpers-Hold" ) ) + "] " ) + GetLocalizedText( "UI-ScriptExports-Buy0" ) );
			cursorData.AddAction( 'upgrade_perk' );
		}
		if( cursorData.GetActionsListSize() >= 0 )
		{
			SetCursorContext( 'hoverOnHoldToComplete', cursorData );
		}
		else
		{
			SetCursorContext( 'Hover' );
		}
	}

	private function SetPerksButtonHintHoverOut()
	{
		m_buttonHintsController.RemoveButtonHint( 'select' );
		m_buttonHintsController.RemoveButtonHint( 'upgrade_perk' );
	}

	private function SetAttributeBuyButtonHintHoverOver( data : AttributeData )
	{
		var cursorData : MenuCursorUserData;
		cursorData = new MenuCursorUserData;
		cursorData.SetAnimationOverride( 'hoverOnHoldToComplete' );
		cursorData.AddAction( 'upgrade_attribute' );
		if( m_dataManager.HasAvailableAttributePoints() )
		{
			m_buttonHintsController.AddButtonHint( 'upgrade_perk', ( ( "(" + GetLocalizedText( "Gameplay-Devices-Interactions-Helpers-Hold" ) ) + ") " ) + GetLocalizedText( "UI-ScriptExports-Buy0" ) );
			SetCursorContext( 'hoverOnHoldToComplete', cursorData );
		}
		else
		{
			SetCursorContext( 'Hover' );
		}
	}

	private function SetAttributeBuyButtonHintHoverOut()
	{
		m_buttonHintsController.RemoveButtonHint( 'upgrade_perk' );
	}

	protected function PlayHoverAnimation( value : Bool )
	{
		var i : Int32;
		var transparencyAnimation : inkAnimDef;
		var transparencyInterpolator : inkAnimTransparency;
		transparencyAnimation = new inkAnimDef;
		transparencyInterpolator = new inkAnimTransparency;
		transparencyInterpolator.SetDuration( 0.34999999 );
		transparencyInterpolator.SetDirection( inkanimInterpolationDirection.To );
		transparencyInterpolator.SetType( inkanimInterpolationType.Linear );
		transparencyInterpolator.SetMode( inkanimInterpolationMode.EasyIn );
		transparencyInterpolator.SetEndTransparency( ( ( value ) ? ( 1.0 ) : ( 0.002 ) ) );
		transparencyAnimation.AddInterpolator( transparencyInterpolator );
		for( i = 0; i < m_centerHiglightParts.Size(); i += 1 )
		{
			inkWidgetRef.PlayAnimation( m_centerHiglightParts[ i ], transparencyAnimation );
		}
	}

}

