#pragma once

#include "dllinterface.h"
#include "DeviceStateManager.h"

class BufferManager;

class DXFUNCTIONS_API Buffer : protected DXRessource
{
protected:
	friend BufferManager;
	friend ListElement<Buffer>;
	friend LList<Buffer>;

	POOL				m_Pool;
	unsigned			m_Usage;
	unsigned			m_Size;
	LPDIRECT3DRESOURCE9	m_pBuffer;
	BufferManager* const m_pManager;

	Buffer(BufferManager* const pManager, const unsigned size, const POOL pool, const unsigned usage);
	virtual ~Buffer(void){SAFERELEASE(m_pBuffer);}

	void DoInvalidate(void) { if(m_Pool == POOL_DEFAULT){SAFERELEASE(m_pBuffer);} }
	void DoRestore(void) { if(!m_pBuffer) Create(); }

	virtual void Create(void) {};

public:

	LPDIRECT3DRESOURCE9 GetBuffer(void) const {return m_pBuffer;}
	BufferManager* GetManager(void) const { return m_pManager;}

	unsigned GetSize(void) const { return m_Size; }
	unsigned GetUsage(void) const { return m_Usage; }
	
	void Release(void);
};

class VertexBuffer;
class IndexBuffer;
class DynamicVB;
class DynamicIB;

class DXFUNCTIONS_API BufferManager
{
	LList<Buffer>	m_lBuffers;

	DeviceStateManager* const m_Device;

protected:
	friend DeviceStateManager;

	BufferManager(DeviceStateManager* const device);
	~BufferManager(void);

	void Restore(void);
	void Invalidate(void);

public:
	VertexBuffer* CreateVB(const unsigned size, const unsigned fvf, const unsigned usage, const POOL pool);
	IndexBuffer* CreateIB(const unsigned size, const unsigned usage, const bool index32, const POOL pool);
	DynamicVB* CreateDynamicVB(const unsigned size);
	DynamicIB* CreateDynamicIB(const unsigned size, const bool index32);

	DeviceStateManager* GetDevice(void) const {return m_Device;}
	
	void Release(Buffer* const buffer);
};


class DXFUNCTIONS_API VertexBuffer : public Buffer
{
	unsigned m_FVF;

	void Create(void)
	{
		m_pManager->GetDevice()->GetDevice()->CreateVertexBuffer(m_Size, m_Usage, m_FVF, (D3DPOOL)m_Pool, (LPDIRECT3DVERTEXBUFFER9*)&m_pBuffer, NULL);
	}

protected:
	friend BufferManager;

	VertexBuffer(BufferManager* const pManager, const unsigned nSize, const unsigned fvf, const unsigned usage, const POOL pool);

public:
	LPDIRECT3DVERTEXBUFFER9 GetBuffer(void) const { return (LPDIRECT3DVERTEXBUFFER9)m_pBuffer; }

	LPVOID Lock(void) { void* pData; ((LPDIRECT3DVERTEXBUFFER9)m_pBuffer)->Lock(0, 0, &pData, 0); return (LPVOID)pData; }
	void Unlock(void) { ((LPDIRECT3DVERTEXBUFFER9)m_pBuffer)->Unlock(); }

	unsigned GetFVF(void) const { return m_FVF; }
};


class DXFUNCTIONS_API IndexBuffer : public Buffer
{
	bool m_Index32;

	void Create(void)
	{
		m_pManager->GetDevice()->GetDevice()->CreateIndexBuffer(m_Size, m_Usage, m_Index32 ? D3DFMT_INDEX32 : D3DFMT_INDEX16, (D3DPOOL)m_Pool, (LPDIRECT3DINDEXBUFFER9*)&m_pBuffer, NULL);
	}

protected:
	friend BufferManager;

	IndexBuffer(BufferManager* const pManager, const unsigned nSize, const unsigned usage, const bool index32, const POOL pool);
	//~IndexBuffer(void) { SAFERELEASE(m_pBuffer); }

public:
	LPDIRECT3DINDEXBUFFER9 GetBuffer(void) const { return (LPDIRECT3DINDEXBUFFER9)m_pBuffer;}

	bool IsIndex32(void) const { return m_Index32; }

	LPVOID Lock(void) { void* pData; ((LPDIRECT3DINDEXBUFFER9)m_pBuffer)->Lock(0, 0, &pData, 0); return (LPVOID)pData; }
	void Unlock(void) { ((LPDIRECT3DINDEXBUFFER9)m_pBuffer)->Unlock(); }
};

class DXFUNCTIONS_API DynamicVB : public Buffer
{
	DWORD					m_nVBUsed;
	DWORD					m_nVBOffset;
	 //for fast access
	LPDIRECT3DVERTEXBUFFER9 m_pVB;
	const LPDIRECT3DDEVICE9	m_pDevice;
 
	void Create(void)
	{
		m_pDevice->CreateVertexBuffer(m_Size, m_Usage, 0, (D3DPOOL)m_Pool, &m_pVB, NULL);
		m_pBuffer = m_pVB;
		m_nVBUsed = 0;
		m_nVBOffset = 0;
	}
	
protected:
	friend BufferManager;

	DynamicVB(BufferManager* const pManager, unsigned nSize);
	//~DynamicVB(void){ SAFERELEASE(m_pBuffer); }

public:

	unsigned LockVB(const unsigned Size);
	void UnlockVB(void) {m_pVB->Unlock();}
	void SetVBStream(const unsigned nStride, const unsigned nStream) {m_pDevice->SetStreamSource(nStream, m_pVB, m_nVBOffset, nStride);}
	unsigned GetStreamOffset(void) const {return m_nVBOffset;}
};

class DXFUNCTIONS_API DynamicIB : public Buffer
{
	bool					m_bIndex32;
	DWORD					m_nIBUsed;
	DWORD					m_nIBOffset;
	 //for fast access
	LPDIRECT3DINDEXBUFFER9 m_pIB;
	const LPDIRECT3DDEVICE9	m_pDevice;
 
	void Create(void)
	{
		m_pDevice->CreateIndexBuffer(m_Size, m_Usage, m_bIndex32 ? D3DFMT_INDEX32: D3DFMT_INDEX16, (D3DPOOL)m_Pool, &m_pIB, NULL);
		m_pBuffer = m_pIB;
		m_nIBUsed = 0;
		m_nIBOffset = 0;
	}
	
protected:
	friend BufferManager;

	DynamicIB(BufferManager* const pManager, const unsigned nSize, const bool bIndex32);
	//~DynamicVB(void){ SAFERELEASE(m_pBuffer); }

public:

	unsigned LockIB(const unsigned Size);
	void UnlockIB(void) {m_pIB->Unlock();}
	unsigned GetIndexOffset(void) const {return m_nIBOffset;}
	void SetIndices(void) { m_pDevice->SetIndices(m_pIB); }
};