//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Common objects and utilities related to the in-game item store
//
//=============================================================================

#ifndef ECON_STORE_H
#define ECON_STORE_H
#ifdef _WIN32
#pragma once
#endif

#include "UtlSortVector.h"
#include "vstdlib/IKeyValuesSystem.h"
#include "econ/econ_storecategory.h"
#ifdef CLIENT_DLL
#include "client_community_market.h"
#endif // CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Error code enum for purchase messages
//-----------------------------------------------------------------------------
enum EPurchaseResult
{
	k_EPurchaseResultOK	= 1,					// Success
	k_EPurchaseResultFail = 2,					// Generic error
	k_EPurchaseResultInvalidParam = 3,			// Invalid parameter
	k_EPurchaseResultInternalError = 4,			// Internal error
	k_EPurchaseResultNotApproved = 5,			// Tried to finalize a transaction that has not yet been approved
	k_EPurchaseResultAlreadyCommitted = 6,		// Tried to finalize a transaction that has already been committed
	k_EPurchaseResultUserNotLoggedIn = 7,		// User is not logged into Steam
	k_EPurchaseResultWrongCurrency = 8,			// Microtransaction's currency does not match user's wallet currency
	k_EPurchaseResultAccountError = 9,			// User's account does not exist or is temporarily unavailable
	k_EPurchaseResultInvalidItem = 10,			// User is trying to purchase an item that doesn't exist or is not for sale
	k_EPurchaseResultNotEnoughBackpackSpace = 11,	// User did not have enough backpack space
	k_EPurchaseResultLimitedQuantityItemsUnavailable = 12,	// User tried to purchase limited-quantity items but there weren't enough left in stock

	k_EPurchaseResultInsufficientFunds = 100,	// User does not have wallet funds
	k_EPurchaseResultTimedOut = 101,			// Time limit for finalization has been exceeded
	k_EPurchaseResultAcctDisabled = 102,		// Steam account is disabled
	k_EPurchaseResultAcctCannotPurchase = 103,	// Steam account is not allowed to make a purchase
	k_EMicroTxnResultFailedFraudChecks = 104,   // Fraud checks inside of Steam failed

	k_EPurchaseResultOldPriceSheet = 150,		// Information on the purchase didn't match the current price sheet
	k_EPurchaseResultTxnNotFound = 151			// Could not find the transaction specified
};


const char *PchNameFromEPurchaseResult( EPurchaseResult ePurchaseState );

//-----------------------------------------------------------------------------
// Purpose: State of a transaction
//
// WARNING: VALUES STORED IN DATABASE. DO NOT RENUMBER!!!
//-----------------------------------------------------------------------------
enum EPurchaseState
{
	k_EPurchaseStateInvalid = 0,				// Invalid
	k_EPurchaseStateInit = 1,					// We have sent InitPurchase to Steam
	k_EPurchaseStateWaitingForAuthorization = 2, // We have gotten initial authorization from Steam. Waiting for user to authorize.
	k_EPurchaseStatePending = 3,				// We are attempting to commit the transaction
	k_EPurchaseStateComplete = 4,				// The transaction was successful
	k_EPurchaseStateFailed = 5,					// The transaction failed
	k_EPurchaseStateCanceled = 6,				// The transaction was canceled
	k_EPurchaseStateRefunded = 7,				// The transaction was refunded
	k_EPurchaseStateChargeback = 8,				// The transaction was charged back
	k_EPurchaseStateChargebackReversed = 9,		// A chargeback has failed and we got the money
	k_EPurchaseStateLast = k_EPurchaseStateChargebackReversed,
};

const char *PchNameFromEPurchaseState( EPurchaseState ePurchaseState );

