//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Holds constants for the econ item system
//
//=============================================================================

#include "cbase.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *g_szQualityStrings[] =
{
	"Normal",
	"rarity1",		// Genuine
	"rarity2",		// Customized
	"vintage",		// Vintage has to stay at 3 for backwards compatibility
	"rarity3",		// Well-Designed
	"rarity4",		// Unusual
	"Unique",
	"community",
	"developer",
	"selfmade",
	"customized",
	"strange",
	"completed",
	"haunted",
	"collectors",
	"paintkitWeapon",

	"default",		// AE_RARITY_DEFAULT,
	"common",		// AE_RARITY_COMMON,
	"uncommon",		// AE_RARITY_UNCOMMON,
	"rare",			// AE_RARITY_RARE,
	"mythical",		// AE_RARITY_MYTHICAL,
	"legendary",	// AE_RARITY_LEGENDARY,
	"ancient",		// AE_RARITY_ANCIENT,
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityStrings ) == AE_MAX_TYPES );

const char *EconQuality_GetQualityString( EEconItemQuality eQuality )
{
	// This is a runtime check and not an assert because we could theoretically bounce the GC with new
	// qualities while the client is running.
	if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
		return g_szQualityStrings[ eQuality ];

	return NULL;
}

EEconItemQuality EconQuality_GetQualityFromString( const char* pszQuality )
{
	// Convert to lowercase
	CUtlString strLoweredInput( pszQuality );
	strLoweredInput.ToLower();

	// Guaranteed with the compile time assert above that AE_MAX_TYPES is
	// the size of the string qualities
	for( int i = 0; i < AE_MAX_TYPES; ++i )
	{
		// Convert to lowercase
		CUtlString strLoweredQuality( g_szQualityStrings[i] );
		strLoweredQuality.ToLower();

		if( !Q_stricmp( strLoweredInput.Get(), strLoweredQuality.Get() ) )
			return EEconItemQuality(i);
	}

	return AE_UNDEFINED;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *g_szQualityColorStrings[] =
{
	"QualityColorNormal",
	"QualityColorrarity1",
	"QualityColorrarity2",
	"QualityColorVintage",
	"QualityColorrarity3",
	"QualityColorrarity4",				// AE_UNUSUAL
	"QualityColorUnique",
	"QualityColorCommunity",
	"QualityColorDeveloper",
	"QualityColorSelfMade",
	"QualityColorSelfMadeCustomized",
	"QualityColorStrange",
	"QualityColorCompleted",
	"QualityColorHaunted",				// AE_HAUNTED
	"QualityColorCollectors",			// AE_COLLECTORS
	"QualityColorPaintkitWeapon",		// AE_PAINTKITWEAPON

	"ItemRarityDefault"		, // AE_RARITY_DEFAULT,
	"ItemRarityCommon"		, // AE_RARITY_COMMON,
	"ItemRarityUncommon"	, // AE_RARITY_UNCOMMON,
	"ItemRarityRare"		, // AE_RARITY_RARE,
	"ItemRarityMythical"	, // AE_RARITY_MYTHICAL,
	"ItemRarityLegendary"	, // AE_RARITY_LEGENDARY,
	"ItemRarityAncient"		, // AE_RARITY_ANCIENT,
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityColorStrings ) == AE_MAX_TYPES );

const char *EconQuality_GetColorString( EEconItemQuality eQuality )
{
	if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
		return g_szQualityColorStrings[ eQuality ];

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *g_szQualityLocalizationStrings[] =
{
	"#Normal",
	"#rarity1",		// Genuine
	"#rarity2",
	"#vintage",
	"#rarity3",		// Artisan
	"#rarity4",		// Unusual
	"#unique",
	"#community",
	"#developer",
	"#selfmade",
	"#customized",
	"#strange",
	"#completed",
	"#haunted",
	"#collectors",
	"#paintkitWeapon",

	"#Rarity_Default",
	"#Rarity_Common",
	"#Rarity_Uncommon",
	"#Rarity_Rare",
	"#Rarity_Mythical",
	"#Rarity_Legendary",
	"#Rarity_Ancient"
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityLocalizationStrings ) == AE_MAX_TYPES );

