class CraftingMainGameController extends gameuiMenuGameController
{
	private editable var m_tooltipsManagerRef : inkWidgetRef;
	private editable var m_tabRootRef : inkWidgetRef;
	private editable var m_buttonHintsManagerRef : inkWidgetRef;
	private editable var m_craftingLogicControllerContainer : inkWidgetRef;
	private editable var m_upgradingLogicControllerContainer : inkWidgetRef;
	private var m_buttonHintsController : weak< ButtonHints >;
	private var m_player : weak< PlayerPuppet >;
	private var m_menuEventDispatcher : weak< inkMenuEventDispatcher >;
	private var m_craftingSystem : CraftingSystem;
	private var m_playerCraftBook : CraftBook;
	private var m_VendorDataManager : VendorDataManager;
	private var m_InventoryManager : InventoryDataManagerV2;
	private var m_uiScriptableSystem : weak< UIScriptableSystem >;
	private var m_tooltipsManager : weak< gameuiTooltipsManager >;
	private var m_craftingDef : UI_CraftingDef;
	private var m_craftingBlackboard : weak< IBlackboard >;
	private var m_craftingBBID : CallbackHandle;
	private var m_levelUpBlackboard : weak< IBlackboard >;
	private var m_playerLevelUpListener : CallbackHandle;
	private var m_mode : CraftingMode;
	private var m_isInitializeOver : Bool;
	private var m_craftingLogicController : weak< CraftingLogicController >;
	private var m_upgradingLogicController : weak< UpgradingScreenController >;
	private var m_tabRoot : weak< TabRadioGroup >;
	private var m_isTabEnabled : Bool;
	default m_isTabEnabled = true;

