class UIInventoryItemInternalData
{
	var Name : String;
	var Quality : gamedataQuality;
	var ItemType : gamedataItemType;
	var Quantity : Int32;
	var Description : String;
	var IconPath : String;
	var IsQuestItem : Bool;
	var IsRecipeItem : Bool;
	var IsIconicItem : Bool;
	var EquipmentArea : gamedataEquipmentArea;
	var FilterCategory : ItemFilterCategory;
	var PrimaryStat : UIInventoryItemStat;
	var ItemTypeOrder : Int32;
	var Weight : Float;
	var ItemPlus : Float;
	var StatsManager : UIInventoryItemStatsManager;
	var ModsManager : UIInventoryItemModsManager;
	var RequirementsManager : UIInventoryItemRequirementsManager;
	var UIItemCategory : UIItemCategory;
	var ComparisonQuality : Float;
	var QualityText : String;
	var IsSellable : Bool;
	var IsBroken : Bool;
}

class UIInventoryWeaponInternalData
{
	var m_fetchedFlags : Int32;
	var WeaponType : WeaponType;
	var Evolution : gamedataWeaponEvolution;
	var PerkGroup : gamedataPerkWeaponGroupType;
	var GameplayDescription : String;
	var EmptySlots : array< TweakDBID >;
	var UsedSlots : array< TweakDBID >;
	var HasSilencerSlot : Bool;
	var HasScopeSlot : Bool;
	var HasSilencerInstalled : Bool;
	var HasScopeInstalled : Bool;
	var DamageMin : Float;
	var DamageMax : Float;
	var AttackSpeed : Float;
	var NumberOfPellets : Int32;
	var Ammo : Int32;
}

class UIInventoryItemProgramData
{
	var MemoryCost : Int32;
	var BaseCost : Int32;
	var UploadTime : Float;
	var Duration : Float;
	var Cooldown : Float;
	var AttackEffects : array< DamageEffectUIEntry >;

	private static function GetDurationAndAttackEffects( instance : weak< UIInventoryItemProgramData >, actionRecord : weak< ObjectAction_Record >, player : weak< PlayerPuppet > )
	{
		var i, j, limit, limitJ : Int32;
		var durationMods : array< weak< ObjectActionEffect_Record > >;
		var effects : array< DamageEffectUIEntry >;
		var effectToCast : weak< StatusEffect_Record >;
		var ignoredDurationStats : array< weak< StatusEffect_Record > >;
		var statModifiers : array< weak< StatModifier_Record > >;
		var duration : weak< StatModifierGroup_Record >;
		var lastMatchingEffect : weak< StatusEffect_Record >;
		var emptyObject : GameObject;
		var dummyEntityID : EntityID;
		ignoredDurationStats.PushBack( TweakDBInterface.GetStatusEffectRecord( T"BaseStatusEffect.WasQuickHacked" ) );
		ignoredDurationStats.PushBack( TweakDBInterface.GetStatusEffectRecord( T"BaseStatusEffect.QuickHackUploaded" ) );
		actionRecord.CompletionEffects( durationMods );
		for( i = 0, limit = durationMods.Size(); i < limit; i += 1 )
		{
			if( !( InventoryDataManagerV2.ProcessQuickhackEffects( player, durationMods[ i ].StatusEffect(), effects ) ) )
			{
				continue;
			}
			for( j = 0, limitJ = effects.Size(); j < limitJ; j += 1 )
			{
				instance.AttackEffects.PushBack( effects[ j ] );
			}
		}
		if( durationMods.Size() > 0 )
		{
			for( i = 0, limit = durationMods.Size(); i < limit; i += 1 )
			{
				effectToCast = durationMods[ i ].StatusEffect();
				if( effectToCast )
				{
					if( !( ignoredDurationStats.Contains( effectToCast ) ) )
					{
						lastMatchingEffect = effectToCast;
					}
				}
				else if( durationMods[ i ].EffectorToTrigger() && durationMods[ i ].EffectorToTrigger().EffectorClassName() == 'ApplyLegendaryWhistleEffector' )
				{
					lastMatchingEffect = TweakDBInterface.GetStatusEffectRecord( T"BaseStatusEffect.WhistleLvl4" );
				}
			}
			effectToCast = lastMatchingEffect;
			duration = effectToCast.Duration();
			duration.StatModifiers( statModifiers );
			instance.Duration = RPGManager.CalculateStatModifiers( statModifiers, player.GetGame(), emptyObject, dummyEntityID, player.GetEntityID() );
			if( effectToCast.GameplayTagsContains( 'Overheat' ) )
			{
				instance.Duration += UIInventoryItemProgramData.GetAdditionalOverheatDuration( effectToCast );
			}
		}
	}

	private static function GetUploadTime( instance : weak< UIInventoryItemProgramData >, actionRecord : weak< ObjectAction_Record >, player : weak< PlayerPuppet > )
	{
		var statModifiers : array< weak< StatModifier_Record > >;
		var baseActionRecord : weak< ObjectAction_Record >;
		var baseStatModifiers : array< weak< StatModifier_Record > >;
		var dummyEntityID : EntityID;
		actionRecord.ActivationTime( statModifiers );
		baseActionRecord.ActivationTime( baseStatModifiers );
		statModifiers = StatModifiersExcept( statModifiers, baseStatModifiers );
		instance.UploadTime = RPGManager.CalculateStatModifiers( statModifiers, player.GetGame(), player, dummyEntityID, player.GetEntityID() );
	}

	private static function StatModifiersExcept( const statModifiers : ref< array< weak< StatModifier_Record > > >, const except : ref< array< weak< StatModifier_Record > > > ) : array< weak< StatModifier_Record > >
	{
		var result : array< weak< StatModifier_Record > >;
		var i : Int32;
		for( i = 0; i < statModifiers.Size(); i += 1 )
		{
			if( !( except.Contains( statModifiers[ i ] ) ) )
			{
				result.PushBack( statModifiers[ i ] );
			}
		}
		return result;
	}