// DO NOT RENUMBER! These values are stored in the audit log table.
enum EGCTransactionAuditReason
{
	k_EGCTransactionAudit_GCTransactionCompleted = 0,				// The transaction completed successfully on the GC.
	k_EGCTransactionAudit_GCTransactionInit = 1,					// The transaction was initialized.
	k_EGCTransactionAudit_GCTransactionPostInit = 2,				// The result of attempting to initialize the transaction. This is where the SteamTxnID is set.
	k_EGCTransactionAudit_GCTransactionFinalize = 3,				// We have started to finalize the transaction (so probably set it to pending).
	k_EGCTransactionAudit_GCTransactionFinalizeFailed = 4,			// Our attempt to finalize the transaction failed for some reason (not due to a timeout).
	k_EGCTransactionAudit_GCTransactionCanceled = 5,				// The client requested that we cancel the transaction.
	k_EGCTransactionAudit_SteamFailedMismatch = 6,					// Steam failed the transaction but we did not.
	k_EGCTransactionAudit_GCRemovePurchasedItems = 7,				// We are attempting to remove the purchased items from their backpack (due to rollback or failure).
	k_EGCTransactionAudit_GCTransactionInsert = 8,					// We are inserting a transaction, usually one created by a web interface (i.e.: a cd-key operation) instead of a standard store interaction.
	k_EGCTransactionAudit_GCTransactionCompletedPostChargeback = 9,	// We thought this transaction had been charged back but Steam came back later and told us it was successful after all.

	k_EGCTransactionAuditLast = k_EGCTransactionAudit_GCTransactionCompletedPostChargeback,
};

const char *PchNameFromEGCTransactionAuditReason( EGCTransactionAuditReason eAuditReason );

enum EGCTransactionAuditInsertReason
{
	k_EGCTransactionAuditInsert_Invalid = 0,					// 
	k_EGCTransactionAuditInsert_CDKey = 1,						// A CD Key was used for this transaction.
	k_EGCTransactionAuditInsert_SettlementNoMatch_Failed = 2,	// We inserted a failed record during settlement to unblock the process.
	k_EGCTransactionAuditInsert_SettlementNoMatch_Pending = 3,	// We inserted a pending record during settlement to unblock the process.

	k_EGCTransactionAuditInsertLast = k_EGCTransactionAuditInsert_SettlementNoMatch_Pending,
};

//-----------------------------------------------------------------------------
// Purpose: Currencies we support
//
// WARNING: VALUES STORED IN DATABASE. DO NOT RENUMBER!!!
// WARNING: THESE DON'T MATCH THE STEAM NUMERIC IDS!!! WE TALK USING CURRENCY
//			CODES LIKE "VND" AND IF YOU SAY "15" TERRIBLE THINGS WILL HAPPEN
//-----------------------------------------------------------------------------
enum ECurrency
{
	k_ECurrencyFirst = 0,
	k_ECurrencyUSD = 0,
	k_ECurrencyGBP = 1,
	k_ECurrencyEUR = 2,
	k_ECurrencyRUB = 3,
	k_ECurrencyBRL = 4,
	// space for Dota currencies
	k_ECurrencyJPY = 8,
	k_ECurrencyNOK = 9,
	k_ECurrencyIDR = 10,
	k_ECurrencyMYR = 11,
	k_ECurrencyPHP = 12,
	k_ECurrencySGD = 13,
	k_ECurrencyTHB = 14,
	k_ECurrencyVND = 15,
	k_ECurrencyKRW = 16,
	k_ECurrencyTRY = 17,
	k_ECurrencyUAH = 18,
	k_ECurrencyMXN = 19,
	k_ECurrencyCAD = 20,
	k_ECurrencyAUD = 21,
	k_ECurrencyNZD = 22,
	k_ECurrencyPLN = 23,
	k_ECurrencyCHF = 24,
	k_ECurrencyCNY = 25,
	k_ECurrencyTWD = 26,
	k_ECurrencyHKD = 27,
	k_ECurrencyINR = 28,
	k_ECurrencyAED = 29,
	k_ECurrencySAR = 30,
	k_ECurrencyZAR = 31,
	k_ECurrencyCOP = 32,
	k_ECurrencyPEN = 33,
	k_ECurrencyCLP = 34,