	protected event OnInitialize()
	{
		AsyncSpawnFromExternal( inkWidgetRef.Get( m_buttonHintsManagerRef ), R"base\gameplay\gui\common\buttonhints.inkwidget", 'Root', this, 'OnHintsControllerSpawned' );
		m_player = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		m_craftingSystem = ( ( CraftingSystem )( GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).Get( 'CraftingSystem' ) ) );
		m_playerCraftBook = m_craftingSystem.GetPlayerCraftBook();
		m_InventoryManager = new InventoryDataManagerV2;
		m_InventoryManager.Initialize( m_player );
		m_uiScriptableSystem = UIScriptableSystem.GetInstance( m_player.GetGame() );
		m_tooltipsManager = ( ( gameuiTooltipsManager )( inkWidgetRef.GetControllerByType( m_tooltipsManagerRef, 'gameuiTooltipsManager' ) ) );
		m_tooltipsManager.Setup( ETooltipsStyle.Menus );
		SetupBB();
		PlayLibraryAnimation( 'crafting_intro' );
		m_tabRoot = ( ( TabRadioGroup )( inkWidgetRef.GetController( m_tabRootRef ) ) );
		m_isInitializeOver = true;
	}

	protected event OnClickArrow( evt : ArrowClickedEvent )
	{
		MoveTab( evt.direction );
	}

	private function MoveTab( direction : Direction )
	{
		var index : Int32;
		var isCurrent : Bool;
		if( !( IsTabEnabled() ) )
		{
			return;
		}
		index = ( ( direction == Direction.Next ) ? ( GetNextTabIndex() ) : ( GetPreviousTabIndex() ) );
		m_tabRoot.Toggle( index );
		isCurrent = IsCurrentTab( index );
		m_craftingLogicController.OnChangeTab( isCurrent );
		m_upgradingLogicController.OnChangeTab( isCurrent );
	}

	private function GetNextTabIndex() : Int32
	{
		var current : Int32;
		current = m_tabRoot.GetCurrentIndex();
		current += 1;
		if( current > ( m_tabRoot.toggles.Size() - 1 ) )
		{
			current = 0;
		}
		return current;
	}

	public function IsCurrentTab( index : Int32 ) : Bool
	{
		return ( ( Int32 )( m_tabRoot.GetCurrentIndex() ) ) == index;
	}

	private function GetPreviousTabIndex() : Int32
	{
		var current : Int32;
		current = m_tabRoot.GetCurrentIndex();
		current -= 1;
		if( current < 0 )
		{
			current = m_tabRoot.toggles.Size() - 1;
		}
		return current;
	}

	protected event OnSetUserData( userData : IScriptable )
	{
		var vendorData : VendorData;
		var vendorUserData : VendorUserData;
		var craftingMenuData : CraftingUserData;
		if( userData == NULL )
		{
			return false;
		}
		vendorUserData = ( ( VendorUserData )( userData ) );
		if( vendorUserData )
		{
			vendorData = vendorUserData.vendorData.data;
			m_VendorDataManager = new VendorDataManager;
			m_VendorDataManager.Initialize( GetPlayerControlledObject(), vendorData.entityID );
			m_menuEventDispatcher.RegisterToEvent( 'OnBack', this, 'OnBack' );
		}
		craftingMenuData = ( ( CraftingUserData )( userData ) );
		if( craftingMenuData )
		{
			m_mode = craftingMenuData.Mode;
		}
	}

	public function GetPlayerLevel() : Float
	{
		return GameInstance.GetStatsSystem( m_player.GetGame() ).GetStatValue( m_player.GetEntityID(), gamedataStatType.Level );
	}

	protected event OnHintsControllerSpawned( widget : inkWidget, userData : IScriptable )
	{
		m_buttonHintsController = ( ( ButtonHints )( widget.GetController() ) );
		m_buttonHintsController.AddButtonHint( 'back', GetLocalizedText( "Common-Access-Close" ) );
		m_craftingLogicController = ( ( CraftingLogicController )( inkWidgetRef.GetControllerByType( m_craftingLogicControllerContainer, 'CraftingLogicController' ) ) );
		m_craftingLogicController.Init( this );
		m_upgradingLogicController = ( ( UpgradingScreenController )( inkWidgetRef.GetControllerByType( m_upgradingLogicControllerContainer, 'UpgradingScreenController' ) ) );
		m_upgradingLogicController.Init( this );
		RegisterTabButtons();
	}

	public function GetScriptableSystem() : weak< UIScriptableSystem >
	{
		return m_uiScriptableSystem;
	}

	public function GetPlayer() : weak< PlayerPuppet >
	{
		return m_player;
	}

	public function GetInventoryManager() : weak< InventoryDataManagerV2 >
	{
		return m_InventoryManager;
	}

	public function GetCraftingSystem() : weak< CraftingSystem >
	{
		return m_craftingSystem;
	}

	public function GetTooltipManager() : weak< gameuiTooltipsManager >
	{
		return m_tooltipsManager;
	}

	public function GetButtonHintsController() : weak< ButtonHints >
	{
		return m_buttonHintsController;
	}

	public function EnableTabs()
	{
		m_isTabEnabled = true;
	}

	public function IsTabEnabled() : Bool
	{
		return m_isTabEnabled;
	}

	public function DisableTabs()
	{
		m_isTabEnabled = false;
	}

	protected function RegisterTabButtons()
	{
		var labels : array< String >;
		m_tabRoot.RegisterToCallback( 'OnValueChanged', this, 'OnValueChanged' );
		RegisterToGlobalInputCallback( 'OnPostOnRelease', this, 'OnSubMenuRelease' );
		labels.PushBack( "UI-ResourceExports-Crafting" );
		labels.PushBack( "UI-PanelNames-UPGRADING" );
		m_tabRoot.SetData( labels.Size(), NULL, labels );
	}

	protected event OnSubMenuRelease( evt : inkPointerEvent )
	{
		if( evt.IsHandled() )
		{
			return true;
		}
		if( evt.IsAction( InputMenuNavigationActionHelper.GetPreviousSubActionName() ) )
		{
			MoveTab( Direction.Previous );
		}
		else if( evt.IsAction( InputMenuNavigationActionHelper.GetNextSubActionName() ) )
		{
			MoveTab( Direction.Next );
		}
		evt.Handle();
	}

	private function SetupBB()
	{
		m_craftingDef = GetAllBlackboardDefs().UI_Crafting;
		m_craftingBlackboard = GetBlackboardSystem().Get( m_craftingDef );
		if( m_craftingBlackboard )
		{
			m_craftingBBID = m_craftingBlackboard.RegisterDelayedListenerVariant( m_craftingDef.lastItem, this, 'OnCraftingComplete', true );
		}
		m_levelUpBlackboard = GetBlackboardSystem().Get( GetAllBlackboardDefs().UI_LevelUp );
		if( m_levelUpBlackboard )
		{
			m_playerLevelUpListener = m_levelUpBlackboard.RegisterListenerVariant( GetAllBlackboardDefs().UI_LevelUp.level, this, 'OnCharacterLevelUpdated' );
		}
	}

	private function RemoveBB()
	{
		if( m_craftingBlackboard )
		{
			m_craftingBlackboard.UnregisterDelayedListener( m_craftingDef.lastItem, m_craftingBBID );
		}
		if( m_levelUpBlackboard )
		{
			m_levelUpBlackboard.UnregisterListenerVariant( GetAllBlackboardDefs().UI_LevelUp.level, m_playerLevelUpListener );
		}
		m_craftingBlackboard = NULL;
		m_levelUpBlackboard = NULL;
	}

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

	protected event OnValueChanged( controller : weak< inkRadioGroupController >, selectedIndex : Int32 )
	{
		SelectTab( selectedIndex );
	}

	private function SelectTab( selectedIndex : Int32 )
	{
		switch( selectedIndex )
		{
			case 0:
				OpenCraftingMode();
			break;
			case 1:
				OpenUpgradeMode();
			break;
		}
		PlaySound( 'TabButton', 'OnPress' );
	}

	private function RefreshUI( optional inventoryItemData : InventoryItemData )
	{
		switch( m_mode )
		{
			case CraftingMode.craft:
				m_craftingLogicController.RefreshListViewContent( inventoryItemData );
			break;
			case CraftingMode.upgrade:
				m_upgradingLogicController.RefreshListViewContent( inventoryItemData );
			break;
		}
	}

	private function OpenCraftingMode()
	{
		m_mode = CraftingMode.craft;
		m_craftingLogicController.OpenPanel();
		m_upgradingLogicController.ClosePanel();
	}

	private function OpenUpgradeMode()
	{
		m_mode = CraftingMode.upgrade;
		m_upgradingLogicController.OpenPanel();
		m_craftingLogicController.ClosePanel();
	}

	protected event OnBack( userData : IScriptable )
	{
		PlayLibraryAnimation( 'crafting_outro' );
		if( !( StatusEffectSystem.ObjectHasStatusEffectWithTag( GetPlayerControlledObject(), 'LockInHubMenu' ) ) )
		{
			super.OnBack( userData );
		}
	}

	protected event OnTransferToPerkSreen( evt : inkPointerEvent )
	{
		if( evt.IsAction( 'upgrade_attribute' ) )
		{
			m_menuEventDispatcher.SpawnEvent( 'OnSwitchToPerk' );
		}
	}

	protected event OnCraftingComplete( value : Variant )
	{
		var command : CraftingCommands;
		var inventoryItemData : InventoryItemData;
		var itemID : ItemID;
		var itemData : weak< gameItemData >;
		itemID = ( ( ItemID )value );
		if( m_isInitializeOver )
		{
			m_InventoryManager.MarkToRebuild();
			m_InventoryManager.ClearInventoryItemDataCache();
			command = ( ( CraftingCommands )( m_craftingBlackboard.GetVariant( GetAllBlackboardDefs().UI_Crafting.lastCommand ) ) );
			itemData = GameInstance.GetTransactionSystem( m_player.GetGame() ).GetItemData( m_player, itemID );
			inventoryItemData = m_InventoryManager.GetInventoryItemData( itemData );
			GameInstance.GetTelemetrySystem( m_player.GetGame() ).LogItemCrafting( ToTelemetryInventoryItem( inventoryItemData ), EnumValueToName( 'CraftingCommands', ( ( Int64 )( command ) ) ) );
			if( command == CraftingCommands.UpgradingFinished && !( RPGManager.IsItemMaxTier( itemData ) ) )
			{
				RefreshUI( inventoryItemData );
			}
			else
			{
				RefreshUI();
				PlayLibraryAnimation( 'craft_complete_celebration' );
			}
		}
	}

	protected event OnCharacterLevelUpdated( value : Variant )
	{
		var levelUpData : LevelUpData;
		levelUpData = ( ( LevelUpData )value );
		if( levelUpData.type == gamedataProficiencyType.TechnicalAbilitySkill )
		{
			RefreshUI();
		}
	}

	protected event OnUninitialize()
	{
		RemoveBB();
		m_menuEventDispatcher.UnregisterFromEvent( 'OnBack', this, 'OnBack' );
		UnregisterFromGlobalInputCallback( 'OnPostOnRelease', this, 'OnSubMenuRelease' );
	}

}

