#pragma once

#include "BsD3D9Prerequisites.h"
#include "BsPixelBuffer.h"

namespace BansheeEngine 
{
	/**
	 * @brief	DirectX9 implementation of a pixel buffer. Represents a hardware buffer
	 *			containing a surface of pixels.
	 */
	class BS_D3D9_EXPORT D3D9PixelBuffer : public PixelBuffer
	{
	protected:
		/**
		 * @brief	Internal DX9 buffer resources container.
		 */
		struct BufferResources
		{
			IDirect3DSurface9* surface;
			IDirect3DVolume9* volume;
			IDirect3DSurface9* tempSurface;
			IDirect3DVolume9* tempVolume;
			IDirect3DBaseTexture9* mipTex;
		};

	public:
		D3D9PixelBuffer(GpuBufferUsage usage, D3D9TextureCore* ownerTexture);
		~D3D9PixelBuffer();

		/**
		 * @brief	Binds the specified surface object to this buffer. This needs to be called in order
		 *			to initialize the pixel buffer.
		 *
		 * @param	dev			Device the surface was created on.
		 * @param	surface		DirectX 9 object representing the surface.
		 * @param	mipTex		Base texture that will be used for generating mipmaps. Usually
		 *						the parent texture of the surface.
		 */
		void bind(IDirect3DDevice9* dev, IDirect3DSurface9* surface, IDirect3DBaseTexture9* mipTex);

		/**
		 * @brief	Binds the specified volume object to this buffer. This needs to be called in order
		 *			to initialize the pixel buffer.
		 *
		 * @param	dev			Device the volume was created on.
		 * @param	surface		DirectX 9 object representing the volume.
		 * @param	mipTex		Base texture that will be used for generating mipmaps. Usually
		 *						the parent texture of the volume.
		 */
		void bind(IDirect3DDevice9* dev, IDirect3DVolume9* volume, IDirect3DBaseTexture9* mipTex);

		/**
		 * @brief	Enables/disabled automatic mipmap generation on updates.
		 *
		 * @param	doMipmapGen	If true, mipmaps will be regenerated whenever data is written to the buffer.
		 * @param	HWMipmaps	If true the mipmaps will be generated by the hardware, otherwise software (slower).
		 */
		void setMipmapping(bool doMipmapGen, bool HWMipmaps);

		/**
		 * @brief	Returns internal DirectX 9 surface object for this buffer.
		 */
		IDirect3DSurface9* getSurface(IDirect3DDevice9* d3d9Device);

		/**
		 * @brief	Release all surface objects held by this buffer for the specified device.
		 */
		void releaseSurfaces(IDirect3DDevice9* d3d9Device);

		/**
		 * @brief	Destroy all resources associated with the specified device.
		 */
		void destroyBufferResources(IDirect3DDevice9* d3d9Device);

		/**
		 * @brief	Called when device state is changing. Access to any device should be locked.
		 */
		static void lockDeviceAccess();

		/**
		 * @brief	Called when device state change completed. Access to any device is allowed.
		 */
		static void unlockDeviceAccess();

		/**
		 * @brief	Initializes the provided pixel data buffer with information provided in the D3D9 locked rectangle.
		 */
		static void initPixelDataFromD3DLock(PixelData& pixelData, const D3DLOCKED_RECT& lrect);

		/**
		 * @brief	Initializes the provided pixel data buffer with information provided in the D3D9 locked box.
		 */
		static void initPixelDataFromD3DLock(PixelData& pixelData, const D3DLOCKED_BOX& lrect);

	protected:
		/**
		 * @copydoc	PixelBuffer::lockImpl
		 */
		PixelData lockImpl(PixelVolume lockBox, GpuLockOptions options);

		/**
		 * @copydoc	PixelBuffer::unlockImpl
		 */
		void unlockImpl();

		/**
		 * @brief	Locks the specified volume of the provided buffer objects and 
		 *			returns a data buffer that you may use to access it.
		 */
		PixelData lockBuffer(BufferResources* bufferResources, const PixelVolume& lockBox, DWORD flags);

		/**
		 * @brief	Unlocks the specified buffer objects.
		 */
		void unlockBuffer(BufferResources* bufferResources);

		/**
		 * @brief	Generates mip-map chain for the specified texture.
		 */
		void genMipmaps(IDirect3DBaseTexture9* mipTex);

		/**
		 * @brief	Retrieves buffer resources for the specified device, or null if they
		 *			do not exist.
		 */
		BufferResources* getBufferResources(IDirect3DDevice9* d3d9Device);

		/**
		 * @brief	Creates a new empty set of buffer resources.
		 */
		BufferResources* createBufferResources();

	protected:
		Map<IDirect3DDevice9*, BufferResources*> mMapDeviceToBufferResources;

		bool mDoMipmapGen;
		bool mHWMipmaps;

		D3D9TextureCore* mOwnerTexture;
		DWORD mLockFlags;
	};
};