	private static function GetCooldown( instance : weak< UIInventoryItemProgramData >, actionRecord : weak< ObjectAction_Record >, player : weak< PlayerPuppet > )
	{
		var i, limit : Int32;
		var actionStartEffects : array< weak< ObjectActionEffect_Record > >;
		var statModifiers : array< weak< StatModifier_Record > >;
		var baseStatModifiers : array< weak< StatModifier_Record > >;
		var baseCooldownRecord : weak< StatModifierGroup_Record >;
		var dummyEntityID : EntityID;
		baseCooldownRecord = TweakDBInterface.GetStatModifierGroupRecord( T"BaseStatusEffect.QuickHackCooldownDuration" );
		baseCooldownRecord.StatModifiers( baseStatModifiers );
		actionRecord.StartEffects( actionStartEffects );
		for( i = 0, limit = actionStartEffects.Size(); i < limit; i += 1 )
		{
			if( actionStartEffects[ i ].StatusEffect().StatusEffectType().Type() == gamedataStatusEffectType.PlayerCooldown )
			{
				statModifiers.Clear();
				actionStartEffects[ i ].StatusEffect().Duration().StatModifiers( statModifiers );
				statModifiers = StatModifiersExcept( statModifiers, baseStatModifiers );
				instance.Cooldown = RPGManager.CalculateStatModifiers( statModifiers, player.GetGame(), player, dummyEntityID, player.GetEntityID() );
			}
			if( instance.Cooldown != 0.0 )
			{
				break;
			}
		}
	}

	private static function GetAdditionalOverheatDuration( effectToCast : weak< StatusEffect_Record > ) : Float
	{
		var i, iLimit, j, jLimit : Int32;
		var packages : array< weak< GameplayLogicPackage_Record > >;
		var stats : array< weak< StatModifier_Record > >;
		var stat : weak< ConstantStatModifier_Record >;
		effectToCast.Packages( packages );
		for( i = 0, iLimit = packages.Size(); i < iLimit; i += 1 )
		{
			packages[ i ].Stats( stats );
			for( j = 0, jLimit = stats.Size(); j < jLimit; j += 1 )
			{
				if( stats[ j ].StatType().StatType() == gamedataStatType.OverheatDurationIncrease )
				{
					stat = ( ( ConstantStatModifier_Record )( stats[ j ] ) );
					if( stat )
					{
						return stat.Value();
					}
				}
			}
		}
		return 0.0;
	}

	public static function Make( itemRecord : weak< Item_Record >, player : weak< PlayerPuppet > ) : UIInventoryItemProgramData
	{
		var instance : UIInventoryItemProgramData;
		var actionRecord : weak< ObjectAction_Record >;
		var actionRecords : array< weak< ObjectAction_Record > >;
		var shouldHideCooldown : Bool;
		var i : Int32;
		var isQuickhackOfDeviceOrPuppetType : Bool;
		var tempActionHasBiggerPriority : Bool;
		actionRecord = NULL;
		instance = new UIInventoryItemProgramData;
		itemRecord.ObjectActions( actionRecords );
		for( i = 0; i < actionRecords.Size(); i += 1 )
		{
			isQuickhackOfDeviceOrPuppetType = actionRecords[ i ].ObjectActionType().Type() == gamedataObjectActionType.DeviceQuickHack || actionRecords[ i ].ObjectActionType().Type() == gamedataObjectActionType.PuppetQuickHack;
			if( isQuickhackOfDeviceOrPuppetType )
			{
				if( !( actionRecord ) )
				{
					actionRecord = actionRecords[ i ];
					continue;
				}
				tempActionHasBiggerPriority = actionRecords[ i ].Priority() > actionRecord.Priority();
				if( !( tempActionHasBiggerPriority ) )
				{
					continue;
				}
				actionRecord = actionRecords[ i ];
			}
		}
		shouldHideCooldown = TweakDBInterface.GetBool( itemRecord.GetID() + T".hideCooldownUI", false );
		instance.BaseCost = BaseScriptableAction.GetBaseCostStatic( player, actionRecord );
		instance.MemoryCost = instance.BaseCost;
		GetDurationAndAttackEffects( instance, actionRecord, player );
		GetUploadTime( instance, actionRecord, player );
		if( !( shouldHideCooldown ) )
		{
			GetCooldown( instance, actionRecord, player );
		}
		return instance;
	}

}

class UIInventoryItemStatComparison
{
	var Type : gamedataStatType;
	var Value : Float;
}

class UIInventoryItemComparisonManager
{
	var ComparedStats : array< UIInventoryItemStatComparison >;
	var ComparedItem : weak< UIInventoryItem >;
	private var m_comparisonHash : Uint64;

	public static function Make( localItem : weak< UIInventoryItem >, comparisonItem : weak< UIInventoryItem > ) : UIInventoryItemComparisonManager
	{
		var instance : UIInventoryItemComparisonManager;
		var i, limit : Int32;
		var localStatsManager : weak< UIInventoryItemStatsManager >;
		var localStat, comparableStat : weak< UIInventoryItemStat >;
		var comparedStat : UIInventoryItemStatComparison;
		instance = new UIInventoryItemComparisonManager;
		instance.ComparedItem = comparisonItem;
		localStatsManager = localItem.GetStatsManager();
		for( i = 0, limit = localStatsManager.Size(); i < limit; i += 1 )
		{
			localStat = localStatsManager.Get( i );
			comparableStat = comparisonItem.GetStatsManager().GetByType( localStat.Type );
			comparedStat = new UIInventoryItemStatComparison;
			comparedStat.Type = localStat.Type;
			if( comparableStat )
			{
				comparedStat.Value = localStat.Value - comparableStat.Value;
			}
			else
			{
				comparedStat.Value = localStat.Value;
			}
			instance.ComparedStats.PushBack( comparedStat );
		}
		if( ( ( localItem.IsWeapon() && comparisonItem.IsWeapon() ) || ( localItem.IsCyberwareWeapon() && comparisonItem.IsCyberwareWeapon() ) ) || ( localItem.IsHealingItem() && comparisonItem.IsHealingItem() ) )
		{
			localItem.GetStatsManager().GetWeaponBars().SetComparedBars( comparisonItem.GetStatsManager().GetWeaponBars() );
			comparisonItem.GetStatsManager().GetWeaponBars().SetComparedBars( localItem.GetStatsManager().GetWeaponBars() );
		}
		return instance;
	}