const char *EconQuality_GetLocalizationString( EEconItemQuality eQuality )
{
	if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
		return g_szQualityLocalizationStrings[ eQuality ];

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Sort order for rarities
// Small Numbers sort to front
//-----------------------------------------------------------------------------
int g_nRarityScores[] =
{
	15,		// AE_NORMAL,
	10,		// AE_RARITY1,	// Geniune
	102,	// AE_RARITY2,	// Customized (unused)
	11,		// AE_VINTAGE,
	101,	// AE_RARITY3,	// Artisan (unused)
	0,		// AE_UNUSUAL,
	14,		// AE_UNIQUE,
	-1,		// AE_COMMUNITY,
	-3,		// AE_DEVELOPER,
	-2,		// AE_SELFMADE,
	100,	// AE_CUSTOMIZED,		// Unused
	9,		// AE_STRANGE,
	103,	// AE_COMPLETED,		// Unused
	13,		// AE_HAUNTED
	12,		// AE_COLLECTORS
	8,		// AE_PAINTKITWEAPON
	7,		// AE_RARITY_DEFAULT,
	6,		// AE_RARITY_COMMON,
	5,		// AE_RARITY_UNCOMMON,
	4,		// AE_RARITY_RARE,
	3,		// AE_RARITY_MYTHICAL,
	2,		// AE_RARITY_LEGENDARY,
	1,		// AE_RARITY_ANCIENT,
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_nRarityScores ) == AE_MAX_TYPES );

//-----------------------------------------------------------------------------
int EconQuality_GetRarityScore( EEconItemQuality eQuality )
{
	if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
		return g_nRarityScores[ eQuality ];

	return 0;
}

//-----------------------------------------------------------------------------
const char *g_pchWearAmountStrings[] =
{
	"#TFUI_InvTooltip_None",
	"#TFUI_InvTooltip_FactoryNew",
	"#TFUI_InvTooltip_MinimalWear",
	"#TFUI_InvTooltip_FieldTested",
	"#TFUI_InvTooltip_WellWorn",
	"#TFUI_InvTooltip_BattleScared"
};

//-----------------------------------------------------------------------------
int EconWear_ToIntCategory( float flWear )
{
	if ( flWear <= 0.2f )
	{
		return 1;
	}
	else if ( flWear <= 0.4f )
	{
		return 2;
	}
	else if ( flWear <= 0.6f )
	{
		return 3;
	}
	else if ( flWear <= 0.8f )
	{
		return 4;
	}
	else if ( flWear <= 1.0f )
	{
		return 5;
	}

	return 3; // default wear
}

// -------------------------------------------------------------------------
// Shim to return a value for buckets.  For strange we bucket all of them in to 1 non-instance data group
int EconStrange_ToStrangeBucket( float value )
{
	return 0;
}
float EconStrange_FromStrangeBucket( int value )
{
	return 0;
}

//-----------------------------------------------------------------------------
const char *GetWearLocalizationString( float flWear )
{
	int nIndex = EconWear_ToIntCategory( flWear );
	return g_pchWearAmountStrings[ nIndex ];
}
//-----------------------------------------------------------------------------
bool EconWear_IsValidValue( int nWear )
{
	return nWear > 0 && nWear <= 5;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CSchemaColorDefHandle g_AttribColorDefs[] =
{
	CSchemaColorDefHandle( "desc_level" ),				// ATTRIB_COL_LEVEL
	CSchemaColorDefHandle( "desc_attrib_neutral" ),		// ATTRIB_COL_NEUTRAL
	CSchemaColorDefHandle( "desc_attrib_positive" ),	// ATTRIB_COL_POSITIVE
	CSchemaColorDefHandle( "desc_attrib_negative" ),	// ATTRIB_COL_NEGATIVE
	CSchemaColorDefHandle( "desc_itemset_name" ),		// ATTRIB_COL_ITEMSET_NAME
	CSchemaColorDefHandle( "desc_itemset_equipped" ),	// ATTRIB_COL_ITEMSET_EQUIPPED
	CSchemaColorDefHandle( "desc_itemset_missing" ),	// ATTRIB_COL_ITEMSET_MISSING
	CSchemaColorDefHandle( "desc_bundle" ),				// ATTRIB_COL_BUNDLE_ITEM
	CSchemaColorDefHandle( "desc_limited_use" ),		// ATTRIB_COL_LIMITED_USE
	CSchemaColorDefHandle( "desc_flags" ),				// ATTRIB_COL_component_flags
	CSchemaColorDefHandle( "desc_limited_quantity" ),	// ATTRIB_COL_LIMITED_QUANTITY

	CSchemaColorDefHandle( "desc_default" ),			// ATTRIB_COL_RARITY_DEFAULT
	CSchemaColorDefHandle( "desc_common" ),				// ATTRIB_COL_RARITY_COMMON
	CSchemaColorDefHandle( "desc_uncommon" ),			// ATTRIB_COL_RARITY_UNCOMMON
	CSchemaColorDefHandle( "desc_rare" ),				// ATTRIB_COL_RARITY_RARE
	CSchemaColorDefHandle( "desc_mythical" ),			// ATTRIB_COL_RARITY_MYTHICAL
	CSchemaColorDefHandle( "desc_legendary" ),			// ATTRIB_COL_RARITY_LEGENDARY
	CSchemaColorDefHandle( "desc_ancient" ),			// ATTRIB_COL_RARITY_ANCIENT
	CSchemaColorDefHandle( "desc_immortal" ),			// ATTRIB_COL_RARITY_IMMORTAL
	CSchemaColorDefHandle( "desc_arcana" ),				// ATTRIB_COL_RARITY_ARCANA

	CSchemaColorDefHandle( "desc_strange" ),			// ATTRIB_COL_STRANGE
	CSchemaColorDefHandle( "desc_unusual" ),			// ATTRIB_COL_UNUSUAL
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_AttribColorDefs ) == NUM_ATTRIB_COLORS );