class InputMenuNavigationActionHelper
{

	public constexpr static function GetPreviousSubActionName() : CName
	{
		return 'option_switch_prev_settings';
	}

	public constexpr static function GetNextSubActionName() : CName
	{
		return 'option_switch_next_settings';
	}

}

enum Direction
{
	Next = 0,
	Previous = 1,
}

class ArrowClickedEvent extends Event
{
	var direction : Direction;
}

class ArrowButton extends inkButtonController
{
	private editable var m_direction : Direction;

	protected event OnInitialize()
	{
		RegisterToCallback( 'OnClick', this, 'OnClick' );
		RegisterToCallback( 'OnHoverOut', this, 'OnHoverOut' );
		RegisterToCallback( 'OnHoverOver', this, 'OnHoverOver' );
	}

	protected event OnUninitialize()
	{
		UnregisterFromCallback( 'OnClick', this, 'OnClick' );
		UnregisterFromCallback( 'OnHoverOut', this, 'OnHoverOut' );
		UnregisterFromCallback( 'OnHoverOver', this, 'OnHoverOver' );
	}

	private function OnClick( evt : inkPointerEvent )
	{
		var clickEvent : ArrowClickedEvent;
		clickEvent = new ArrowClickedEvent;
		if( evt.IsAction( 'click' ) )
		{
			clickEvent.direction = m_direction;
			SetState( 'Press' );
			QueueEvent( clickEvent );
			evt.Consume();
		}
	}