	// NOTE: Not actually the Maximum currency value, but the Terminator for the possible currency code range.
	k_ECurrencyMax = 35,

	// make this a big number so we can avoid having to move it when we add another currency type
	k_ECurrencyInvalid = 255,
	k_ECurrencyCDKeyTransaction = k_ECurrencyInvalid,
};

// Macro for looping across all valid currencies
#define FOR_EACH_CURRENCY( _i ) for ( ECurrency _i = GetFirstValidCurrency(); _i != k_ECurrencyInvalid; _i = GetNextValidCurrency( _i ) )

const char *PchNameFromECurrency( ECurrency eCurrency );	// NOTE: Defined with ENUMSTRINGS_START/ENUMSTRINGS_REVERSE macros
ECurrency ECurrencyFromName( const char *pchName );			//

inline bool BIsCurrencyValid( ECurrency eCurrency )
{
	switch ( eCurrency )
	{
	case k_ECurrencyUSD:
	case k_ECurrencyGBP:
	case k_ECurrencyEUR:
	case k_ECurrencyRUB:
	case k_ECurrencyBRL:
	case k_ECurrencyJPY:
	case k_ECurrencyNOK:
	case k_ECurrencyIDR:
	case k_ECurrencyMYR:
	case k_ECurrencyPHP:
	case k_ECurrencySGD:
	case k_ECurrencyTHB:
	case k_ECurrencyVND:
	case k_ECurrencyKRW:
	case k_ECurrencyTRY:
	case k_ECurrencyUAH:
	case k_ECurrencyMXN:
	case k_ECurrencyCAD:
	case k_ECurrencyAUD:
	case k_ECurrencyNZD:
	//case k_ECurrencyPLN:
	case k_ECurrencyCHF:
	case k_ECurrencyCNY:
	case k_ECurrencyTWD:
	case k_ECurrencyHKD:
	case k_ECurrencyINR:
	case k_ECurrencyAED:
	case k_ECurrencySAR:
	case k_ECurrencyZAR:
	case k_ECurrencyCOP:
	case k_ECurrencyPEN:
	case k_ECurrencyCLP:
		return true;
	}

	return false;
}

inline ECurrency GetFirstValidCurrency()
{
	for ( int i = k_ECurrencyFirst; i < k_ECurrencyMax; i++ )
	{
		if ( BIsCurrencyValid( (ECurrency)i ) )
			return (ECurrency)i;
	}
	return k_ECurrencyInvalid;
}

inline ECurrency GetNextValidCurrency( ECurrency ePrevious )
{
	for ( int i = ePrevious + 1; i < k_ECurrencyMax; i++ )
	{
		if ( BIsCurrencyValid( (ECurrency)i ) )
			return (ECurrency)i;
	}
	return k_ECurrencyInvalid;
}

//-----------------------------------------------------------------------------
// Purpose: Simple struct for pairing a sort type with a localization string
//-----------------------------------------------------------------------------
struct ItemSortTypeData_t
{
	const char *szSortDesc;		// localization string
	uint32		iSortType;		// maps to the GC-specific sort value
};

// Description of a single item for sale
struct econ_store_entry_t
{
	// Constructor
	econ_store_entry_t()
	:	m_pchCategoryTags( NULL ),
		m_unGiftSteamPackageID( 0 ),
		m_bHighlighted( false )
	{
		V_memset( m_unBaseCosts, 0, sizeof(m_unBaseCosts) );
		V_memset( m_unSaleCosts, 0, sizeof(m_unSaleCosts) );
	}

	void SetItemDefinitionIndex( item_definition_index_t usDefIndex );
	item_definition_index_t GetItemDefinitionIndex() const { return m_usDefIndex; }
	
	void InitCategoryTags( const char *pTags );	// Sets m_pchCategoryTags and initializes m_vecTagIds and m_fRentalPriceScale