attrib_colors_t GetAttribColorIndexForName( const char* pszName )
{
	for ( int i = 0; i < NUM_ATTRIB_COLORS; ++i )
	{
		if ( !Q_strcmp( g_AttribColorDefs[i].GetName(), pszName ) )
			return (attrib_colors_t)i;
	}

	return (attrib_colors_t)0;
}

const char *GetColorNameForAttribColor( attrib_colors_t unAttribColor )
{
	Assert( unAttribColor >= 0 );
	Assert( unAttribColor < NUM_ATTRIB_COLORS );

	return g_AttribColorDefs[unAttribColor]
		 ? g_AttribColorDefs[unAttribColor]->GetColorName()
		 : "ItemAttribNeutral";
}

const char *GetHexColorForAttribColor( attrib_colors_t unAttribColor )
{
	Assert( unAttribColor >= 0 );
	Assert( unAttribColor < NUM_ATTRIB_COLORS );

	return g_AttribColorDefs[unAttribColor]
	     ? g_AttribColorDefs[unAttribColor]->GetHexColor()
		 : "#ebe2ca";
}

entityquality_t GetItemQualityFromString( const char *sQuality )
{
	for ( int i = 0; i < AE_MAX_TYPES; i++ )
	{
		if ( !Q_strnicmp( sQuality, g_szQualityStrings[i], 16 ) )
			return (entityquality_t)i;
	}

	return AE_NORMAL;
}

const char *g_szRecipeCategoryStrings[] =
{
	"crafting",		// RECIPE_CATEGORY_CRAFTINGITEMS = 0,
	"commonitem",	// RECIPE_CATEGORY_COMMONITEMS,
	"rareitem",		// RECIPE_CATEGORY_RAREITEMS,
	"special",		// RECIPE_CATEGORY_SPECIAL,
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_szRecipeCategoryStrings ) == NUM_RECIPE_CATEGORIES );