	private function OnHoverOut( evt : inkPointerEvent )
	{
		SetState( 'Default' );
	}

	private function OnHoverOver( evt : inkPointerEvent )
	{
		SetState( 'Hover' );
	}

	private function SetState( stateName : CName )
	{
		GetRootWidget().SetState( stateName );
	}

}

class IngredientListItemLogicController extends inkButtonController
{
	private editable var m_itemName : inkTextRef;
	private editable var m_inventoryQuantity : inkTextRef;
	private editable var m_ingredientQuantity : inkTextRef;
	private editable var m_availability : inkTextRef;
	private editable var m_icon : inkImageRef;
	private editable var m_emptyIcon : inkImageRef;
	private const editable var m_availableBgElements : array< inkWidgetRef >;
	private const editable var m_unavailableBgElements : array< inkWidgetRef >;
	private editable var m_buyButton : inkWidgetRef;
	private editable var m_countWrapper : inkWidgetRef;
	private editable var m_itemRarity : inkWidgetRef;
	private var m_data : IngredientData;
	private var m_root : weak< inkWidget >;
	private var m_TooltipsManager : weak< gameuiTooltipsManager >;
	private var m_itemAmount : Int32;

	protected event OnInitialize()
	{
		m_root = GetRootWidget();
		RegisterToCallback( 'OnHoverOver', this, 'OnDisplayHoverOver' );
		RegisterToCallback( 'OnHoverOut', this, 'OnDisplayHoverOut' );
	}