	bool IsListedInCategory( StoreCategoryID_t unID ) const;	// Is this item listed in the given category?
	bool IsListedInSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const;	// Is this item listed in one of Category's subcategories?
	bool IsListedInCategoryOrSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const;	// Is this item listed in Category or one of Category's subcategories?
	
	bool IsOnSale( ECurrency eCurrency ) const;
	bool IsRentable() const;
#ifdef CLIENT_DLL
	bool HasDiscount( ECurrency eCurrency, item_price_t *out_punOptionalBasePrice ) const;			// returns true if we're on sale or if we're a bundle with a discounted total price
#endif // CLIENT_DLL
	item_price_t GetCurrentPrice( ECurrency eCurrency ) const;
	float GetRentalPriceScale() const;

	uint32 GetGiftSteamPackageID() const { return m_unGiftSteamPackageID; }

	// Helper function -- so we do this calculation in a single place.
	static item_price_t GetDiscountedPrice( ECurrency eCurrency, item_price_t unBasePrice, float fDiscountPercentage );

	static item_price_t CalculateSalePrice( const econ_store_entry_t* pSaleStoreEntry, ECurrency eCurrency, float fDiscountPercentage, int32 *out_pAdjustedDiscountPercentage = NULL );

	item_price_t GetBasePrice( ECurrency eCurrency ) const
	{
		Assert( eCurrency >= k_ECurrencyFirst );
		Assert( eCurrency < k_ECurrencyMax );
		if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
			return 0;
#ifdef CLIENT_DLL
		if ( m_bIsMarketItem )
		{
			const client_market_data_t *pClientMarketData = GetClientMarketData( GetItemDefinitionIndex(), AE_UNIQUE );
			if ( !pClientMarketData )
				return 0;
			return pClientMarketData->m_unLowestPrice;
		}
#endif
		// Weird-looking pattern: we're making sure that the value we're about to return fits correctly
		// into the variable we're about to put it into. We do this to avoid integer conversion problems,
		// especially overflow (!) where someone changes one of the return type or the storage type but
		// not the other.
		Assert( (item_price_t)m_unBaseCosts[eCurrency] == m_unBaseCosts[eCurrency] );
		return m_unBaseCosts[eCurrency];
	}

	item_price_t GetSalePrice( ECurrency eCurrency ) const
	{
		Assert( eCurrency >= k_ECurrencyFirst );
		Assert( eCurrency < k_ECurrencyMax );
		if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
			return 0;
#ifdef CLIENT_DLL
		if ( m_bIsMarketItem )
		{
			const client_market_data_t *pClientMarketData = GetClientMarketData( GetItemDefinitionIndex(), AE_UNIQUE );
			if ( !pClientMarketData )
				return 0;
			return pClientMarketData->m_unLowestPrice;
		}
#endif
		// Weird-looking pattern: we're making sure that the value we're about to return fits correctly
		// into the variable we're about to put it into. We do this to avoid integer conversion problems,
		// especially overflow (!) where someone changes one of the return type or the storage type but
		// not the other. 
		Assert( (item_price_t)m_unSaleCosts[eCurrency] == m_unSaleCosts[eCurrency] );
		return m_unSaleCosts[eCurrency];
	}

	uint16 GetQuantity() const
	{
		return m_usQuantity;
	}

	const char* GetDate() const
	{
		return m_strDate.Get();
	}

	bool CanPreview() const
	{
		// No previewing of new items or weapons.
		return m_bPreviewAllowed;
	}

	void SetQuantity( uint16 usQuantity )
	{
		Assert( usQuantity > 0 );
		m_usQuantity = usQuantity;
	}

	void ValidatePrice( ECurrency eCurrency, item_price_t unPrice );

	void SetBasePrice( ECurrency eCurrency, item_price_t unPrice )
	{
		Assert( eCurrency >= k_ECurrencyFirst );
		Assert( eCurrency < k_ECurrencyMax );
		if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
			return;

		ValidatePrice( eCurrency, unPrice );

		m_unBaseCosts[eCurrency] = unPrice;
	}