	public function GetByType( type : gamedataStatType ) : weak< UIInventoryItemStatComparison >
	{
		var i, limit : Int32;
		for( i = 0, limit = ComparedStats.Size(); i < limit; i += 1 )
		{
			if( ComparedStats[ i ].Type == type )
			{
				return ComparedStats[ i ];
			}
		}
		return NULL;
	}

	public function GetQuality() : gamedataQuality
	{
		if( ComparedItem == NULL )
		{
			return gamedataQuality.Invalid;
		}
		return ComparedItem.GetQuality();
	}

	public function GetComparisonQualityF() : Float
	{
		if( ComparedItem == NULL )
		{
			return -1.0;
		}
		return ComparedItem.GetComparisonQualityF();
	}

}

class UIInventoryItem
{
	var ID : ItemID;
	var Hash : Uint64;
	var DEBUG_iconErrorInfo : DEBUG_IconErrorInfo;
	private var m_manager : weak< UIInventoryItemsManager >;
	private var m_owner : weak< GameObject >;
	private var m_itemData : weak< gameItemData >;
	private var m_realItemData : weak< gameItemData >;
	private var m_recipeItemData : gameItemData;
	private var m_itemRecord : weak< Item_Record >;
	private var m_realItemRecord : weak< Item_Record >;
	private var m_itemTweakID : TweakDBID;
	private var m_realItemTweakID : TweakDBID;
	private var m_data : UIInventoryItemInternalData;
	private var m_weaponData : UIInventoryWeaponInternalData;
	private var m_programData : UIInventoryItemProgramData;
	private var m_grenadeData : UIInventoryItemGrenadeData;
	private var m_cyberwareUpgradeData : InventoryTooltiData_CyberwareUpgradeData;
	private var m_parentItem : weak< gameItemData >;
	private var m_slotID : TweakDBID;
	private var m_fetchedFlags : Int32;
	private var m_isQuantityDirty : Bool;
	private var m_craftingResult : weak< CraftingResult_Record >;
	private var TEMP_isEquippedPrefetched : Bool;
	var TEMP_isEquipped : Bool;

	public static function Make( owner : weak< GameObject >, itemData : gameItemData, optional manager : weak< UIInventoryItemsManager > ) : UIInventoryItem
	{
		var itemTweakID : TweakDBID;
		itemTweakID = ItemID.GetTDBID( itemData.GetID() );
		return UIInventoryItem.Make( owner, itemData, itemTweakID, TweakDBInterface.GetItemRecord( itemTweakID ), manager );
	}

	public static function Make( owner : weak< GameObject >, itemData : gameItemData, itemTweakID : TweakDBID, itemRecord : weak< Item_Record >, optional manager : weak< UIInventoryItemsManager > ) : UIInventoryItem
	{
		var instance : UIInventoryItem;
		instance = new UIInventoryItem;
		instance.ID = itemData.GetID();
		instance.Hash = ItemID.GetCombinedHash( instance.ID );
		instance.m_owner = owner;
		instance.m_realItemData = itemData;
		instance.m_manager = manager;
		instance.m_realItemRecord = itemRecord;
		instance.m_realItemTweakID = itemTweakID;
		instance.m_craftingResult = TweakDBInterface.GetItemRecipeRecord( instance.m_realItemTweakID ).CraftingResult();
		if( instance.m_craftingResult )
		{
			instance.m_itemRecord = instance.m_craftingResult.Item();
			instance.m_itemTweakID = instance.m_itemRecord.GetID();
			instance.m_recipeItemData = InventoryDataManagerV2.GetDryGameItemData( instance.m_itemRecord, instance.m_manager.GetInventoryManager(), instance.m_manager.GetAttachedPlayer(), RPGManager.GetItemDataQuality( instance.m_realItemData ) );
			instance.m_itemData = instance.m_recipeItemData;
		}
		else
		{
			instance.m_itemRecord = instance.m_realItemRecord;
			instance.m_itemTweakID = instance.m_realItemTweakID;
			instance.m_itemData = instance.m_realItemData;
		}
		instance.m_data = new UIInventoryItemInternalData;
		instance.FetchImmediate();
		return instance;
	}

	public static function FromMinimalItemTooltipData( owner : weak< GameObject >, data : MinimalItemTooltipData, optional manager : weak< UIInventoryItemsManager > ) : UIInventoryItem
	{
		var weaponType : WeaponType;
		var instance : UIInventoryItem;
		var iconPathName : CName;
		instance = new UIInventoryItem;
		instance.ID = data.itemID;
		instance.Hash = ItemID.GetCombinedHash( instance.ID );
		instance.m_owner = owner;
		instance.m_realItemData = data.itemData;
		instance.m_manager = manager;
		instance.m_realItemRecord = TweakDBInterface.GetItemRecord( instance.m_itemTweakID );
		instance.m_realItemTweakID = ItemID.GetTDBID( instance.ID );
		instance.m_craftingResult = TweakDBInterface.GetItemRecipeRecord( instance.m_realItemTweakID ).CraftingResult();
		if( instance.m_craftingResult )
		{
			instance.m_itemRecord = instance.m_craftingResult.Item();
			instance.m_itemTweakID = instance.m_itemRecord.GetID();
			instance.m_itemData = InventoryDataManagerV2.GetDryGameItemData( instance.m_itemRecord, instance.m_manager.GetInventoryManager(), instance.m_manager.GetAttachedPlayer(), RPGManager.GetItemDataQuality( instance.m_realItemData ) );
		}
		else
		{
			instance.m_itemRecord = instance.m_realItemRecord;
			instance.m_itemTweakID = instance.m_realItemTweakID;
		}
		instance.m_data = new UIInventoryItemInternalData;
		instance.m_data.Name = data.itemName;
		instance.m_data.Quality = data.quality;
		instance.m_data.ItemType = data.itemType;
		instance.m_data.Quantity = data.quantity;
		instance.m_data.Description = data.description;
		instance.m_data.IconPath = data.iconPath;
		if( !( IsStringValid( instance.m_data.IconPath ) ) )
		{
			iconPathName = IconsNameResolver.GetIconsNameResolver().TranslateItemToIconName( instance.m_itemTweakID, data.useMaleIcon );
			instance.m_data.IconPath = "UIIcon." + NameToString( iconPathName );
		}
		instance.m_data.IsQuestItem = instance.m_realItemData.HasTag( 'Quest' ) || instance.m_realItemData.HasTag( 'UnequipBlocked' );
		instance.m_data.IsIconicItem = RPGManager.IsItemIconic( instance.m_realItemData );
		instance.m_data.EquipmentArea = data.equipmentArea;
		instance.m_data.FilterCategory = ItemCategoryFliter.GetItemCategoryType( instance.m_realItemData );
		instance.m_fetchedFlags |= ( ( ( ( ( ( ( ( ( ( 1 | 2 ) | 4 ) | 8 ) | 16 ) | 32 ) | 64 ) | 256 ) | 512 ) | 1024 ) | 16384 );
		weaponType = UIInventoryItemsManager.GetItemTypeWeapon( instance.m_data.ItemType );
		if( weaponType != WeaponType.Invalid )
		{
			instance.FetchWeaponImmediate( weaponType );
		}
		if( UIInventoryItemsManager.IsItemTypeCloting( instance.m_data.ItemType ) )
		{
			instance.m_data.UIItemCategory = UIItemCategory.Clothing;
		}
		instance.m_data.ItemTypeOrder = ItemCompareBuilder.GetItemTypeOrder( instance.m_realItemData, instance.m_data.EquipmentArea, instance.m_data.ItemType );
		instance.m_data.Weight = instance.m_realItemData.GetStatValueByType( gamedataStatType.Weight );
		instance.m_data.ItemPlus = instance.m_realItemData.GetStatValueByType( gamedataStatType.IsItemPlus );
		instance.m_data.StatsManager = UIInventoryItemStatsManager.FromMinimalItemTooltipData( data, manager );
		instance.TEMP_isEquipped = data.isEquipped;
		instance.TEMP_isEquippedPrefetched = true;
		return instance;
	}

