class CompareBuilder
{
	private var FLOAT_EQUAL_EPSILON : Float;
	default FLOAT_EQUAL_EPSILON = 0.01f;
	private var value : Int32;

	public static function Make() : CompareBuilder
	{
		var builder : CompareBuilder;
		builder = new CompareBuilder;
		return builder;
	}

	public function Reset()
	{
		value = 0;
	}

	public function Get() : Int32
	{
		return value;
	}

	public function GetBool() : Bool
	{
		return value > 0;
	}

	public function StringAsc( const a : ref< String >, const b : ref< String > ) : CompareBuilder
	{
		if( value == 0 )
		{
			value = UnicodeStringCompare( a, b ) * -1;
		}
		return this;
	}

	public function StringDesc( const a : ref< String >, const b : ref< String > ) : CompareBuilder
	{
		if( value == 0 )
		{
			value = UnicodeStringCompare( a, b );
		}
		return this;
	}

	public function UnicodeStringAsc( const a : ref< String >, const b : ref< String > ) : CompareBuilder
	{
		if( value == 0 )
		{
			value = ( ( UnicodeStringLessThan( a, b ) ) ? ( 1 ) : ( -1 ) );
		}
		return this;
	}

	public function UnicodeStringDesc( const a : ref< String >, const b : ref< String > ) : CompareBuilder
	{
		if( value == 0 )
		{
			value = ( ( !( UnicodeStringLessThanEqual( a, b ) ) ) ? ( 1 ) : ( -1 ) );
		}
		return this;
	}

	public function IntAsc( a : Int32, b : Int32 ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a < b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function IntDesc( a : Int32, b : Int32 ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a > b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function FloatAsc( a : Float, b : Float ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( AbsF( a - b ) < FLOAT_EQUAL_EPSILON )
			{
				value = 0;
			}
			else
			{
				value = ( ( a < b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function FloatDesc( a : Float, b : Float ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( AbsF( a - b ) < FLOAT_EQUAL_EPSILON )
			{
				value = 0;
			}
			else
			{
				value = ( ( a > b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function BoolTrue( a : Bool, b : Bool ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function BoolFalse( a : Bool, b : Bool ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a ) ? ( -1 ) : ( 1 ) );
			}
		}
		return this;
	}

	public function GameTimeAsc( a : GameTime, b : GameTime ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a < b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

	public function GameTimeDesc( a : GameTime, b : GameTime ) : CompareBuilder
	{
		if( value == 0 )
		{
			if( a == b )
			{
				value = 0;
			}
			else
			{
				value = ( ( a > b ) ? ( 1 ) : ( -1 ) );
			}
		}
		return this;
	}

}

enum ItemSortMode
{
	Default = 0,
	NewItems = 1,
	NameAsc = 2,
	NameDesc = 3,
	QualityAsc = 4,
	QualityDesc = 5,
	WeightAsc = 6,
	WeightDesc = 7,
	PriceAsc = 8,
	PriceDesc = 9,
	ItemType = 10,
	DpsAsc = 11,
	DpsDesc = 12,
}

class NewItemCompareBuilder
{
	private var m_sortData1 : UIInventoryItem;
	private var m_sortData2 : UIInventoryItem;
	private var m_compareBuilder : CompareBuilder;

	public static function Make( sortData1 : UIInventoryItem, sortData2 : UIInventoryItem ) : NewItemCompareBuilder
	{
		var builder : NewItemCompareBuilder;
		builder = new NewItemCompareBuilder;
		builder.m_compareBuilder = CompareBuilder.Make();
		builder.m_sortData1 = sortData1;
		builder.m_sortData2 = sortData2;
		return builder;
	}

	public function Get() : Int32
	{
		return m_compareBuilder.Get();
	}

	public function GetBool() : Bool
	{
		return m_compareBuilder.GetBool();
	}

	public function NameAsc() : NewItemCompareBuilder
	{
		m_compareBuilder.StringAsc( m_sortData1.GetName(), m_sortData2.GetName() );
		return this;
	}

	public function NameDesc() : NewItemCompareBuilder
	{
		m_compareBuilder.StringDesc( m_sortData1.GetName(), m_sortData2.GetName() );
		return this;
	}

	public function QualityAsc() : NewItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.GetQualityInt(), m_sortData2.GetQualityInt() );
		return this;
	}

	public function QualityDesc() : NewItemCompareBuilder
	{
		m_compareBuilder.IntDesc( m_sortData1.GetQualityInt(), m_sortData2.GetQualityInt() );
		return this;
	}

	public function PriceAsc() : NewItemCompareBuilder
	{
		m_compareBuilder.FloatAsc( m_sortData1.GetSellPrice(), m_sortData2.GetSellPrice() );
		return this;
	}

	public function PriceDesc() : NewItemCompareBuilder
	{
		m_compareBuilder.FloatDesc( m_sortData1.GetSellPrice(), m_sortData2.GetSellPrice() );
		return this;
	}

	public function WeightAsc() : NewItemCompareBuilder
	{
		m_compareBuilder.FloatAsc( m_sortData1.GetWeight(), m_sortData2.GetWeight() );
		return this;
	}

	public function WeightDesc() : NewItemCompareBuilder
	{
		m_compareBuilder.FloatDesc( m_sortData1.GetWeight(), m_sortData2.GetWeight() );
		return this;
	}

	public function DPSAsc() : NewItemCompareBuilder
	{
		var leftValue, rightValue : Float;
		if( m_sortData1.IsWeapon() )
		{
			leftValue = m_sortData1.GetPrimaryStat().Value;
		}
		if( m_sortData2.IsWeapon() )
		{
			rightValue = m_sortData2.GetPrimaryStat().Value;
		}
		m_compareBuilder.FloatAsc( leftValue, rightValue );
		return this;
	}

	public function DPSDesc() : NewItemCompareBuilder
	{
		var leftValue, rightValue : Float;
		if( m_sortData1.IsWeapon() )
		{
			leftValue = m_sortData1.GetPrimaryStat().Value;
		}
		if( m_sortData2.IsWeapon() )
		{
			rightValue = m_sortData2.GetPrimaryStat().Value;
		}
		m_compareBuilder.FloatDesc( leftValue, rightValue );
		return this;
	}

	public function ItemType() : NewItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.GetItemTypeOrder(), m_sortData2.GetItemTypeOrder() );
		return this;
	}

	public function QuestItem() : NewItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.IsQuestItem(), m_sortData2.IsQuestItem() );
		return this;
	}

	public function NewItem() : NewItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.IsNew(), m_sortData2.IsNew() );
		return this;
	}

