#ifndef IDATACACHE_H
#define IDATACACHE_H

#ifdef _WIN32
#pragma once
#endif


#include "dbg.h"
#include "IAppSystem.h"

class IDataCache;

#define DATACACHE_INTERFACE_VERSION		"VDataCache003"

typedef uint32 DataCacheClientID_t;


FORWARD_DECLARE_HANDLE(memhandle_t);
typedef memhandle_t DataCacheHandle_t;
#define DC_INVALID_HANDLE ((DataCacheHandle_t)0)

struct DataCacheLimits_t
{
	DataCacheLimits_t(unsigned _nMaxBytes = (unsigned)-1, unsigned _nMaxItems = (unsigned)-1, unsigned _nMinBytes = 0, unsigned _nMinItems = 0)
		: nMaxBytes(_nMaxBytes),
		nMaxItems(_nMaxItems),
		nMinBytes(_nMinBytes),
		nMinItems(_nMinItems)
	{
	}

	unsigned nMaxBytes;
	unsigned nMaxItems;

	unsigned nMinBytes;
	unsigned nMinItems;
};

struct DataCacheStatus_t
{
	unsigned nBytes;
	unsigned nItems;

	unsigned nBytesLocked;
	unsigned nItemsLocked;

	unsigned nFindRequests;
	unsigned nFindHits;
};

enum DataCacheOptions_t
{
	DC_TRACE_ACTIVITY = (1 << 0),
	DC_FORCE_RELOCATE = (1 << 1),
	DC_ALWAYS_MISS = (1 << 2),
	DC_VALIDATE = (1 << 3),
};


enum DataCacheReportType_t
{
	DC_SUMMARY_REPORT,
	DC_DETAIL_REPORT,
	DC_DETAIL_REPORT_LRU,
};


enum DataCacheNotificationType_t
{
	DC_NONE,

	DC_AGE_DISCARD,

	DC_FLUSH_DISCARD,

	DC_REMOVED,

	DC_RELOCATE,

	DC_PRINT_INF0,
};

struct DataCacheNotification_t
{
	DataCacheNotificationType_t type;
	const char* pszSectionName;
	DataCacheClientID_t			clientId;
	const void* pItemData;
	unsigned					nItemSize;
};

const int DC_MAX_CLIENT_NAME = 15;
const int DC_MAX_ITEM_NAME = 511;

enum DataCacheRemoveResult_t
{
	DC_OK,
	DC_NOT_FOUND,
	DC_LOCKED,
};

enum DataCacheAddFlags_t
{
	DCAF_LOCK = (1 << 0),
	DCAF_DEFAULT = 0,
};



abstract_class IDataCacheSection
{
public:
	virtual IDataCache * GetSharedCache() = 0;
	virtual const char* GetName() = 0;

	virtual void SetLimits(const DataCacheLimits_t& limits) = 0;
	virtual void SetOptions(unsigned options) = 0;


	virtual void GetStatus(DataCacheStatus_t* pStatus, DataCacheLimits_t* pLimits = NULL) = 0;


	virtual void EnsureCapacity(unsigned nBytes, unsigned nItems = 1) = 0;


	virtual bool Add(DataCacheClientID_t clientId, const void* pItemData, unsigned size, DataCacheHandle_t* pHandle) = 0;

	virtual DataCacheHandle_t Find(DataCacheClientID_t clientId) = 0;


	virtual DataCacheRemoveResult_t Remove(DataCacheHandle_t handle, const void** ppItemData, unsigned* pItemSize = NULL, bool bNotify = false) = 0;
	DataCacheRemoveResult_t Remove(DataCacheHandle_t handle, bool bNotify = false) { return Remove(handle, NULL, NULL, bNotify); }


	virtual bool IsPresent(DataCacheHandle_t handle) = 0;


	virtual void* Lock(DataCacheHandle_t handle) = 0;


	virtual int Unlock(DataCacheHandle_t handle) = 0;


	virtual void* Get(DataCacheHandle_t handle, bool bFrameLock = false) = 0;
	virtual void* GetNoTouch(DataCacheHandle_t handle, bool bFrameLock = false) = 0;

	virtual int BeginFrameLocking() = 0;
	virtual bool IsFrameLocking() = 0;
	virtual void* FrameLock(DataCacheHandle_t handle) = 0;
	virtual int EndFrameLocking() = 0;
	virtual int* GetFrameUnlockCounterPtr() = 0;


	virtual int GetLockCount(DataCacheHandle_t handle) = 0;
	virtual int BreakLock(DataCacheHandle_t handle) = 0;


	virtual bool Touch(DataCacheHandle_t handle) = 0;


	virtual bool Age(DataCacheHandle_t handle) = 0;


	virtual unsigned Flush(bool bUnlockedOnly = true, bool bNotify = true) = 0;


	virtual unsigned Purge(unsigned nBytes) = 0;


	virtual void OutputReport(DataCacheReportType_t reportType = DC_SUMMARY_REPORT) = 0;

	virtual void UpdateSize(DataCacheHandle_t handle, unsigned int nNewSize) = 0;


	virtual void LockMutex() = 0;
	virtual void UnlockMutex() = 0;

	virtual bool AddEx(DataCacheClientID_t clientId, const void* pItemData, unsigned size, unsigned flags, DataCacheHandle_t* pHandle) = 0;
};


