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

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

#include "tier0/threadtools.h"
#include "utlmultilist.h"
#include "utlvector.h"

FORWARD_DECLARE_HANDLE( memhandle_t );

#define INVALID_MEMHANDLE ((memhandle_t)0xffffffff)

class CDataManagerBase
{
public:

	// public API
	// -----------------------------------------------------------------------------
	// memhandle_t			CreateResource( params ) // implemented by derived class
	void					DestroyResource( memhandle_t handle );

	// type-safe implementation in derived class
	//void					*LockResource( memhandle_t handle );
	int						UnlockResource( memhandle_t handle );
	void					TouchResource( memhandle_t handle );
	void					MarkAsStale( memhandle_t handle );		// move to head of LRU

	int						LockCount( memhandle_t handle );
	int						BreakLock( memhandle_t handle );
	int						BreakAllLocks();

	// HACKHACK: For convenience - offers no lock protection 
	// type-safe implementation in derived class
	//void					*GetResource_NoLock( memhandle_t handle );

	unsigned int			TargetSize();
	unsigned int			AvailableSize();
	unsigned int			UsedSize();

	void					NotifySizeChanged( memhandle_t handle, unsigned int oldSize, unsigned int newSize );

	void					SetTargetSize( unsigned int targetSize );

	// NOTE: flush is equivalent to Destroy
	unsigned int			FlushAllUnlocked();
	unsigned int			FlushToTargetSize();
	unsigned int			FlushAll();
	unsigned int			Purge( unsigned int nBytesToPurge );
	unsigned int			EnsureCapacity( unsigned int size );

	// Thread lock
	virtual void			Lock() {}
	virtual bool			TryLock() { return true; }
	virtual void			Unlock() {}

	// Iteration

	// -----------------------------------------------------------------------------

	// Debugging only!!!!
	void					GetLRUHandleList( CUtlVector< memhandle_t >& list );
	void					GetLockHandleList( CUtlVector< memhandle_t >& list );


protected:
	// derived class must call these to implement public API
	unsigned short			CreateHandle( bool bCreateLocked );
	memhandle_t				StoreResourceInHandle( unsigned short memoryIndex, void *pStore, unsigned int realSize );
	void					*GetResource_NoLock( memhandle_t handle );
	void					*GetResource_NoLockNoLRUTouch( memhandle_t handle );
	void					*LockResource( memhandle_t handle );

	// NOTE: you must call this from the destructor of the derived class! (will assert otherwise)
	void					FreeAllLists()	{ FlushAll(); m_listsAreFreed = true; }

							CDataManagerBase( unsigned int maxSize );
	virtual					~CDataManagerBase();
	
	
	inline unsigned int		MemTotal_Inline() const { return m_targetMemorySize; }
	inline unsigned int		MemAvailable_Inline() const { return m_targetMemorySize - m_memUsed; }
	inline unsigned int		MemUsed_Inline() const { return m_memUsed; }

// Implemented by derived class:
	virtual void			DestroyResourceStorage( void * ) = 0;
	virtual unsigned int	GetRealSize( void * ) = 0;

	memhandle_t				ToHandle( unsigned short index );
	unsigned short			FromHandle( memhandle_t handle );
	
	void					TouchByIndex( unsigned short memoryIndex );
	void *					GetForFreeByIndex( unsigned short memoryIndex );

	// One of these is stored per active allocation
	struct resource_lru_element_t
	{
		resource_lru_element_t()
		{
			lockCount = 0;
			serial = 1;
			pStore = 0;
		}

		unsigned short lockCount;
		unsigned short serial;
		void	*pStore;
	};

	unsigned int m_targetMemorySize;
	unsigned int m_memUsed;
	
	CUtlMultiList< resource_lru_element_t, unsigned short >  m_memoryLists;
	
	unsigned short m_lruList;
	unsigned short m_lockList;
	unsigned short m_freeList;
	unsigned short m_listsAreFreed : 1;
	unsigned short m_unused : 15;

};

template< class STORAGE_TYPE, class CREATE_PARAMS, class LOCK_TYPE = STORAGE_TYPE *, class MUTEX_TYPE = CThreadNullMutex>
class CDataManager : public CDataManagerBase
{
	typedef CDataManagerBase BaseClass;
public:

	CDataManager<STORAGE_TYPE, CREATE_PARAMS, LOCK_TYPE, MUTEX_TYPE>( unsigned int size = (unsigned)-1 ) : BaseClass(size) {}
	