	public function FavouriteItem() : NewItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.IsPlayerFavourite(), m_sortData2.IsPlayerFavourite() );
		return this;
	}

}

class ItemCompareBuilder
{
	private var m_sortData1 : InventoryItemSortData;
	private var m_sortData2 : InventoryItemSortData;
	private var m_compareBuilder : CompareBuilder;

	public static function Make( const sortData1 : ref< InventoryItemSortData >, const sortData2 : ref< InventoryItemSortData > ) : ItemCompareBuilder
	{
		var builder : ItemCompareBuilder;
		builder = new ItemCompareBuilder;
		builder.m_compareBuilder = CompareBuilder.Make();
		builder.m_sortData1 = sortData1;
		builder.m_sortData2 = sortData2;
		return builder;
	}

	public function Get() : Int32
	{
		return m_compareBuilder.Get();
	}

	public function GetBool() : Bool
	{
		return m_compareBuilder.GetBool();
	}

	public function NameAsc() : ItemCompareBuilder
	{
		m_compareBuilder.StringAsc( m_sortData1.Name, m_sortData2.Name );
		return this;
	}

	public function NameDesc() : ItemCompareBuilder
	{
		m_compareBuilder.StringDesc( m_sortData1.Name, m_sortData2.Name );
		return this;
	}