abstract_class IDataCacheClient
{
public:
	virtual bool HandleCacheNotification(const DataCacheNotification_t & notification) = 0;


	virtual bool GetItemName(DataCacheClientID_t clientId, const void* pItem, char* pDest, unsigned nMaxLen) = 0;
};

class CDefaultDataCacheClient : public IDataCacheClient
{
public:
	virtual bool HandleCacheNotification(const DataCacheNotification_t& notification)
	{
		switch (notification.type)
		{
		case DC_AGE_DISCARD:
		case DC_FLUSH_DISCARD:
		case DC_REMOVED:
		default:
			Assert(0);
			return false;
		}
		return false;
	}

	virtual bool GetItemName(DataCacheClientID_t clientId, const void* pItem, char* pDest, unsigned nMaxLen)
	{
		return false;
	}
};


abstract_class IDataCache : public IAppSystem
{
public:
	virtual void SetSize(int nMaxBytes) = 0;
	virtual void SetOptions(unsigned options) = 0;
	virtual void SetSectionLimits(const char* pszSectionName, const DataCacheLimits_t& limits) = 0;


	virtual void GetStatus(DataCacheStatus_t* pStatus, DataCacheLimits_t* pLimits = NULL) = 0;


	virtual IDataCacheSection* AddSection(IDataCacheClient* pClient, const char* pszSectionName, const DataCacheLimits_t& limits = DataCacheLimits_t(), bool bSupportFastFind = false) = 0;


	virtual void RemoveSection(const char* pszClientName, bool bCallFlush = true) = 0;
	void RemoveSection(IDataCacheSection* pSection, bool bCallFlush = true) { if (pSection) RemoveSection(pSection->GetName()); }


	virtual IDataCacheSection* FindSection(const char* pszClientName) = 0;


	virtual unsigned Purge(unsigned nBytes) = 0;


	virtual unsigned Flush(bool bUnlockedOnly = true, bool bNotify = true) = 0;


	virtual void OutputReport(DataCacheReportType_t reportType = DC_SUMMARY_REPORT, const char* pszSection = NULL) = 0;
};

template< class STORAGE_TYPE, class CREATE_PARAMS, class LOCK_TYPE = STORAGE_TYPE* >
class CManagedDataCacheClient : public CDefaultDataCacheClient
{
public:
	typedef CManagedDataCacheClient<STORAGE_TYPE, CREATE_PARAMS, LOCK_TYPE> CCacheClientBaseClass;

	CManagedDataCacheClient()
		: m_pCache(NULL)
	{
	}

	void Init(IDataCache* pSharedCache, const char* pszSectionName, const DataCacheLimits_t& limits = DataCacheLimits_t(), bool bSupportFastFind = false)
	{
		if (!m_pCache)
		{
			m_pCache = pSharedCache->AddSection(this, pszSectionName, limits, bSupportFastFind);
		}
	}

	void Shutdown()
	{
		if (m_pCache)
		{
			m_pCache->GetSharedCache()->RemoveSection(m_pCache);
			m_pCache = NULL;
		}
	}

	LOCK_TYPE CacheGet(DataCacheHandle_t handle, bool bFrameLock = true)
	{
		return (LOCK_TYPE)(((STORAGE_TYPE*)m_pCache->Get(handle, bFrameLock))->GetData());
	}

	LOCK_TYPE CacheGetNoTouch(DataCacheHandle_t handle)
	{
		return (LOCK_TYPE)(((STORAGE_TYPE*)m_pCache->GetNoTouch(handle))->GetData());
	}

	LOCK_TYPE CacheLock(DataCacheHandle_t handle)
	{
		return (LOCK_TYPE)(((STORAGE_TYPE*)m_pCache->Lock(handle))->GetData());
	}

	int CacheUnlock(DataCacheHandle_t handle)
	{
		return m_pCache->Unlock(handle);
	}

	void CacheTouch(DataCacheHandle_t handle)
	{
		m_pCache->Touch(handle);
	}

	void CacheRemove(DataCacheHandle_t handle, bool bNotify = true)
	{
		m_pCache->Remove(handle, bNotify);
	}

	void CacheFlush()
	{
		m_pCache->Flush();
	}

	DataCacheHandle_t CacheCreate(const CREATE_PARAMS& createParams, unsigned flags = DCAF_DEFAULT)
	{
		m_pCache->EnsureCapacity(STORAGE_TYPE::EstimatedSize(createParams));
		STORAGE_TYPE* pStore = STORAGE_TYPE::CreateResource(createParams);
		DataCacheHandle_t handle;
		m_pCache->AddEx((DataCacheClientID_t)pStore, pStore, pStore->Size(), flags, &handle);
		return handle;
	}

	void CacheLockMutex()
	{
		m_pCache->LockMutex();
	}

	void CacheUnlockMutex()
	{
		m_pCache->UnlockMutex();
	}

	bool HandleCacheNotification(const DataCacheNotification_t& notification)
	{
		switch (notification.type)
		{
		case DC_AGE_DISCARD:
		case DC_FLUSH_DISCARD:
		case DC_REMOVED:
		{
			STORAGE_TYPE* p = (STORAGE_TYPE*)notification.clientId;
			p->DestroyResource();
		}
		return true;
		default:
			return CDefaultDataCacheClient::HandleCacheNotification(notification);
		}
	}


protected:

	~CManagedDataCacheClient()
	{
		Shutdown();
	}

	IDataCacheSection* GetCacheSection()
	{
		return m_pCache;
	}

private:
	IDataCacheSection* m_pCache;

};

#endif  