	public static function FromInventoryItemData( owner : weak< GameObject >, const itemData : ref< InventoryItemData >, optional manager : weak< UIInventoryItemsManager > ) : UIInventoryItem
	{
		var weaponType : WeaponType;
		var instance : UIInventoryItem;
		instance = new UIInventoryItem;
		instance.ID = InventoryItemData.GetID( itemData );
		instance.Hash = ItemID.GetCombinedHash( instance.ID );
		instance.m_owner = owner;
		instance.m_realItemData = InventoryItemData.GetGameItemData( itemData );
		instance.m_manager = manager;
		instance.m_itemTweakID = ItemID.GetTDBID( instance.ID );
		instance.m_itemRecord = TweakDBInterface.GetItemRecord( instance.m_itemTweakID );
		instance.m_data = new UIInventoryItemInternalData;
		instance.m_data.Name = InventoryItemData.GetName( itemData );
		instance.m_data.Quality = UIItemsHelper.QualityNameToEnum( InventoryItemData.GetQuality( itemData ) );
		instance.m_data.ItemType = InventoryItemData.GetItemType( itemData );
		instance.m_data.Quantity = InventoryItemData.GetQuantity( itemData );
		instance.m_data.Description = InventoryItemData.GetDescription( itemData );
		instance.m_data.IconPath = InventoryItemData.GetIconPath( itemData );
		instance.m_data.IsQuestItem = instance.m_realItemData.HasTag( 'Quest' ) || instance.m_realItemData.HasTag( 'UnequipBlocked' );
		instance.m_data.IsIconicItem = RPGManager.IsItemIconic( instance.m_realItemData );
		instance.m_data.EquipmentArea = InventoryItemData.GetEquipmentArea( itemData );
		instance.m_data.FilterCategory = ItemCategoryFliter.GetItemCategoryType( instance.m_realItemData );
		instance.m_fetchedFlags |= ( ( ( ( ( ( ( ( ( ( 1 | 2 ) | 4 ) | 8 ) | 16 ) | 32 ) | 64 ) | 128 ) | 256 ) | 512 ) | 16384 );
		weaponType = UIInventoryItemsManager.GetItemTypeWeapon( instance.m_data.ItemType );
		if( weaponType != WeaponType.Invalid )
		{
			instance.FetchWeaponImmediate( weaponType );
		}
		if( UIInventoryItemsManager.IsItemTypeCloting( instance.m_data.ItemType ) )
		{
			instance.m_data.UIItemCategory = UIItemCategory.Clothing;
		}
		instance.m_data.ItemTypeOrder = ItemCompareBuilder.GetItemTypeOrder( instance.m_realItemData, instance.m_data.EquipmentArea, instance.m_data.ItemType );
		instance.m_data.Weight = instance.m_realItemData.GetStatValueByType( gamedataStatType.Weight );
		instance.m_data.ItemPlus = instance.m_realItemData.GetStatValueByType( gamedataStatType.IsItemPlus );
		return instance;
	}

	private function FetchImmediate()
	{
		var weaponType : WeaponType;
		if( m_itemRecord.TagsContains( 'Shard' ) )
		{
			if( m_manager != NULL )
			{
				m_data.Name = UIItemsHelper.GetShardName( m_itemRecord, m_manager.GetAttachedPlayer().GetGame() );
			}
			if( !( IsStringValid( m_data.Name ) ) )
			{
				m_data.Name = "INVALID";
			}
			m_fetchedFlags = m_fetchedFlags | 1;
		}
		else
		{
			m_data.Name = UIItemsHelper.GetItemName( m_itemRecord, m_realItemData );
		}
		m_data.Quantity = m_realItemData.GetQuantity();
		m_data.ItemType = m_itemRecord.ItemType().Type();
		m_data.Quality = RPGManager.GetItemDataQuality( m_realItemData );
		weaponType = UIInventoryItemsManager.GetItemTypeWeapon( m_data.ItemType );
		if( weaponType != WeaponType.Invalid )
		{
			FetchWeaponImmediate( weaponType );
		}
		else if( UIInventoryItemsManager.IsItemTypeCloting( m_data.ItemType ) )
		{
			m_data.UIItemCategory = UIItemCategory.Clothing;
		}
		else if( UIInventoryItemsManager.IsItemTypeCyberwareWeapon( m_data.ItemType ) )
		{
			m_data.UIItemCategory = UIItemCategory.CyberwareWeapon;
		}
		else if( m_data.ItemType == gamedataItemType.Prt_Program )
		{
			m_data.UIItemCategory = UIItemCategory.Program;
		}
		else if( UIInventoryItemsManager.IsItemTypeCyberware( m_data.ItemType ) )
		{
			if( EquipmentSystem.IsItemCyberdeck( ID ) )
			{
				m_data.UIItemCategory = UIItemCategory.Cyberdeck;
			}
			else
			{
				m_data.UIItemCategory = UIItemCategory.Cyberware;
			}
		}
		GetName();
	}