//-----------------------------------------------------------------------------
// Item acquisition.
//-----------------------------------------------------------------------------
// Strings shown to the local player in the pickup dialog
const char *g_pszItemPickupMethodStrings[] = 
{
	"#NewItemMethod_Dropped",			// UNACK_ITEM_DROPPED = 1,
	"#NewItemMethod_Crafted",			// UNACK_ITEM_CRAFTED,
	"#NewItemMethod_Traded",			// UNACK_ITEM_TRADED,
	"#NewItemMethod_Purchased",			// UNACK_ITEM_PURCHASED,
	"#NewItemMethod_FoundInCrate",		// UNACK_ITEM_FOUND_IN_CRATE,
	"#NewItemMethod_Gifted",			// UNACK_ITEM_GIFTED,
	"#NewItemMethod_Support",			// UNACK_ITEM_SUPPORT,
	"#NewItemMethod_Promotion",			// UNACK_ITEM_PROMOTION,
	"#NewItemMethod_Earned",			// UNACK_ITEM_EARNED,
	"#NewItemMethod_Refunded",			// UNACK_ITEM_REFUNDED,
	"#NewItemMethod_GiftWrapped",		// UNACK_ITEM_GIFT_WRAPPED,
	"#NewItemMethod_Foreign",			// UNACK_ITEM_FOREIGN,
	"#NewItemMethod_CollectionReward",	// UNACK_ITEM_COLLECTION_REWARD
	"#NewItemMethod_PreviewItem",		// UNACK_ITEM_PREVIEW_ITEM
	"#NewItemMethod_PreviewItemPurchased", // UNACK_ITEM_PREVIEW_ITEM_PURCHASED
	"#NewItemMethod_PeriodicScoreReward",// UNACK_ITEM_PERIODIC_SCORE_REWARD
	"#NewItemMethod_MvMBadgeCompletionReward",// UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
	"#NewItemMethod_MvMSquadSurplusReward",// UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
	"#NewItemMethod_HolidayGift",		// UNACK_ITEM_FOUND_HOLIDAY_GIFT
	"#NewItemMethod_CommunityMarketPurchase",	// UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
	"#NewItemMethod_RecipeOutput",	// UNACK_ITEM_RECIPE_OUTPUT
	NULL,							// UNACK_ITEM_HIDDEN_QUEST_ITEM
	"#NewItemMethod_QuestOutput",	// UNACK_ITEM_QUEST_OUTPUT
	"#NewItemMethod_QuestLoaner",	// UNACK_ITEM_QUEST_LOANER
	"#NewItemMethod_TradeUp",		// UNACK_ITEM_TRADE_UP
	"#NewItemMethod_QuestMerasmissionOutput", //UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
	"#NewItemMethod_ViralCompetitiveBetaPassSpread", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
#ifdef ENABLE_STORE_RENTAL_BACKEND
	"#NewItemMethod_RentalPurchase",	// UNACK_ITEM_RENTAL_PURCHASE
#endif
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemPickupMethodStrings ) == (UNACK_NUM_METHODS - 1) );		// -1 because UNACK_ITEM_DROPPED is index 1, not 0

const char *g_pszItemPickupMethodStringsUnloc[] = 
{
	"dropped",			// UNACK_ITEM_DROPPED = 1,
	"crafted",			// UNACK_ITEM_CRAFTED,
	"traded",			// UNACK_ITEM_TRADED,
	"purchased",		// UNACK_ITEM_PURCHASED,
	"found_in_crate",	// UNACK_ITEM_FOUND_IN_CRATE,
	"gifted",			// UNACK_ITEM_GIFTED,
	"support",			// UNACK_ITEM_SUPPORT,
	"promotion",		// UNACK_ITEM_PROMOTION,
	"earned",			// UNACK_ITEM_EARNED,
	"refunded",			// UNACK_ITEM_REFUNDED,
	"gift_wrapped",		// UNACK_ITEM_GIFT_WRAPPED
	"foreign",			// UNACK_ITEM_FOREIGN
	"collection_reward",// UNACK_ITEM_COLLECTION_REWARD
	"preview_item",		// UNACK_ITEM_PREVIEW_ITEM
	"preview_item_purchased", // UNACK_ITEM_PREVIEW_ITEM_PURCHASED
	"periodic_score_reward", // UNACK_ITEM_PERIODIC_SCORE_REWARD
	"mvm_badge_completion_reward", // UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
	"mvm_squad_surplus_reward", // UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
	"holiday_gift",		// UNACK_ITEM_FOUND_HOLIDAY_GIFT
	"market_purchase",	// UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
	"recipe_output",	// UNACK_ITEM_RECIPE_OUTPUT
	"hidden_quest",		// UNACK_ITEM_HIDDEN_QUEST_ITEM
	"quest_output",		// UNACK_ITEM_QUEST_OUTPUT
	"trade_up",			// UNACK_ITEM_TRADE_UP
	"quest_output",		// UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
	"viral_competitive_beta_pass", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
#ifdef ENABLE_STORE_RENTAL_BACKEND
	"rental_purchase",	// UNACK_ITEM_RENTAL_PURCHASE
#endif
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemPickupMethodStringsUnloc ) == (UNACK_NUM_METHODS - 1) );

