//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//

#ifndef SHARED_OBJECT_MANAGER_H
#define SHARED_OBJECT_MANAGER_H

#include "GameEventListener.h"
#include "econ_item_constants.h"
#include "econ_item_inventory.h"

#ifdef GAME_DLL
	#include "tf_player.h"
#else
	#include "c_tf_player.h"
	#include "local_steam_shared_object_listener.h"
#endif


#if defined( _WIN32 )
#pragma once
#endif

using namespace GCSDK;
class CSOTrackerManager;

extern short g_nQuestSpewFlags;
#define SO_TRACKER_SPEW_OBJECTIVES 1<<0
#define SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT 1<<1
#define SO_TRACKER_SPEW_GC_COMMITS 1<<2
#define SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT 1<<3
#define SO_TRACKER_SPEW_SOCACHE_ACTIVITY 1<<4
#define SO_TRACKER_SPEW_TRACKER_ACCEPTANCE 1<<5
void SOTrackerSpew( const char* pszBuff, int nType );
#define SO_TRACKER_SPEW( pszBuff, nType ) SOTrackerSpew( pszBuff, nType );		

class CBaseSOTracker
{
public:
	DECLARE_CLASS_NOBASE( CBaseSOTracker )

	CBaseSOTracker( const CSharedObject* pSObject, CSteamID steamIDOwner, CSOTrackerManager* pManager );
	virtual ~CBaseSOTracker();

	const CSharedObject* GetSObject() const			{ return m_pSObject; }
	const CSteamID		 GetOwnerSteamID() const	{ return m_steamIDOwner; }
	const CTFPlayer*	 GetTrackedPlayer() const	{ return ToTFPlayer( GetPlayerBySteamID( m_steamIDOwner ) ); }
	virtual void Spew() const;

	virtual void		 CommitChangesToDB() = 0;
	virtual	void		 OnUpdate() = 0;
	virtual void		 OnRemove() = 0;
protected:

	const CSharedObject* m_pSObject;
	CSteamID m_steamIDOwner;
	CSOTrackerManager* m_pManager;
};

struct CommitRecord_t
{
	CommitRecord_t( ::google::protobuf::Message* pMessage ) 
		: m_flLastCommitTime( Plat_FloatTime() )
		, m_flReportedTime( Plat_FloatTime() )
		, m_pProtoMsg( pMessage )
	{}

	~CommitRecord_t()
	{
		if ( m_pProtoMsg )
			delete m_pProtoMsg;
	}

	double m_flLastCommitTime;
	double m_flReportedTime;
	::google::protobuf::Message* m_pProtoMsg;

private:
	CommitRecord_t(); // Nope
};
typedef CUtlMap< uint64, CommitRecord_t* > CommitsMap_t;

// A class to handle the creation and deletion of shared object trackers. Automatically
// subscribes to the local player's SOCache and will subscribe to any connecting players'
// SOCaches when they connect.
#ifdef GAME_DLL
class CSOTrackerManager : public ISharedObjectListener, public CGameEventListener, public CAutoGameSystemPerFrame
#else
class CSOTrackerManager : public CLocalSteamSharedObjectListener, public CGameEventListener, public CAutoGameSystemPerFrame
#endif
{
public:

	DECLARE_CLASS_NOBASE( CSOTrackerManager )

	typedef CUtlMap< uint64, CBaseSOTracker * > SOTrackerMap_t;

	CSOTrackerManager();
	virtual ~CSOTrackerManager();

	virtual void Initialize();
	virtual void Shutdown();

	virtual void FireGameEvent( IGameEvent *pEvent ) OVERRIDE;
	void EnsureTrackersForPlayer( const CSteamID& steamIDPlayer );
	void EnsureTrackersForPlayer( CTFPlayer* pPlayer );

	void SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
	void PreSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE {};
	void SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
	void PostSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE {};
	void SODestroyed( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
	void SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE;
	void SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE;

#ifdef GAME_DLL
	virtual void FrameUpdatePreEntityThink() OVERRIDE;
	void AddCommitRecord( const ::google::protobuf::Message* pRecord, uint64 nKey, bool bRequireResponse );
	void AcknowledgeCommit( const ::google::protobuf::Message* pRecord, uint64 nKey );
	void DBG_SpewPendingCommits();
#endif
	void Spew();


	virtual SOTrackerMap_t::KeyType_t GetKeyForObjectTracker( const CSharedObject* pItem, CSteamID steamIDOwner ) = 0;
	CBaseSOTracker* GetTracker( SOTrackerMap_t::KeyType_t nKey ) const;
	template< class T >
	T GetTypedTracker( SOTrackerMap_t::KeyType_t nKey ) const { return assert_cast< T >( GetTracker( nKey ) ); }
	CommitRecord_t* GetCommitRecord( CommitsMap_t::KeyType_t );
protected:

	enum ETrackerHandling_t
	{
		TRACKER_CREATE_OR_UPDATE = 0,
		TRACKER_REMOVE,
	};

	void UpdateTrackerForItem( const CSharedObject* pItem, ETrackerHandling_t eHandling, CSteamID steamIDOwner );

private:

	virtual int GetType() const = 0;
	virtual const char* GetName() const = 0;
	virtual CFmtStr GetDebugObjectDescription( const CSharedObject* pItem ) const = 0;
	virtual CBaseSOTracker* AllocateNewTracker( const CSharedObject* pItem, CSteamID steamIDOwner, CSOTrackerManager* pManager ) const = 0;
	virtual ::google::protobuf::Message* AllocateNewProtoMessage() const = 0;
	virtual void OnCommitRecieved( const ::google::protobuf::Message* pProtoMsg ) = 0;
	virtual bool ShouldTrackObject( const CSteamID & steamIDOwner, const CSharedObject *pObject ) const = 0;
	virtual int CompareRecords( const ::google::protobuf::Message* pNewProtoMsg, const ::google::protobuf::Message* pExistingProtoMsg ) const = 0;

	void HandleSOEvent( const CSteamID & steamIDOwner, const CSharedObject *pObject, ETrackerHandling_t eHandling );
	void CommitAllChanges();
	void CreateAndAddTracker( const CSharedObject* pItem, CSteamID steamIDOwner );
	void RemoveAndDeleteTrackerAtIndex( SOTrackerMap_t::IndexType_t idx );
	void RemoveTrackersForSteamID( const CSteamID & steamIDOwner );

#ifdef GAME_DLL
	void CommitRecord( CommitRecord_t* pRecord ) const;
	virtual void SendMessageForCommit( const ::google::protobuf::Message* pProtoMessage ) const = 0;
#endif

	double m_flLastUnacknowledgeCommitTime;
	CommitsMap_t m_mapUnacknowledgedCommits;
	SOTrackerMap_t m_mapItemTrackers;
};



#endif // SHARED_OBJECT_MANAGER_H