	private function FetchWeaponImmediate( weaponType : WeaponType )
	{
		var modsManager : weak< UIInventoryItemModsManager >;
		m_data.UIItemCategory = UIItemCategory.Weapon;
		m_weaponData = new UIInventoryWeaponInternalData;
		m_weaponData.WeaponType = weaponType;
		modsManager = GetModsManager();
		if( weaponType == WeaponType.Ranged )
		{
			m_weaponData.HasSilencerInstalled = modsManager.UsedSlotsContains( T"AttachmentSlots.PowerModule" );
			m_weaponData.HasScopeInstalled = modsManager.UsedSlotsContains( T"AttachmentSlots.Scope" );
			if( m_weaponData.HasSilencerInstalled )
			{
				m_weaponData.HasSilencerSlot = true;
			}
			else
			{
				m_weaponData.HasSilencerSlot = modsManager.EmptySlotsContains( T"AttachmentSlots.PowerModule" );
			}
			if( m_weaponData.HasScopeInstalled )
			{
				m_weaponData.HasScopeSlot = true;
			}
			else
			{
				m_weaponData.HasScopeSlot = modsManager.EmptySlotsContains( T"AttachmentSlots.Scope" );
			}
		}
	}

	public function GetOwner() : weak< GameObject >
	{
		return m_owner;
	}

	public function GetID() : ItemID
	{
		return ID;
	}

	public function GetTweakDBID() : TweakDBID
	{
		return m_itemTweakID;
	}

	public function GetRealTweakDBID() : TweakDBID
	{
		return m_realItemTweakID;
	}

	public function GetItemData() : weak< gameItemData >
	{
		return m_itemData;
	}

	public function GetRealItemData() : weak< gameItemData >
	{
		return m_realItemData;
	}

	public function GetItemRecord() : weak< Item_Record >
	{
		return m_itemRecord;
	}

	public function GetRealItemRecord() : weak< Item_Record >
	{
		return m_realItemRecord;
	}

	public function Internal_GetParentItem() : weak< gameItemData >
	{
		return m_parentItem;
	}

	public function Internal_GetSlotID() : TweakDBID
	{
		return m_slotID;
	}

	public function Internal_MarkStatsDirty()
	{
		m_data.StatsManager = NULL;
		m_fetchedFlags &= ~( 1024 );
	}

	public function Internal_MarkModsDirty()
	{
		m_data.ModsManager = NULL;
		m_fetchedFlags &= ~( 2048 );
	}

	public function Internal_FlushRequirements()
	{
		m_data.RequirementsManager = NULL;
		m_fetchedFlags &= ~( 4096 );
	}

	public function Internal_FlushComparedBars()
	{
		var statsManager : weak< UIInventoryItemStatsManager >;
		if( m_fetchedFlags & 1024 )
		{
			statsManager = m_data.StatsManager;
			statsManager.FlushComparedBars();
		}
	}

	public function Internal_FlushCyberwareUpgrade()
	{
		m_cyberwareUpgradeData = NULL;
	}

	public function Internal_FlushCraftingResults()
	{
		if( m_recipeItemData )
		{
			m_recipeItemData = NULL;
			m_data = NULL;
			m_fetchedFlags = 0;
		}
	}

	public function SetQuantity( quantity : Int32 )
	{
		m_data.Quantity = quantity;
		m_isQuantityDirty = false;
	}

	public function MarkQuantityDirty()
	{
		m_isQuantityDirty = true;
	}

	public function GetQuantity( optional update : Bool ) : Int32
	{
		if( update || m_isQuantityDirty )
		{
			SetQuantity( m_realItemData.GetQuantity() );
		}
		return m_data.Quantity;
	}

	public function GetQuality() : gamedataQuality
	{
		return m_data.Quality;
	}

	public function GetComparisonQualityF() : Float
	{
		if( m_fetchedFlags & 32768 )
		{
			return m_data.ComparisonQuality;
		}
		m_data.ComparisonQuality = ( ( Float )( GetQualityInt() ) );
		if( IsIconic() )
		{
			m_data.ComparisonQuality += 0.5;
		}
		m_data.ComparisonQuality += ( GetItemPlus() / 10.0 );
		m_fetchedFlags |= 32768;
		return m_data.ComparisonQuality;
	}

	public function GetQualityText( optional type : RarityItemType ) : String
	{
		var plus : Int32;
		var isProgramIconic : Bool;
		if( m_fetchedFlags & 131072 )
		{
			return m_data.QualityText;
		}
		if( GetItemData().HasTag( 'ChimeraMod' ) )
		{
			m_data.QualityText = GetLocalizedText( UIItemsHelper.QualityToDefaultString( gamedataQuality.Iconic, type ) );
			m_fetchedFlags |= 131072;
			return m_data.QualityText;
		}
		plus = ( ( Int32 )( GetItemPlus() ) );
		m_data.QualityText = GetLocalizedText( UIItemsHelper.QualityToDefaultString( GetQuality(), type ) );
		if( !( IsProgram() ) )
		{
			if( plus >= 2 )
			{
				m_data.QualityText += "++";
			}
			else if( plus >= 1 )
			{
				m_data.QualityText += "+";
			}
		}
		else if( IsRecipe() )
		{
			isProgramIconic = RPGManager.IsItemIconic( m_itemData );
		}
		if( IsIconic() || isProgramIconic )
		{
			m_data.QualityText += ( " / " + GetLocalizedText( UIItemsHelper.QualityToDefaultString( gamedataQuality.Iconic, type ) ) );
		}
		m_fetchedFlags |= 131072;
		return m_data.QualityText;
	}

	public function GetQualityInt() : Int32
	{
		return UIInventoryItemsManager.QualityToInt( GetQuality() );
	}