	void SetSalePrice( ECurrency eCurrency, item_price_t unPrice )
	{
		Assert( eCurrency >= k_ECurrencyFirst );
		Assert( eCurrency < k_ECurrencyMax );
		if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
			return;

		ValidatePrice( eCurrency, unPrice );

		// It's legal to have a sale price of zero, meaninig "this item is not on sale" in this
		// currency.
		// Assert( unPrice > 0 );
		m_unSaleCosts[eCurrency] = unPrice;
	}

	void SetSteamGiftPackageID( uint32 unGiftSteamPackageID )
	{
		m_unGiftSteamPackageID = unGiftSteamPackageID;
	}
	
	void SetDate( const char* pszDate )
	{
		m_strDate.Set( pszDate );
	}


	bool IsValidCategoryTagIndex( uint32 iIndex ) const
	{
		AssertMsg( m_vecCategoryTags.IsValidIndex( iIndex ), "Category tag index out of range." );
		return m_vecCategoryTags.IsValidIndex( iIndex );
	}

	uint32 GetCategoryTagCount() const
	{
		return m_vecCategoryTags.Count();
	}

	const char *GetCategoryTagNameFromIndex( uint32 iIndex ) const
	{
		if ( !IsValidCategoryTagIndex( iIndex ) )
			return NULL;

		return m_vecCategoryTags[ iIndex ].m_strName;
	}

	StoreCategoryID_t GetCategoryTagIDFromIndex( uint32 iIndex ) const;

	const char *GetCategoryTagString() const
	{
		return m_pchCategoryTags;
	}

	bool							m_bLimited;							// Item is a limited sale
	bool							m_bNew;								// Item is new
	bool							m_bHighlighted;						// Item is highlighted
	CUtlString						m_strDate;							// Date Added
	bool							m_bSoldOut;							// True if the item is sold out from the store (for example if the item is a ticket or another physical item)
	bool							m_bPreviewAllowed;					// Is this item previewable?
	bool							m_bIsPackItem;						// Is this item a pack item? Pack items are items which are not individually for sale, but are sold via a bundle known as a "pack bundle"

	bool							m_bIsMarketItem;					// Is Market Item Link

private:
	item_definition_index_t			m_usDefIndex;						// DefIndex of the item

	// Private data so that we can check in the accessor functions that the data fits before returning it.
	item_price_t					m_unBaseCosts[k_ECurrencyMax];		// Costs of the items indexed by ECurrency -- if the items are on sale, this will be the current sale price
	item_price_t					m_unSaleCosts[k_ECurrencyMax];		// Original costs of the items indexed by ECurrency -- if the items are on sale, this will be the pre-sale price
	uint16							m_usQuantity;						// Quantity sold in a single purchase (ie., dueling pistols come in stacks of five)
	float							m_fRentalPriceScale;				// 100.0 or greater means "unavailable to rent"
	uint32							m_unGiftSteamPackageID;				// if non-zero, when this item is purchased (including inside bundles, etc.), grant a gift copy of this Steam package

	struct CategoryTag_t
	{
		CUtlString				m_strName;								// Individual tag name, like "Weapons," "New," etc.
		StoreCategoryID_t		m_unID;									// The category ID
	};
	CCopyableUtlVector< CategoryTag_t >	m_vecCategoryTags;				// Category tag data
	
	const char					*m_pchCategoryTags;						// All tags - this string will something like: "New" or "Weapons+New" etc.
};

#ifdef GC_DLL
struct econ_store_timed_sale_item_t
{
	item_definition_index_t m_unItemDef;
	float					m_fPricePercentage;	// 100.0 = regular price; 50.0 = half price
};

struct econ_store_timed_sale_t
{
	bool m_bSaleCurrentlyActive;				// set in ::UpdatePricesForTimedSales()
	CUtlConstString m_sIdentifier;				// can't point to memory in the base KV because we toss it afterwards
	RTime32	m_SaleStartTime;
	RTime32	m_SaleEndTime;
	CUtlVector<econ_store_timed_sale_item_t> m_vecSaleItems;