// Strings shown to other players in the chat dialog
const char *g_pszItemFoundMethodStrings[] = 
{
	"#Item_Found",				// UNACK_ITEM_DROPPED = 1,
	"#Item_Crafted",			// UNACK_ITEM_CRAFTED,
	"#Item_Traded",				// UNACK_ITEM_TRADED,
	NULL,						// UNACK_ITEM_PURCHASED,
	"#Item_FoundInCrate",		// UNACK_ITEM_FOUND_IN_CRATE,
	"#Item_Gifted",				// UNACK_ITEM_GIFTED,
	NULL,						// UNACK_ITEM_SUPPORT,
	NULL,						// UNACK_ITEM_PROMOTION
	"#Item_Earned",				// UNACK_ITEM_EARNED
	"#Item_Refunded",			// UNACK_ITEM_REFUNDED
	"#Item_GiftWrapped",		// UNACK_ITEM_GIFT_WRAPPED
	"#Item_Foreign",			// UNACK_ITEM_FOREIGN
	"#Item_CollectionReward",	// UNACK_ITEM_COLLECTION_REWARD
	"#Item_PreviewItem",		// UNACK_ITEM_PREVIEW_ITEM
	"#Item_PreviewItemPurchased",// UNACK_ITEM_PREVIEW_ITEM_PURCHASED
	"#Item_PeriodicScoreReward",// UNACK_ITEM_PERIODIC_SCORE_REWARD
	"#Item_MvMBadgeCompletionReward",// UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
	"#Item_MvMSquadSurplusReward",// UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
	"#Item_HolidayGift",		// UNACK_ITEM_FOUND_HOLIDAY_GIFT
	NULL,						// UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
	"#Item_RecipeOutput",		// UNACK_ITEM_RECIPE_OUTPUT
	NULL,						// UNACK_ITEM_HIDDEN_QUEST_ITEM
	"#Item_QuestOutput",		// UNACK_ITEM_QUEST_OUTPUT
	NULL,						// UNACK_ITEM_QUEST_LOANER
	"#Item_TradeUp",			// UNACK_ITEM_TRADE_UP
	"#Item_QuestMerasmissionOutput", // UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
	"#Item_ViralCompetitiveBetaPassSpread", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
#ifdef ENABLE_STORE_RENTAL_BACKEND
	NULL,						// UNACK_ITEM_RENTAL_PURCHASE
#endif
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemFoundMethodStrings ) == (UNACK_NUM_METHODS - 1) );


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
struct strange_attr_set_t
{
	strange_attr_set_t( const char *pScoreAttrName, const char *pTypeAttrName, const char *pRestrictionAttrName, const char *pRestrictionValueAttrName, bool bIsUserCustomizable )
		: m_attrScore( pScoreAttrName )
		, m_attrType( pTypeAttrName )
		, m_attrRestriction( pRestrictionAttrName )
		, m_attrRestrictionValue( pRestrictionValueAttrName )
		, m_bIsUserCustomizable( bIsUserCustomizable )
	{
		//
	}

	CSchemaAttributeDefHandle m_attrScore;
	CSchemaAttributeDefHandle m_attrType;
	CSchemaAttributeDefHandle m_attrRestriction;
	CSchemaAttributeDefHandle m_attrRestrictionValue;
	bool m_bIsUserCustomizable;
};

strange_attr_set_t g_KillEaterAttr[] =
{
	strange_attr_set_t( "kill eater",			"kill eater score type",		"strange restriction type 1",		"strange restriction value 1",		false ),
	strange_attr_set_t( "kill eater 2",			"kill eater score type 2",		"strange restriction type 2",		"strange restriction value 2",		false ),
	strange_attr_set_t( "kill eater 3",			"kill eater score type 3",		"strange restriction type 3",		"strange restriction value 3",		false ),

	// assumption: all of the user-customizable attributes will follow all of the schema-specified attributes
	strange_attr_set_t( "kill eater user 1",	"kill eater user score type 1",	"strange restriction user type 1",	"strange restriction user value 1",	true ),
	strange_attr_set_t( "kill eater user 2",	"kill eater user score type 2", "strange restriction user type 2",	"strange restriction user value 2",	true ),
	strange_attr_set_t( "kill eater user 3",	"kill eater user score type 3", "strange restriction user type 3",	"strange restriction user value 3",	true ),
};

int GetKillEaterAttrCount()
{
#ifdef DBGFLAG_ASSERT
	// Verify our commented assumption that all of the non-user-customizable attributes will be followed by
	// all of the user-customizable attributes.
	bool bInUserCustomizableBlock = false;

	for ( int i = 0; i < ARRAYSIZE( g_KillEaterAttr ); i++ )
	{
		if ( bInUserCustomizableBlock )
		{
			AssertMsg( g_KillEaterAttr[i].m_bIsUserCustomizable, "Ordering assumption for g_KillEaterAttr violated! User-customizable attributes should all be at the end of the list!" );
		}

		bInUserCustomizableBlock |= g_KillEaterAttr[i].m_bIsUserCustomizable;
	}
#endif

	return ARRAYSIZE( g_KillEaterAttr );
}

int GetKillEaterAttrCount_UserCustomizable()
{
	int iCount = 0;
	for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
	{
		if ( GetKillEaterAttr_IsUserCustomizable( i ) )
		{
			iCount++;
		}
	}

	return iCount;
}