	public function GetQualityName() : CName
	{
		return UIInventoryItemsManager.QualityToName( GetQuality() );
	}

	public function GetItemType() : gamedataItemType
	{
		return m_data.ItemType;
	}

	public function GetUIItemCategory() : UIItemCategory
	{
		return m_data.UIItemCategory;
	}

	public function IsRecipe() : Bool
	{
		if( m_fetchedFlags & 8192 )
		{
			return m_data.IsRecipeItem;
		}
		m_data.IsRecipeItem = m_realItemData.HasTag( 'Recipe' ) || m_itemData.HasTag( 'Recipe' );
		m_fetchedFlags |= 8192;
		return m_data.IsRecipeItem;
	}

	public function IsIllegal() : Bool
	{
		if( m_itemData.HasTag( 'IllegalItem' ) )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public function IsWeapon() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.Weapon;
	}

	public function IsClothing() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.Clothing;
	}

	public function IsCyberware() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.Cyberware || m_data.UIItemCategory == UIItemCategory.Cyberdeck;
	}

	public function IsCyberwareWeapon() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.CyberwareWeapon;
	}

	public function IsPart() : Bool
	{
		return m_itemRecord.IsPart();
	}

	public function IsCyberdeck() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.Cyberdeck;
	}

	public function IsAnyCyberware() : Bool
	{
		return ( m_data.UIItemCategory == UIItemCategory.Cyberware || m_data.UIItemCategory == UIItemCategory.CyberwareWeapon ) || m_data.UIItemCategory == UIItemCategory.Cyberdeck;
	}

	public function IsProgram() : Bool
	{
		return m_data.UIItemCategory == UIItemCategory.Program;
	}

	public function IsHealingItem() : Bool
	{
		return m_data.ItemType == gamedataItemType.Con_Injector || m_data.ItemType == gamedataItemType.Con_Inhaler;
	}

	public function IsJunk() : Bool
	{
		return m_data.ItemType == gamedataItemType.Gen_Junk || m_data.ItemType == gamedataItemType.Gen_Jewellery;
	}

	public function IsUsingBars() : Bool
	{
		return IsCyberwareWeapon() || IsHealingItem();
	}

	public function GetName() : String
	{
		if( m_fetchedFlags & 1 )
		{
			return m_data.Name;
		}
		if( m_craftingResult )
		{
			m_data.Name = GetLocalizedItemNameByCName( m_craftingResult.Item().DisplayName() );
			m_data.Name = m_data.Name;
		}
		else
		{
			m_data.Name = GetLocalizedItemNameByCName( m_itemRecord.DisplayName() );
		}
		m_fetchedFlags |= 1;
		return m_data.Name;
	}

	public function GetIconPath() : String
	{
		if( m_fetchedFlags & 4 )
		{
			return m_data.IconPath;
		}
		m_data.IconPath = UIInventoryItemsManager.ResolveItemIconName( m_itemTweakID, m_itemRecord, m_manager );
		m_fetchedFlags |= 4;
		return m_data.IconPath;
	}

	public function IsQuestItem() : Bool
	{
		if( m_fetchedFlags & 8 )
		{
			return m_data.IsQuestItem;
		}
		m_data.IsQuestItem = m_realItemData.HasTag( 'Quest' ) || m_realItemData.HasTag( 'UnequipBlocked' );
		m_fetchedFlags |= 8;
		return m_data.IsQuestItem;
	}

	public function IsIconic() : Bool
	{
		if( m_fetchedFlags & 32 )
		{
			return m_data.IsIconicItem;
		}
		m_data.IsIconicItem = RPGManager.IsItemIconic( m_realItemData );
		m_fetchedFlags |= 32;
		return m_data.IsIconicItem;
	}

	public function IsOfEquippableType() : Bool
	{
		return ( ( IsWeapon() || IsClothing() ) || IsCyberware() ) || IsCyberwareWeapon();
	}

	public function GetEquipmentArea() : gamedataEquipmentArea
	{
		if( m_fetchedFlags & 16 )
		{
			return m_data.EquipmentArea;
		}
		if( IsOfEquippableType() )
		{
			m_data.EquipmentArea = m_itemRecord.EquipArea().Type();
		}
		else
		{
			m_data.EquipmentArea = gamedataEquipmentArea.Invalid;
		}
		m_fetchedFlags |= 16;
		return m_data.EquipmentArea;
	}

	public function GetFilterCategory() : ItemFilterCategory
	{
		if( m_fetchedFlags & 64 )
		{
			return m_data.FilterCategory;
		}
		m_data.FilterCategory = ItemCategoryFliter.GetItemCategoryType( m_realItemData );
		m_fetchedFlags |= 64;
		return m_data.FilterCategory;
	}

	public function GetPrimaryStat() : weak< UIInventoryItemStat >
	{
		if( m_fetchedFlags & 128 )
		{
			return m_data.PrimaryStat;
		}
		if( IsWeapon() )
		{
			m_data.PrimaryStat = new UIInventoryItemStat;
			m_data.PrimaryStat.Type = gamedataStatType.EffectiveDPS;
			m_data.PrimaryStat.Value = m_itemData.GetStatValueByType( gamedataStatType.EffectiveDPS );
		}
		else if( IsCyberware() || IsCyberwareWeapon() )
		{
			m_data.PrimaryStat = new UIInventoryItemStat;
			m_data.PrimaryStat.Type = gamedataStatType.Armor;
			m_data.PrimaryStat.Value = m_itemData.GetStatValueByType( gamedataStatType.Armor );
			m_data.PrimaryStat.Value -= m_itemData.GetStatValueByType( gamedataStatType.ItemArmor );
			m_data.PrimaryStat.Value = MaxF( m_data.PrimaryStat.Value, 0.0 );
		}
		m_fetchedFlags |= 128;
		return m_data.PrimaryStat;
	}

	public function GetWeight() : Float
	{
		if( m_fetchedFlags & 512 )
		{
			return m_data.Weight;
		}
		m_data.Weight = m_itemData.GetStatValueByType( gamedataStatType.Weight );
		m_fetchedFlags |= 512;
		return m_data.Weight;
	}

	public function GetItemPlus() : Float
	{
		if( m_fetchedFlags & 16384 )
		{
			return m_data.ItemPlus;
		}
		m_data.ItemPlus = m_realItemData.GetStatValueByType( gamedataStatType.IsItemPlus );
		m_fetchedFlags |= 16384;
		return m_data.ItemPlus;
	}

	public function GetItemTypeOrder() : Int32
	{
		if( m_fetchedFlags & 256 )
		{
			return m_data.ItemTypeOrder;
		}
		m_data.ItemTypeOrder = ItemCompareBuilder.GetItemTypeOrder( m_realItemData, GetEquipmentArea(), GetItemType() );
		m_fetchedFlags |= 256;
		return m_data.ItemTypeOrder;
	}

	public function GetStatsManager() : weak< UIInventoryItemStatsManager >
	{
		var record : UIStatsMap_Record;
		if( m_fetchedFlags & 1024 )
		{
			return m_data.StatsManager;
		}
		record = UIInventoryItemsManager.GetUIStatsMap( m_data.ItemType, m_manager );
		m_data.StatsManager = UIInventoryItemStatsManager.Make( this, record, m_manager );
		m_fetchedFlags |= 1024;
		return m_data.StatsManager;
	}

	public function GetStatsManagerPure() : weak< UIInventoryItemStatsManager >
	{
		return m_data.StatsManager;
	}

	public function GetModsManager() : weak< UIInventoryItemModsManager >
	{
		var transactionSystem : TransactionSystem;
		if( m_fetchedFlags & 2048 )
		{
			return m_data.ModsManager;
		}
		transactionSystem = ( ( m_manager ) ? ( m_manager.GetTransactionSystem() ) : ( GameInstance.GetTransactionSystem( m_owner.GetGame() ) ) );
		m_data.ModsManager = UIInventoryItemModsManager.Make( this, transactionSystem );
		m_fetchedFlags |= 2048;
		return m_data.ModsManager;
	}

	public function GetRequirementsManager( player : weak< GameObject > ) : weak< UIInventoryItemRequirementsManager >
	{
		if( m_fetchedFlags & 4096 )
		{
			return m_data.RequirementsManager;
		}
		m_data.RequirementsManager = UIInventoryItemRequirementsManager.Make( this, player );
		m_fetchedFlags |= 4096;
		return m_data.RequirementsManager;
	}

	public function GetDescription() : String
	{
		if( m_fetchedFlags & 2 )
		{
			return m_data.Description;
		}
		m_data.Description = LocKeyToString( m_itemRecord.LocalizedDescription() );
		m_fetchedFlags |= 2;
		return m_data.Description;
	}

	public function IsSellable() : Bool
	{
		if( m_fetchedFlags & 65536 )
		{
			return m_data.IsSellable;
		}
		if( m_manager )
		{
			m_data.IsSellable = m_manager.IsSellable( m_itemData );
		}
		else
		{
			m_data.IsSellable = GetItemType() != gamedataItemType.Gen_CraftingMaterial;
		}
		m_fetchedFlags |= 65536;
		return m_data.IsSellable;
	}

	public function GetSellPrice() : Float
	{
		return ( ( Float )( RPGManager.CalculateSellPriceItemData( m_owner.GetGame(), m_owner, this.GetRealItemData() ) ) );
	}

	public function GetBuyPrice() : Float
	{
		return ( ( Float )( MarketSystem.GetBuyPrice( m_owner, this.ID ) ) );
	}

	public function IsCrafted() : Bool
	{
		return RPGManager.IsItemCrafted( m_realItemData );
	}

	public function IsBroken() : Bool
	{
		if( m_fetchedFlags & 262144 )
		{
			return m_data.IsBroken;
		}
		m_data.IsBroken = RPGManager.IsItemBroken( m_itemData );
		m_fetchedFlags |= 262144;
		return m_data.IsBroken;
	}

	public function IsEquipped( optional force : Bool ) : Bool
	{
		if( TEMP_isEquippedPrefetched && !( force ) )
		{
			return TEMP_isEquipped;
		}
		if( m_manager )
		{
			return m_manager.IsItemEquipped( this.ID );
		}
		return false;
	}

	public function IsTransmogItem() : Bool
	{
		if( m_manager )
		{
			return m_manager.IsItemTransmog( this.ID );
		}
		return false;
	}

	public constexpr function GetRequiredLevel() : Int32
	{
		return 0;
	}

	public function IsNew() : Bool
	{
		if( m_manager )
		{
			return m_manager.IsItemNew( this.ID );
		}
		return false;
	}

	public function IsPlayerFavourite() : Bool
	{
		if( m_manager )
		{
			return m_manager.IsItemPlayerFavourite( this.ID );
		}
		return false;
	}

	public function HasAnyTag( const tagsToCheck : ref< array< CName > > ) : Bool
	{
		var i, limit : Int32;
		for( i = 0, limit = tagsToCheck.Size(); i < limit; i += 1 )
		{
			if( m_realItemData.HasTag( tagsToCheck[ i ] ) )
			{
				return true;
			}
		}
		return false;
	}

	public function GetWeaponType() : WeaponType
	{
		if( m_weaponData == NULL )
		{
			return WeaponType.Invalid;
		}
		return m_weaponData.WeaponType;
	}

	public function HasSilencerSlot() : Bool
	{
		if( m_weaponData == NULL )
		{
			return false;
		}
		return m_weaponData.HasSilencerSlot;
	}

	public function HasScopeSlot() : Bool
	{
		if( m_weaponData == NULL )
		{
			return false;
		}
		return m_weaponData.HasScopeSlot;
	}

	public function HasSilencerInstalled() : Bool
	{
		if( m_weaponData == NULL )
		{
			return false;
		}
		return m_weaponData.HasSilencerInstalled;
	}

	public function HasScopeInstalled() : Bool
	{
		if( m_weaponData == NULL )
		{
			return false;
		}
		return m_weaponData.HasScopeInstalled;
	}

	private function FetchDamageData()
	{
		var divideByPellets : Bool;
		if( GetWeaponType() == WeaponType.Ranged )
		{
			m_weaponData.DamageMin = m_itemData.GetStatValueByType( gamedataStatType.EffectiveDamagePerHitMin );
			m_weaponData.DamageMax = m_itemData.GetStatValueByType( gamedataStatType.EffectiveDamagePerHitMax );
			m_weaponData.NumberOfPellets = RoundF( m_itemData.GetStatValueByType( gamedataStatType.ProjectilesPerShot ) );
		}
		else
		{
			m_weaponData.DamageMin = m_itemData.GetStatValueByType( gamedataStatType.EffectiveDamagePerHit );
			m_weaponData.DamageMax = m_weaponData.DamageMin;
		}
		divideByPellets = TweakDBInterface.GetBool( m_itemTweakID + T".divideAttacksByPelletsOnUI", false );
		if( divideByPellets && ( m_weaponData.NumberOfPellets > 0 ) )
		{
			m_weaponData.AttackSpeed = m_itemData.GetStatValueByType( gamedataStatType.AttacksPerSecond ) / ( ( Float )( m_weaponData.NumberOfPellets ) );
		}
		else
		{
			m_weaponData.AttackSpeed = m_itemData.GetStatValueByType( gamedataStatType.AttacksPerSecond );
		}
	}

	public function GetDamageMin() : Float
	{
		if( m_weaponData == NULL )
		{
			return 0.0;
		}
		if( !( m_weaponData.m_fetchedFlags & 8 ) )
		{
			FetchDamageData();
		}
		return m_weaponData.DamageMin;
	}

	public function GetDamageMax() : Float
	{
		if( m_weaponData == NULL )
		{
			return 0.0;
		}
		if( !( m_weaponData.m_fetchedFlags & 8 ) )
		{
			FetchDamageData();
		}
		return m_weaponData.DamageMax;
	}

	public function GetAttackSpeed() : Float
	{
		if( m_weaponData == NULL )
		{
			return 0.0;
		}
		if( !( m_weaponData.m_fetchedFlags & 8 ) )
		{
			FetchDamageData();
		}
		return m_weaponData.AttackSpeed;
	}

	public function GetNumberOfPellets() : Int32
	{
		if( m_weaponData == NULL )
		{
			return 0;
		}
		if( !( m_weaponData.m_fetchedFlags & 8 ) )
		{
			FetchDamageData();
		}
		return m_weaponData.NumberOfPellets;
	}

	public function GetWeaponEvolution() : gamedataWeaponEvolution
	{
		var weaponRecord : weak< WeaponItem_Record >;
		if( m_weaponData == NULL )
		{
			return gamedataWeaponEvolution.Invalid;
		}
		if( m_weaponData.m_fetchedFlags & 2 )
		{
			return m_weaponData.Evolution;
		}
		weaponRecord = ( ( weak< weak< WeaponItem_Record > > )( m_itemRecord ) );
		if( weaponRecord )
		{
			m_weaponData.Evolution = weaponRecord.Evolution().Type();
		}
		m_weaponData.m_fetchedFlags |= 2;
		return m_weaponData.Evolution;
	}

	public function GetPerkGroup() : gamedataPerkWeaponGroupType
	{
		if( m_weaponData == NULL )
		{
			return gamedataPerkWeaponGroupType.Invalid;
		}
		if( m_weaponData.m_fetchedFlags & 4 )
		{
			return m_weaponData.PerkGroup;
		}
		m_weaponData.PerkGroup = UIItemsHelper.GetBasicPerkRelevanceGroup( m_data.ItemType );
		m_weaponData.m_fetchedFlags |= 4;
		return m_weaponData.PerkGroup;
	}

	public function GetGameplayDescription() : String
	{
		var weaponRecord : weak< WeaponItem_Record >;
		if( m_weaponData == NULL )
		{
			return "";
		}
		weaponRecord = ( ( weak< weak< WeaponItem_Record > > )( m_itemRecord ) );
		if( weaponRecord )
		{
			m_weaponData.GameplayDescription = LocKeyToString( weaponRecord.GameplayDescription() );
		}
		m_weaponData.m_fetchedFlags |= 16;
		if( m_weaponData.m_fetchedFlags & 16 )
		{
			return m_weaponData.GameplayDescription;
		}
		return m_weaponData.GameplayDescription;
	}

	public function GetAmmo( optional update : Bool ) : Int32
	{
		if( m_weaponData == NULL )
		{
			return -1;
		}
		if( ( m_weaponData.m_fetchedFlags & 1 ) && !( update ) )
		{
			return m_weaponData.Ammo;
		}
		m_weaponData.Ammo = UIInventoryItemsManager.GetAmmo( m_itemRecord, update, m_manager );
		m_weaponData.m_fetchedFlags |= 1;
		return m_weaponData.Ammo;
	}

	public function GetProgramData( player : weak< PlayerPuppet >, force : Bool ) : weak< UIInventoryItemProgramData >
	{
		if( m_programData && !( force ) )
		{
			return m_programData;
		}
		if( GetItemType() == gamedataItemType.Prt_Program )
		{
			m_programData = UIInventoryItemProgramData.Make( m_itemRecord, player );
		}
		return m_programData;
	}

	public function GetGrenadeData( player : weak< PlayerPuppet >, force : Bool ) : weak< UIInventoryItemGrenadeData >
	{
		if( m_grenadeData && !( force ) )
		{
			return m_grenadeData;
		}
		if( GetItemType() == gamedataItemType.Gad_Grenade )
		{
			m_grenadeData = UIInventoryItemGrenadeData.Make( this, player );
		}
		return m_grenadeData;
	}

	public function GetCyberwareUpgradeData( player : weak< GameObject >, optional force : Bool ) : InventoryTooltiData_CyberwareUpgradeData
	{
		if( m_cyberwareUpgradeData && !( force ) )
		{
			return m_cyberwareUpgradeData;
		}
		if( IsCyberware() || IsCyberwareWeapon() )
		{
			m_cyberwareUpgradeData = InventoryTooltiData_CyberwareUpgradeData.Make( this, player );
		}
		return m_cyberwareUpgradeData;
	}

}

enum UIItemCategory
{
	Default = 0,
	Weapon = 1,
	Clothing = 2,
	Grenade = 3,
	Cyberware = 4,
	CyberwareWeapon = 5,
	Cyberdeck = 6,
	Program = 7,
}

enum WeaponType
{
	Ranged = 0,
	Melee = 1,
	Count = 2,
	Invalid = -1,
}