	public function SetupData( const data : ref< IngredientData >, tooltipsManager : weak< gameuiTooltipsManager >, itemAmount : Int32 )
	{
		var quality : CName;
		m_data = data;
		m_TooltipsManager = tooltipsManager;
		m_root.SetVisible( true );
		m_itemAmount = itemAmount;
		if( m_data.quantity > m_data.inventoryQuantity )
		{
			m_root.SetState( 'Unavailable' );
			m_data.hasEnoughQuantity = false;
		}
		else
		{
			m_root.SetState( 'Default' );
			m_data.hasEnoughQuantity = true;
		}
		inkWidgetRef.SetVisible( m_countWrapper, true );
		inkTextRef.SetLocalizedTextScript( m_itemName, m_data.id.DisplayName() );
		inkTextRef.SetText( m_inventoryQuantity, ( ( String )( m_data.inventoryQuantity ) ) );
		inkTextRef.SetText( m_ingredientQuantity, ( ( String )( m_data.quantity * m_itemAmount ) ) );
		inkTextRef.SetText( m_availability, ( ( m_data.hasEnoughQuantity ) ? ( "available" ) : ( "missing" ) ) );
		inkImageRef.SetVisible( m_emptyIcon, false );
		inkImageRef.SetVisible( m_icon, true );
		InkImageUtils.RequestSetImage( this, m_icon, "UIIcon." + m_data.id.IconPath() );
		inkImageRef.SetVisible( m_emptyIcon, false );
		inkWidgetRef.SetVisible( m_itemRarity, true );
		quality = StringToName( m_data.id.Quality().Name() );
		inkWidgetRef.SetState( m_itemRarity, quality );
	}

	public function SetUnusedState()
	{
		m_root.SetVisible( false );
	}

	protected event OnDisplayHoverOver( evt : inkPointerEvent )
	{
		var tooltipData : MaterialTooltipData;
		tooltipData = new MaterialTooltipData;
		tooltipData.Title = GetLocalizedText( LocKeyToString( m_data.id.DisplayName() ) );
		tooltipData.BaseMaterialQuantity = m_data.baseQuantity * m_itemAmount;
		tooltipData.MaterialQuantity = m_data.quantity * m_itemAmount;
		m_TooltipsManager.ShowTooltipAtWidget( 'materialTooltip', evt.GetTarget(), tooltipData, gameuiETooltipPlacement.RightTop, true );
	}

	protected event OnDisplayHoverOut( evt : inkPointerEvent )
	{
		m_TooltipsManager.HideTooltips();
	}

	public function GetData() : IngredientData
	{
		return m_data;
	}

}

class CraftableItemLogicController extends inkVirtualCompoundItemController
{
	protected editable var m_normalAppearence : inkCompoundRef;
	private var m_controller : weak< InventoryItemDisplayController >;
	var m_itemData : ItemCraftingData;
	var m_recipeData : RecipeData;
	private var m_isSpawnInProgress : Bool;
	private var m_displayToCreate : CName;

	protected event OnInitialize()
	{
		RegisterToCallback( 'OnSelected', this, 'OnSelected' );
		RegisterToCallback( 'OnToggledOn', this, 'OnToggledOn' );
		RegisterToCallback( 'OnToggledOff', this, 'OnToggledOff' );
	}

	public export function OnDataChanged( value : Variant )
	{
		var displayToCreate : CName;
		m_itemData = ( ( ItemCraftingData )( ( ( IScriptable )value ) ) );
		m_recipeData = ( ( RecipeData )( ( ( IScriptable )value ) ) );
		displayToCreate = 'itemDisplay';
		if( m_itemData && CraftingMainLogicController.IsWeapon( InventoryItemData.GetEquipmentArea( m_itemData.inventoryItem ) ) )
		{
			displayToCreate = 'weaponDisplay';
		}
		if( m_recipeData && CraftingMainLogicController.IsWeapon( m_recipeData.id.EquipArea().Type() ) )
		{
			displayToCreate = 'weaponDisplay';
		}
		if( !( m_isSpawnInProgress ) )
		{
			if( m_displayToCreate != displayToCreate || !( m_controller ) )
			{
				m_isSpawnInProgress = true;
				inkCompoundRef.RemoveAllChildren( m_normalAppearence );
				ItemDisplayUtils.SpawnCommonSlotAsync( this, m_normalAppearence, displayToCreate, 'OnSlotSpawned' );
				m_displayToCreate = displayToCreate;
			}
			else
			{
				UpdateControllerData();
			}
		}
	}