	~CDataManager<STORAGE_TYPE, CREATE_PARAMS, LOCK_TYPE, MUTEX_TYPE>()
	{
		// NOTE: This must be called in all implementations of CDataManager
		FreeAllLists();
	}

	// Use GetData() to translate pointer to LOCK_TYPE
	LOCK_TYPE LockResource( memhandle_t hMem )
	{
		void *pLock = BaseClass::LockResource( hMem );
		if ( pLock )
		{
			return StoragePointer(pLock)->GetData();
		}

		return NULL;
	}

	// Use GetData() to translate pointer to LOCK_TYPE
	LOCK_TYPE GetResource_NoLock( memhandle_t hMem )
	{
		void *pLock = const_cast<void *>(BaseClass::GetResource_NoLock( hMem ));
		if ( pLock )
		{
			return StoragePointer(pLock)->GetData();
		}
		return NULL;
	}

	// Use GetData() to translate pointer to LOCK_TYPE
	// Doesn't touch the memory LRU
	LOCK_TYPE GetResource_NoLockNoLRUTouch( memhandle_t hMem )
	{
		void *pLock = const_cast<void *>(BaseClass::GetResource_NoLockNoLRUTouch( hMem ));
		if ( pLock )
		{
			return StoragePointer(pLock)->GetData();
		}
		return NULL;
	}

	// Wrapper to match implementation of allocation with typed storage & alloc params.
	memhandle_t CreateResource( const CREATE_PARAMS &createParams, bool bCreateLocked = false )
	{
		BaseClass::EnsureCapacity(STORAGE_TYPE::EstimatedSize(createParams));
		unsigned short memoryIndex = BaseClass::CreateHandle( bCreateLocked );
		STORAGE_TYPE *pStore = STORAGE_TYPE::CreateResource( createParams );
		return BaseClass::StoreResourceInHandle( memoryIndex, pStore, pStore->Size() );
	}

	// Iteration. Must lock first
	memhandle_t GetFirstUnlocked()
	{
		unsigned node = m_memoryLists.Head(m_lruList);
		if ( node == m_memoryLists.InvalidIndex() )
		{
			return INVALID_MEMHANDLE;
		}
		return ToHandle( node );
	}

	memhandle_t GetFirstLocked()
	{
		unsigned node = m_memoryLists.Head(m_lockList);
		if ( node == m_memoryLists.InvalidIndex() )
		{
			return INVALID_MEMHANDLE;
		}
		return ToHandle( node );
	}

	memhandle_t GetNext( memhandle_t hPrev )
	{
		if ( hPrev == INVALID_MEMHANDLE )
		{
			return INVALID_MEMHANDLE;
		}

		unsigned short iNext = m_memoryLists.Next( FromHandle( hPrev ) );
		if ( iNext == m_memoryLists.InvalidIndex() ) 
		{
			return INVALID_MEMHANDLE;
		}

		return ToHandle( iNext );
	}

	MUTEX_TYPE &AccessMutex()	{ return m_mutex; }
	virtual void Lock() { m_mutex.Lock(); }
	virtual bool TryLock() { return m_mutex.TryLock(); }
	virtual void Unlock() { m_mutex.Unlock(); }

private:
	STORAGE_TYPE *StoragePointer( void *pMem )
	{
		return static_cast<STORAGE_TYPE *>(pMem);
	}

	virtual void DestroyResourceStorage( void *pStore )
	{
		StoragePointer(pStore)->DestroyResource();
	}
	
	virtual unsigned int GetRealSize( void *pStore )
	{
		return StoragePointer(pStore)->Size();
	}

	MUTEX_TYPE m_mutex;
};

//-----------------------------------------------------------------------------

inline unsigned short CDataManagerBase::FromHandle( memhandle_t handle )
{
	unsigned int fullWord = (unsigned int)handle;
	unsigned short serial = fullWord>>16;
	unsigned short index = fullWord & 0xFFFF;
	index--;
	if ( m_memoryLists.IsValidIndex(index) && m_memoryLists[index].serial == serial )
		return index;
	return m_memoryLists.InvalidIndex();
}

inline int CDataManagerBase::LockCount( memhandle_t handle )
{
	Lock();
	int result = 0;
	unsigned short memoryIndex = FromHandle(handle);
	if ( memoryIndex != m_memoryLists.InvalidIndex() )
	{
		result = m_memoryLists[memoryIndex].lockCount;
	}
	Unlock();
	return result;
}


#endif // RESOURCEMANAGER_H