const CEconItemAttributeDefinition *GetKillEaterAttr_Score( int i )
{
	Assert( i >= 0 );
	Assert( i < GetKillEaterAttrCount() );

	const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrScore;
	AssertMsg1( pAttrRes, "Missing Killeater attr score %s", g_KillEaterAttr[ i ].m_attrScore.GetName() );
	
	return pAttrRes;
}

const CEconItemAttributeDefinition *GetKillEaterAttr_Type( int i )
{
	Assert( i >= 0 );
	Assert( i < GetKillEaterAttrCount() );

	const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrType;
	AssertMsg1( pAttrRes, "Missing Killeater attr type %s", g_KillEaterAttr[ i ].m_attrType.GetName() );

	return pAttrRes;
}

const CEconItemAttributeDefinition *GetKillEaterAttr_Restriction( int i )
{
	Assert( i >= 0 );
	Assert( i < GetKillEaterAttrCount() );

	const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrRestriction;
	AssertMsg1( pAttrRes, "Missing Killeater attr restriction %s", g_KillEaterAttr[ i ].m_attrRestriction.GetName() );

	return pAttrRes;
}

const CEconItemAttributeDefinition *GetKillEaterAttr_RestrictionValue( int i )
{
	Assert( i >= 0 );
	Assert( i < GetKillEaterAttrCount() );

	const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrRestrictionValue;
	AssertMsg1( pAttrRes, "Missing Killeater attr restriction value %s", g_KillEaterAttr[ i ].m_attrRestrictionValue.GetName() );
	
	return pAttrRes;
}

bool GetKillEaterAttr_IsUserCustomizable( int i )
{
	Assert( i >= 0 );
	Assert( i < GetKillEaterAttrCount() );

	return g_KillEaterAttr[i].m_bIsUserCustomizable;
}


bool GetKilleaterValueByEvent( const IEconItemInterface* pItem, const kill_eater_event_t& EEventType, uint32& value )
{
	for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
	{
		const CEconItemAttributeDefinition *pAttribKillEater		  = GetKillEaterAttr_Score( i );
		const CEconItemAttributeDefinition *pAttribKillEaterScoreType = GetKillEaterAttr_Type( i );
		
		Assert( pAttribKillEater && pAttribKillEaterScoreType );
		if ( !pAttribKillEater || !pAttribKillEaterScoreType )
			return false;

		// make sure this item even has a kill count attribute we're looking for
		uint32 unKillEaterAttrValue;
		if ( !pItem->FindAttribute( pAttribKillEater, &unKillEaterAttrValue ) )
			continue;
		
		uint32 unKillEaterScoreTypeAttrValue = kKillEaterEvent_PlayerKill;

		float fKillEaterScoreTypeAttrValue;
		if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttribKillEaterScoreType, &fKillEaterScoreTypeAttrValue ) )
		{
			unKillEaterScoreTypeAttrValue = (uint32)fKillEaterScoreTypeAttrValue;
		}

		// this isn't the attribute we're trying to find
		if ( EEventType != (kill_eater_event_t)unKillEaterScoreTypeAttrValue )
			continue;

		value = unKillEaterAttrValue;
		return true;
	}

	return false;
}