	public function QualityAsc() : ItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.Quality, m_sortData2.Quality );
		return this;
	}

	public function QualityDesc() : ItemCompareBuilder
	{
		m_compareBuilder.IntDesc( m_sortData1.Quality, m_sortData2.Quality );
		return this;
	}

	public function PriceAsc() : ItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.Price, m_sortData2.Price );
		return this;
	}

	public function PriceDesc() : ItemCompareBuilder
	{
		m_compareBuilder.IntDesc( m_sortData1.Price, m_sortData2.Price );
		return this;
	}

	public function WeightAsc() : ItemCompareBuilder
	{
		m_compareBuilder.FloatAsc( m_sortData1.Weight, m_sortData2.Weight );
		return this;
	}

	public function WeightDesc() : ItemCompareBuilder
	{
		m_compareBuilder.FloatDesc( m_sortData1.Weight, m_sortData2.Weight );
		return this;
	}

	public function DPSAsc() : ItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.DPS, m_sortData2.DPS );
		return this;
	}

	public function DPSDesc() : ItemCompareBuilder
	{
		m_compareBuilder.IntDesc( m_sortData1.DPS, m_sortData2.DPS );
		return this;
	}

	public function ItemType() : ItemCompareBuilder
	{
		m_compareBuilder.IntAsc( m_sortData1.ItemType, m_sortData2.ItemType );
		return this;
	}

	public function QuestItem() : ItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.QuestItem, m_sortData2.QuestItem );
		return this;
	}

	public function NewItem( uiScriptableSystem : UIScriptableSystem ) : ItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.NewItem, m_sortData2.NewItem );
		return this;
	}

	public function DLCAddedItem() : ItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.DLCAddedItem, m_sortData2.DLCAddedItem );
		return this;
	}

	public function ChimeraMod() : ItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.ChimeraMod, m_sortData2.ChimeraMod );
		return this;
	}

	public function FavouriteItem() : ItemCompareBuilder
	{
		m_compareBuilder.BoolTrue( m_sortData1.IsPlayerFavourite, m_sortData2.IsPlayerFavourite );
		return this;
	}

	public static function BuildInventoryItemSortData( const item : ref< InventoryItemData >, uiScriptableSystem : UIScriptableSystem ) : InventoryItemSortData
	{
		var itemID : ItemID;
		var sortData : InventoryItemSortData;
		itemID = InventoryItemData.GetID( item );
		sortData.Name = GetLocalizedText( InventoryItemData.GetName( item ) );
		sortData.Quality = UIItemsHelper.QualityNameToInt( InventoryItemData.GetQuality( item ) );
		sortData.Price = ( ( Int32 )( InventoryItemData.GetPrice( item ) ) );
		sortData.Weight = InventoryItemData.GetGameItemData( item ).GetStatValueByType( gamedataStatType.Weight );
		sortData.DPS = GetDPS( item );
		sortData.ItemType = GetItemTypeOrder( item );
		sortData.QuestItem = InventoryItemData.GetGameItemData( item ).HasTag( 'Quest' );
		sortData.NewItem = uiScriptableSystem.IsInventoryItemNew( itemID );
		sortData.DLCAddedItem = InventoryItemData.GetGameItemData( item ).HasTag( 'DLCAdded' ) && uiScriptableSystem.IsDLCAddedActiveItem( ItemID.GetTDBID( itemID ) );
		sortData.ChimeraMod = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemID ) ).TagsContains( 'ChimeraMod' );
		sortData.IsPlayerFavourite = uiScriptableSystem.IsItemPlayerFavourite( itemID );
		return sortData;
	}

	private static function GetDPS( const item : ref< InventoryItemData > ) : Int32
	{
		var i : Int32;
		var size : Int32;
		var stat : StatViewData;
		size = InventoryItemData.GetPrimaryStatsSize( item );
		for( i = 0; i < size; i += 1 )
		{
			stat = InventoryItemData.GetPrimaryStat( item, i );
			if( stat.type == gamedataStatType.EffectiveDPS )
			{
				return stat.value;
			}
		}
		return -1;
	}

	private static function GetTypeSortValue( const item : ref< InventoryItemData > ) : Int32
	{
		if( InventoryItemData.GetGameItemData( item ).HasTag( 'Quest' ) )
		{
			return 0;
		}
		switch( InventoryItemData.GetEquipmentArea( item ) )
		{
			case gamedataEquipmentArea.Weapon:
				return 1;
			case gamedataEquipmentArea.Outfit:
				return 2;
			case gamedataEquipmentArea.Head:
				return 3;
			case gamedataEquipmentArea.Face:
				return 4;
			case gamedataEquipmentArea.OuterChest:
				return 5;
			case gamedataEquipmentArea.InnerChest:
				return 6;
			case gamedataEquipmentArea.Legs:
				return 7;
			case gamedataEquipmentArea.Feet:
				return 8;
			case gamedataEquipmentArea.Gadget:
				return 9;
			case gamedataEquipmentArea.QuickSlot:
				return 9;
			case gamedataEquipmentArea.Consumable:
				return 9;
		}
		return 99;
	}

	private static function GetEquipmentAreaIndex( equipmentArea : gamedataEquipmentArea ) : Int32
	{
		switch( equipmentArea )
		{
			case gamedataEquipmentArea.Weapon:
				return 1;
			case gamedataEquipmentArea.Outfit:
				return 2;
			case gamedataEquipmentArea.Head:
				return 3;
			case gamedataEquipmentArea.Face:
				return 4;
			case gamedataEquipmentArea.OuterChest:
				return 5;
			case gamedataEquipmentArea.InnerChest:
				return 6;
			case gamedataEquipmentArea.Legs:
				return 7;
			case gamedataEquipmentArea.Feet:
				return 8;
			case gamedataEquipmentArea.Gadget:
				return 9;
			case gamedataEquipmentArea.QuickSlot:
				return 10;
			case gamedataEquipmentArea.Consumable:
				return 11;
		}
		return 12;
	}

	private static function GetItemTypeIndex( itemType : gamedataItemType ) : Int32
	{
		switch( itemType )
		{
			case gamedataItemType.Clo_Face:
				return 1;
			case gamedataItemType.Clo_Head:
				return 2;
			case gamedataItemType.Clo_OuterChest:
				return 3;
			case gamedataItemType.Clo_InnerChest:
				return 4;
			case gamedataItemType.Clo_Legs:
				return 5;
			case gamedataItemType.Clo_Feet:
				return 6;
			case gamedataItemType.Clo_Outfit:
				return 7;
			case gamedataItemType.Con_Edible:
				return 1;
			case gamedataItemType.Con_Inhaler:
				return 2;
			case gamedataItemType.Con_Injector:
				return 3;
			case gamedataItemType.Con_LongLasting:
				return 4;
			case gamedataItemType.Cyb_Ability:
				return 5;
			case gamedataItemType.Cyb_Launcher:
				return 6;
			case gamedataItemType.Cyb_MantisBlades:
				return 7;
			case gamedataItemType.Cyb_NanoWires:
				return 8;
			case gamedataItemType.Cyb_StrongArms:
				return 9;
			case gamedataItemType.Fla_Launcher:
				return 1;
			case gamedataItemType.Fla_Rifle:
				return 2;
			case gamedataItemType.Fla_Shock:
				return 3;
			case gamedataItemType.Fla_Support:
				return 4;
			case gamedataItemType.Gad_Grenade:
				return 1;
			case gamedataItemType.Gen_CraftingMaterial:
				return 2;
			case gamedataItemType.Gen_DataBank:
				return 3;
			case gamedataItemType.Gen_Junk:
				return 4;
			case gamedataItemType.Gen_Jewellery:
				return 5;
			case gamedataItemType.Gen_Keycard:
				return 6;
			case gamedataItemType.Gen_Misc:
				return 7;
			case gamedataItemType.Gen_Readable:
				return 8;
			case gamedataItemType.GrenadeDelivery:
				return 9;
			case gamedataItemType.Grenade_Core:
				return 10;
			case gamedataItemType.Gen_Tarot:
				return 11;
			case gamedataItemType.Prt_Capacitor:
				return 1;
			case gamedataItemType.Prt_FabricEnhancer:
				return 2;
			case gamedataItemType.Prt_Fragment:
				return 3;
			case gamedataItemType.Prt_Magazine:
				return 4;
			case gamedataItemType.Prt_Mod:
				return 5;
			case gamedataItemType.Prt_RangedMod:
				return 6;
			case gamedataItemType.Prt_PowerMod:
				return 7;
			case gamedataItemType.Prt_TechMod:
				return 8;
			case gamedataItemType.Prt_SmartMod:
				return 9;
			case gamedataItemType.Prt_AR_SMG_LMGMod:
				return 10;
			case gamedataItemType.Prt_HandgunMod:
				return 11;
			case gamedataItemType.Prt_Precision_Sniper_RifleMod:
				return 12;
			case gamedataItemType.Prt_ShotgunMod:
				return 13;
			case gamedataItemType.Prt_MeleeMod:
				return 14;
			case gamedataItemType.Prt_BladeMod:
				return 15;
			case gamedataItemType.Prt_BluntMod:
				return 16;
			case gamedataItemType.Prt_ThrowableMod:
				return 17;
			case gamedataItemType.Prt_Muzzle:
				return 18;
			case gamedataItemType.Prt_Program:
				return 19;
			case gamedataItemType.Prt_Receiver:
				return 20;
			case gamedataItemType.Prt_Scope:
				return 21;
			case gamedataItemType.Prt_ShortScope:
				return 22;
			case gamedataItemType.Prt_LongScope:
				return 23;
			case gamedataItemType.Prt_TechSniperScope:
				return 24;
			case gamedataItemType.Prt_PowerSniperScope:
				return 25;
			case gamedataItemType.Prt_ScopeRail:
				return 26;
			case gamedataItemType.Prt_Stock:
				return 27;
			case gamedataItemType.Prt_TargetingSystem:
				return 28;
			case gamedataItemType.Wea_SniperRifle:
				return 1;
			case gamedataItemType.Wea_PrecisionRifle:
				return 2;
			case gamedataItemType.Wea_AssaultRifle:
				return 3;
			case gamedataItemType.Wea_Rifle:
				return 4;
			case gamedataItemType.Wea_HeavyMachineGun:
				return 5;
			case gamedataItemType.Wea_LightMachineGun:
				return 6;
			case gamedataItemType.Wea_SubmachineGun:
				return 7;
			case gamedataItemType.Wea_Shotgun:
				return 8;
			case gamedataItemType.Wea_ShotgunDual:
				return 9;
			case gamedataItemType.Wea_Handgun:
				return 10;
			case gamedataItemType.Wea_Revolver:
				return 11;
			case gamedataItemType.Wea_TwoHandedClub:
				return 12;
			case gamedataItemType.Wea_Hammer:
				return 13;
			case gamedataItemType.Wea_ShortBlade:
				return 14;
			case gamedataItemType.Wea_LongBlade:
				return 15;
			case gamedataItemType.Wea_Melee:
				return 16;
			case gamedataItemType.Wea_OneHandedClub:
				return 17;
			case gamedataItemType.Wea_Katana:
				return 18;
			case gamedataItemType.Wea_Knife:
				return 19;
			case gamedataItemType.Wea_Axe:
				return 20;
			case gamedataItemType.Wea_Chainsword:
				return 21;
			case gamedataItemType.Wea_Machete:
				return 22;
			case gamedataItemType.Wea_Sword:
				return 23;
		}
		return 0;
	}

	private static function HasItemTypeInnerIndex( itemType : gamedataItemType ) : Bool
	{
		return itemType == gamedataItemType.Prt_Mod;
	}

	private static function GetItemTypeInnerIndex( itemType : gamedataItemType, itemData : weak< gameItemData > ) : Int32
	{
		var tags : array< CName >;
		if( RPGManager.IsWeaponMod( itemType ) )
		{
			tags = TweakDBInterface.GetItemRecord( ItemID.GetTDBID( itemData.GetID() ) ).Tags();
			if( tags.Contains( 'Power' ) )
			{
				return 1;
			}
			else if( tags.Contains( 'Tech' ) )
			{
				return 2;
			}
			else if( tags.Contains( 'Smart' ) )
			{
				return 3;
			}
			else if( tags.Contains( 'MeleeMod' ) )
			{
				return 4;
			}
		}
		return 0;
	}

	public static function GetItemTypeOrder( const item : ref< InventoryItemData > ) : Int32
	{
		var itemType : gamedataItemType;
		var equipmentArea : gamedataEquipmentArea;
		var value : Int32;
		equipmentArea = InventoryItemData.GetEquipmentArea( item );
		itemType = InventoryItemData.GetItemType( item );
		if( itemType == gamedataItemType.Invalid )
		{
			return 0;
		}
		value += ( GetEquipmentAreaIndex( equipmentArea ) * 10000 );
		value += ( GetItemTypeIndex( itemType ) * 100 );
		if( HasItemTypeInnerIndex( itemType ) )
		{
			value += GetItemTypeInnerIndex( itemType, InventoryItemData.GetGameItemData( item ) );
		}
		return value;
	}

	public static function GetItemTypeOrder( itemData : weak< gameItemData >, equipmentArea : gamedataEquipmentArea, itemType : gamedataItemType ) : Int32
	{
		var value : Int32;
		if( itemType == gamedataItemType.Invalid )
		{
			return 0;
		}
		value += ( GetEquipmentAreaIndex( equipmentArea ) * 10000 );
		value += ( GetItemTypeIndex( itemType ) * 100 );
		if( HasItemTypeInnerIndex( itemType ) )
		{
			value += GetItemTypeInnerIndex( itemType, itemData );
		}
		return value;
	}

}