	// Work around protected default vector constructor.
	econ_store_timed_sale_t() { }
	econ_store_timed_sale_t( const econ_store_timed_sale_t& other )
		: m_bSaleCurrentlyActive( other.m_bSaleCurrentlyActive )
		, m_sIdentifier( other.m_sIdentifier )
		, m_SaleStartTime( other.m_SaleStartTime )
		, m_SaleEndTime( other.m_SaleEndTime )
	{
		m_vecSaleItems.CopyArray( other.m_vecSaleItems.Base(), other.m_vecSaleItems.Count() );
	}
};
#endif // GC_DLL

// Spend xxx amount of money, get a free item from the loot list
struct store_promotion_spend_for_free_item_t
{
	const CEconItemDefinition	  *m_pItemDef;
	item_price_t				   m_rgusPriceThreshold[k_ECurrencyMax];	// Price threshold to get an item from the loot list indexed by ECurrency
};


//-----------------------------------------------------------------------------
// Purpose: Class that represents what's currently for sale in TF
//-----------------------------------------------------------------------------
typedef enum
{
	kEconStoreSortType_Price_HighestToLowest = 0,
	kEconStoreSortType_Price_LowestToHighest = 1,
	kEconStoreSortType_DevName_AToZ = 2,
	kEconStoreSortType_DevName_ZToA = 3,
	kEconStoreSortType_Name_AToZ = 4,
	kEconStoreSortType_Name_ZToA = 5,
	kEconStoreSortType_ItemDefIndex = 6,
	kEconStoreSortType_ReverseItemDefIndex = 7,
	kEconStoreSortType_DateNewest = 8,
	kEconStoreSortType_DateOldest = 9,
} eEconStoreSortType;

struct price_point_map_key_t
{
	item_price_t m_unPriceUSD;
	ECurrency m_eCurrency;

	static bool Less( const price_point_map_key_t& a, const price_point_map_key_t& b )
	{
		if ( a.m_eCurrency == b.m_eCurrency )
			return a.m_unPriceUSD < b.m_unPriceUSD;

		return a.m_eCurrency < b.m_eCurrency;
	}
};

typedef CUtlMap<price_point_map_key_t, item_price_t> CurrencyPricePointMap_t;

class CEconStorePriceSheet
{
public:
	typedef CUtlMap<item_definition_index_t, econ_store_entry_t> StoreEntryMap_t;
	typedef CUtlMap<const char *, float> RentalPriceScaleMap_t;
	typedef CUtlVector<item_definition_index_t> FeaturedItems_t;

	CEconStorePriceSheet();
	~CEconStorePriceSheet();

	bool InitFromKV( KeyValues *pKVPrices );

	// Gets or sets the version stamp. This is just a number the GC can use
	// to know if the client is in sync without sending it down on every
	// request.
	RTime32 GetVersionStamp( void ) const { return m_RTimeVersionStamp; }
	void SetVersionStamp( RTime32 stamp ) { m_RTimeVersionStamp = stamp; }

	uint32 GetHashForAllItems() const { return m_unHashForAllItems; }

	typedef CUtlMap<uint16, econ_store_entry_t> EconStoreEntryMap_t;
	EconStoreEntryMap_t &GetEntries() { return m_mapEntries; }
	
#ifdef GC_DLL
	econ_store_entry_t *GetEntryWriteable( item_definition_index_t unDefIndex );
#endif // GC_DLL

	const StoreEntryMap_t &GetEntries() const { return m_mapEntries; }
	const CEconStoreCategoryManager::StoreCategory_t *GetFeaturedItems( void ) { return &m_FeaturedItems; }
	const econ_store_entry_t *GetEntry( item_definition_index_t usDefIndex ) const;

	uint32 GetFeaturedItemIndex() const { return m_unFeaturedItemIndex; }
	void SetFeaturedItemIndex( uint32 unIdx ) { m_unFeaturedItemIndex = unIdx; }