// Does this thing have kill eater
bool BIsItemStrange( const IEconItemInterface *pItem )
{
	// Go over the attributes of the item, if it has any strange attributes the item is strange and don't apply
	uint32 unKillEaterAttr;
	for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
	{
		if ( pItem->FindAttribute( GetKillEaterAttr_Score( i ), &unKillEaterAttr ) )
		{
			return true;
		}
	}
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Get a localization token that describes why an item is not usable
//			in the trade-up crafting.  Returns NULL if no reason.  Can pass in
//			another item to compare against, which causes extra consistency checks
//-----------------------------------------------------------------------------
const char* GetCollectionCraftingInvalidReason( const IEconItemInterface *pTestItem, const IEconItemInterface *pSourceItem )
{
	if ( !pTestItem )
	{
		return "#TF_CollectionCrafting_NoItem";
	}

	// Needs to have a collection
	const CEconItemCollectionDefinition* pTestCollection = pTestItem->GetItemDefinition()->GetItemCollectionDefinition();
	if ( !pTestCollection )
	{
		return "#TF_CollectionCrafting_NoCollection";
	}

	// Make sure this item is a part of the collection it claims to be in
	{
		item_definition_index_t nThisDefIndex = pTestItem->GetItemDefIndex();
		bool bFound = false;
		for( int i=0; i < pTestCollection->m_iItemDefs.Count() && !bFound; ++i )
		{
			bFound |= pTestCollection->m_iItemDefs[i] == nThisDefIndex;
		}

		if ( !bFound )
		{
			return "#TF_CollectionCrafting_NoCollection";
		}
	}
	 
	// Needs rarity
	uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
	if( nRarity == k_unItemRarity_Any )
	{
		return "#TF_CollectionCrafting_NoRarity";
	}

	// Can't use items with rarity at the "top" of a collection (what would they craft into?)
	if ( nRarity == pTestCollection->GetMaxRarity() )
	{
		return "#TF_CollectionCrafting_MaxRarity";
	}

	// No self mades or community items
	uint32 eQuality = pTestItem->GetQuality();
	if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	// This is how we test for unusuals.  Don't let unusuals be crafted
	static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
	if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
	if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	// Not allowed to be crafted?
	if ( !pTestItem->IsUsableInCrafting() )
	{
		return "#TF_CollectionCrafting_NotCraftable";
	}

	// If another item was passed in, we have a few consistency checks to make
	if ( pSourceItem )
	{
		// Need to have the same rarity
		if ( nRarity != pSourceItem->GetItemDefinition()->GetRarity() )
		{
			return "#TF_CollectionCrafting_MismatchRarity";
		}

		// Need to have the same strangeness
		if ( BIsItemStrange( pSourceItem ) != BIsItemStrange( pTestItem ) )
		{
			return "#TF_CollectionCrafting_MismatchStrange";
		}
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Get a localization token that describes why an item is not usable
//			in the Halloween Offering.  Returns NULL if no reason.  Can pass in
//			another item to compare against, which causes extra consistency checks
//-----------------------------------------------------------------------------
const char* GetHalloweenOfferingInvalidReason( const IEconItemInterface *pTestItem, const IEconItemInterface *pSourceItem )
{
	// Must either be a Cosmetic
	// Taunt
	// Allowable Tool (Strange part, Paint, name tag, killstreak).  Not crates, keys
	// Marketable Weapon ie Strange, Genuine, Vintage, paintkit

	// Cannot be Unusual

	if ( !pTestItem )
	{
		return "#TF_CollectionCrafting_NoItem";
	}

	// No self mades or community items
	uint32 eQuality = pTestItem->GetQuality();
	if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	// This is how we test for unusuals.  Don't let unusuals be crafted
	static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
	if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
	if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
	{
		return "#TF_CollectionCrafting_NoUnusual";
	}

	// Invalid Items
	static CSchemaAttributeDefHandle pAttrDef_CannotTransmute( "cannot_transmute" );
	if ( pTestItem->FindAttribute( pAttrDef_CannotTransmute ) )
	{
		return "#TF_HalloweenOffering_Invalid";
	}

	static CSchemaAttributeDefHandle pAttrDef_CannotDelete( "cannot delete" );
	if ( pTestItem->FindAttribute( pAttrDef_CannotDelete ) )
	{
		return "#TF_HalloweenOffering_Invalid";
	}

	const CEconItemDefinition *pItemDef = pTestItem->GetItemDefinition();
	if ( pItemDef == NULL )
	{
		return "#TF_CollectionCrafting_NoItem";
	}

	if ( pTestItem->IsTemporaryItem() )
	{
		return "#TF_CollectionCrafting_NoItem";
	}

	// If you are a taunt or a cosmetic you are allowed
	if ( pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MISC || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_TAUNT )
	{
		// do not 'medal' equip region items
		if ( pTestItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "medal" ) )
		{
			return "#TF_HalloweenOffering_Invalid";
		}

		return NULL;
	}

	// Do not allow Crates
	if ( ( pItemDef->GetCapabilities() & ITEM_CAP_DECODABLE ) != 0 )
	{
		return "#TF_HalloweenOffering_Invalid";
	}

	// Cause of weird legacy items lets be explicit about what we allow
	if ( pItemDef->IsTool() )
	{
		// ignore everything that is not a paint can tool
		const IEconTool *pEconTool = pItemDef->GetEconTool();
		if ( !pEconTool )
			return "#TF_HalloweenOffering_Invalid";

		const char *pToolType = pEconTool->GetTypeName();

		if ( !V_strcmp( pToolType, "paint_can" ) ) 
			return NULL;
		else if ( !V_strcmp( pToolType, "strange_part" ) ) 
			return NULL;
		else if ( !V_strcmp( pToolType, "name" ) ) 
			return NULL;
		else if ( !V_strcmp( pToolType, "desc" ) ) 
			return NULL;
		else if ( !V_strcmp( pToolType, "killstreakifier" ) )
			return NULL;
		else if ( !V_strcmp( pToolType, "strangifier" ) )
			return NULL;

		// Not a tool we are allowing
		return "#TF_HalloweenOffering_Invalid";
	}

	// Otherwise you must be a weapon or we won't allow
	if ( pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PRIMARY 
		|| pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_SECONDARY
		|| pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MELEE
		|| pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_BUILDING
		|| pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PDA
		|| pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PDA2
	) {
		// Must be strange, genuine, vintage, haunted or paintkit (ie a marketable weapon)
		eQuality = pTestItem->GetQuality();
		if ( eQuality == AE_RARITY1 
			|| eQuality == AE_VINTAGE 
			|| eQuality == AE_HAUNTED
			|| eQuality == AE_COLLECTORS 
			|| eQuality == AE_PAINTKITWEAPON 
		) {
			return NULL;
		}

		// Weapons with rarity are allowed
		uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
		if ( nRarity != k_unItemRarity_Any )
		{
			return NULL;
		}

		// Strange items.  Dont just check for strange quality, actually check for a strange attribute.
		// See if we've got any strange attributes.
		for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
		{
			if ( pTestItem->FindAttribute( GetKillEaterAttr_Score( i ) ) )
			{
				return NULL;
			}
		}
	}

	return "#TF_HalloweenOffering_Invalid";
}

const char* GetCraftCommonStatClockInvalidReason( const class IEconItemInterface *pTestItem, const class IEconItemInterface *pSourceItem )
{
	if ( !pTestItem )
	{
		return "#TF_CollectionCrafting_NoItem";
	}

	// Not allowed to be crafted?
	if ( !pTestItem->IsUsableInCrafting() )
	{
		return "#TF_CollectionCrafting_NotCraftable";
	}

	// No self mades or community items
	uint32 eQuality = pTestItem->GetQuality();
	if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
		return "#TF_CollectionCrafting_NoUnusual";

	// This is how we test for unusuals.  Don't let unusuals be crafted
	static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
	if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
		return "#TF_CollectionCrafting_NoUnusual";

	static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
	if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
		return "#TF_CollectionCrafting_NoUnusual";

	const CEconItemDefinition *pItemDef = pTestItem->GetItemDefinition();
	if ( pItemDef == NULL )
		return "#TF_CollectionCrafting_NoItem";

	if ( pTestItem->IsTemporaryItem() )
		return "#TF_CollectionCrafting_NoItem";

	// Strange items.  Dont just check for strange quality, actually check for a strange attribute.
	// See if we've got any strange attributes.
	for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
	{
		if ( pTestItem->FindAttribute( GetKillEaterAttr_Score( i ) ) )
		{
			return NULL;
		}
	}

	// Needs Rarity
	uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
	if ( nRarity != k_unItemRarity_Any && nRarity > 1 ) // do not allow default nor common rarity
	{
		return NULL;
	}

	return "#TF_MannCoTrade_ItemInvalid";
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
enum { kMaxCardUpgradesPerItem = 2 };

int GetMaxCardUpgradesPerItem()
{
	return kMaxCardUpgradesPerItem;
}

const CEconItemAttributeDefinition *GetCardUpgradeForIndex( const IEconItemInterface *pItem, int i )
{
	Assert( pItem );
	Assert( i >= 0 );
	Assert( i < kMaxCardUpgradesPerItem );

	class CGetNthUserGeneratedAttributeIterator : public IEconItemUntypedAttributeIterator
	{
	public:
		CGetNthUserGeneratedAttributeIterator( int iTargetIndex )
			: m_iCount( iTargetIndex )
			, m_pAttrDef( NULL )
		{
		}

		virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef ) OVERRIDE
		{
			if ( pAttrDef->GetUserGenerationType() != 0 && m_iCount-- == 0 )
			{
				m_pAttrDef = pAttrDef;
				return false;
			}

			return true;
		}

		const CEconItemAttributeDefinition *GetAttrDef() const { return m_pAttrDef; }

	private:
		int m_iCount;
		const CEconItemAttributeDefinition *m_pAttrDef;
	};

	CGetNthUserGeneratedAttributeIterator findNthAttrIterator( i );
	pItem->IterateAttributes( &findNthAttrIterator );

	return findNthAttrIterator.GetAttrDef();
}