	protected event OnSlotSpawned( widget : inkWidget, userData : IScriptable )
	{
		m_isSpawnInProgress = false;
		m_controller = ( ( InventoryItemDisplayController )( widget.GetController() ) );
		m_controller.RegisterToCallback( 'OnHoverOver', this, 'OnDisplayHoverOver' );
		UpdateControllerData();
	}

	private function UpdateControllerData()
	{
		if( m_itemData )
		{
			m_controller.Setup( m_itemData.inventoryItem, ItemDisplayContext.Crafting, , , m_itemData.isUpgradable );
			SelectSlot( m_itemData.isSelected );
		}
		if( m_recipeData )
		{
			m_controller.Setup( m_recipeData, ItemDisplayContext.Crafting );
			m_controller.SetIsNew( m_recipeData.isNew );
			SelectSlot( m_recipeData.isSelected );
		}
		inkCompoundRef.SetVisible( m_normalAppearence, true );
	}

	private function SelectSlot( select : Bool )
	{
		if( m_controller )
		{
			SetSelected( select );
			m_controller.SetHighlighted( select );
			UpdateHightlightColor();
			if( m_itemData )
			{
				m_itemData.isSelected = select;
			}
			if( m_recipeData )
			{
				m_recipeData.isSelected = select;
			}
		}
	}

	protected event OnToggledOn( itemController : weak< inkVirtualCompoundItemController > )
	{
		SelectSlot( true );
	}

	protected event OnToggledOff( itemController : weak< inkVirtualCompoundItemController > )
	{
		SelectSlot( false );
	}

	protected event OnSelected( itemController : weak< inkVirtualCompoundItemController >, discreteNav : Bool )
	{
		if( m_recipeData )
		{
			m_recipeData.isNew = false;
		}
	}

	public function UpdateHightlightColor()
	{
		var isCraftable : Bool;
		var color : CName;
		isCraftable = m_recipeData.isCraftable || m_itemData.isUpgradable;
		color = ( ( isCraftable ) ? ( 'MainColors.ActiveBlue' ) : ( 'MainColors.ActiveRed' ) );
		if( m_controller )
		{
			m_controller.SetHighlightColor( color );
		}
	}

}

struct IngredientData
{
	var label : String;
	var quantity : Int32;
	var baseQuantity : Int32;
	var itemAmount : Int32;
	var inventoryQuantity : Int32;
	var id : Item_Record;
	var icon : String;
	var iconGender : ItemIconGender;
	var playerSelectableIngredient : Bool;
	var buyableIngredient : Bool;
	var hasEnoughQuantity : Bool;
}

class RecipeData extends IScriptable
{
	var label : String;
	var ingredients : array< IngredientData >;
	var icon : String;
	var iconGender : ItemIconGender;
	var description : String;
	var type : String;
	var id : Item_Record;
	var isCraftable : Bool;
	var inventoryItem : InventoryItemData;
	var amount : Int32;
	var isNew : Bool;
	var isSelected : Bool;
	var quality : gamedataQuality;
}

class ItemCraftingData extends IScriptable
{
	var inventoryItem : InventoryItemData;
	var isUpgradable : Bool;
	var isNew : Bool;
	var isSelected : Bool;
}

enum CraftingMode
{
	craft = 0,
	upgrade = 1,
}

class CraftingUserData extends IScriptable
{
	var Mode : CraftingMode;

	public static function Make( mode : CraftingMode ) : CraftingUserData
	{
		var instance : CraftingUserData;
		instance = new CraftingUserData;
		instance.Mode = mode;
		return instance;
	}

}