	void SetEconStoreSortType( eEconStoreSortType eType ) { m_eEconStoreSortType = eType; }
	eEconStoreSortType GetEconStoreSortType() { return m_eEconStoreSortType; }

	const store_promotion_spend_for_free_item_t *GetStorePromotion_SpendForFreeItem() const { return &m_StorePromotionSpendForFreeItem; }
	const CEconItemDefinition * GetStorePromotion_FirstTimePurchaseItem() const { return m_pStorePromotionFirstTimePurchaseItem; }
	const CEconItemDefinition * GetStorePromotion_FirstTimeWebPurchaseItem() const { return m_pStorePromotionFirstTimeWebPurchaseItem; }

	uint32 GetPreviewPeriod() const { return m_unPreviewPeriod; }
	uint32 GetBonusDiscountPeriod() const { return m_unBonusDiscountPeriod; }
	float GetPreviewPeriodDiscount() const { return m_flPreviewPeriodDiscount; }

	bool BItemExistsInPriceSheet( item_definition_index_t unDefIndex ) const;

	float GetRentalPriceScale( const char *pszCategory ) const
	{
		RentalPriceScaleMap_t::IndexType_t i = m_mapRentalPriceScales.Find( pszCategory );
		if ( i == RentalPriceScaleMap_t::InvalidIndex() )
			return 1.0f;

		return m_mapRentalPriceScales[i];
	}

	KeyValues *GetRawData() const { return m_pKVRaw; }

#ifdef GC_DLL
	void UpdatePricesForTimedSales( const RTime32 curTime );
	void DumpTimeSaleState( const RTime32 curTime ) const;
#endif // GC_DLL

#ifdef CLIENT_DLL
	const FeaturedItems_t& GetFeaturedItems() const { return m_vecFeaturedItems; }
#endif // CLIENT_DLL

private:
	bool BInitEntryFromKV( KeyValues *pKVEntry );
#ifdef CLIENT_DLL
	bool BInitMarketEntryFromKV( KeyValues *pKVEntry );
#endif // CLIENT_DLL

#ifdef GC_DLL
	bool InitTimedSaleEntryFromKV( KeyValues *pKVTimedSaleEntry );
	bool VerifyTimedSaleEntries();
#endif // GC_DLL

private:
	void Clear();
	uint32 CalculateHashFromItems() const;
	
	KeyValues	*m_pKVRaw;
	RTime32		m_RTimeVersionStamp;
	CEconStoreCategoryManager::StoreCategory_t	m_FeaturedItems;		// Special section, not a tab, kept outside m_vecContents
	StoreEntryMap_t m_mapEntries;
	RentalPriceScaleMap_t m_mapRentalPriceScales;
	store_promotion_spend_for_free_item_t m_StorePromotionSpendForFreeItem;
	CEconItemDefinition* m_pStorePromotionFirstTimePurchaseItem;
	CEconItemDefinition* m_pStorePromotionFirstTimeWebPurchaseItem;

#ifdef CLIENT_DLL
	FeaturedItems_t m_vecFeaturedItems;
#endif // CLIENT_DLL

#ifdef GC_DLL
	CUtlVector<econ_store_timed_sale_t> m_vecTimedSales;
#endif // GC_DLL

	// changes based on experiments
	uint32	m_unFeaturedItemIndex;
	eEconStoreSortType m_eEconStoreSortType;

	uint32 m_unPreviewPeriod;
	uint32 m_unBonusDiscountPeriod;
	float m_flPreviewPeriodDiscount;
	uint32 m_unHashForAllItems;

	// price point lookup
	CurrencyPricePointMap_t m_mapCurrencyPricePoints;
};

#ifdef CLIENT_DLL
void MakeMoneyString( wchar_t *pchDest, uint32 nDest, item_price_t unPrice, ECurrency eCurrencyCode );

bool ShouldUseNewStore();
int GetStoreVersion();
#endif // CLIENT_DLL

const CEconStorePriceSheet *GetEconPriceSheet();

#endif // ECON_STORE_H