#pragma once

#include <map>
#include <list>

#include "Mix/Plugin/IReader.h"
#include "Mix/Plugin/Graphics/TextureLoader.h"

#include "Mix/IO/IManager.h"
#include "Mix/Graphics/IDevice.h"

#include "Mix/Private/Engine.h"
#include "Mix/Private/Graphics/Common/DeviceObject.h"

namespace Mix{ namespace Graphics{ namespace Common{

	class Device : public Mix::Graphics::IDevice
	{
		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Graphics::Common::Device
		////////////////////////////////////////////////////////////////////////////////////////////////////

	protected:
		struct TEXTURE_SUBRESOURCE_DATA
		{
			const void* pMem;
			unsigned int memSize;
			unsigned int memRowPitch;
			UInt32 memSlicePitch;
		};

		struct TEXTURE_DESC
		{
			Mix::Plugin::Graphics::ITextureKit::TYPE type;
			Mix::Graphics::FORMAT format;
			UInt32 width;
			UInt32 height;
			UInt32 depth;
			UInt32 mipLevels;
			UInt32 flags;
		};

	private:
		class TextureLoaderPlugin
		{
		private:
			typedef int ( __stdcall* _Load )( Mix::Plugin::IReader* pSrc, Mix::Plugin::Graphics::ITextureKit* pDst );

		private:
			HMODULE m_hModule;

		public:
			static TextureLoaderPlugin* CreateInstance( HMODULE hModule );
			void Destroy( void );

		private:
			TextureLoaderPlugin( HMODULE hModule );
			~TextureLoaderPlugin( void );

		public:
			int Load( Mix::Plugin::IReader* pSrc, Mix::Plugin::Graphics::ITextureKit* pDst );

		private:
			static const char* FN_LOAD;
		};

		class TextureKit : public Mix::Plugin::Graphics::ITextureKit
		{
		private:
			Device::TEXTURE_DESC m_Desc;
			Mix::STL::Vector<Mix::Memory::SECTION_GRAPHICS, Device::TEXTURE_SUBRESOURCE_DATA> m_SubResDatList;

			UInt8* m_pBuff;
			UInt32 m_BuffSize;
			UInt32 m_BuffPos;

		public:
			TextureKit( void );
			virtual ~TextureKit( void );

			void Reset( void );

			const Device::TEXTURE_DESC& GetDesc( void ) const;

			unsigned int GetSubResourceCount( void ) const;
			const Device::TEXTURE_SUBRESOURCE_DATA* GetSubResources( void ) const;

		public:
			virtual void SetType( ITextureKit::TYPE type );
			virtual void SetFormat( ITextureKit::FORMAT format );
			virtual void SetSize( unsigned int width, unsigned int height );
			virtual void SetDepth( unsigned int depth );
			virtual void SetMipLevels( unsigned int mipLevels );
			virtual void SetFlags( unsigned int flags );

			virtual void* AddSubResourceData( unsigned int memSize, unsigned int memRowPitch, unsigned int memSlicePitch );
			virtual void AddSubResourceData( const void* pMem, unsigned int memRowPitch, unsigned int memPitch, unsigned int memSlicePitch );
		};

		class TextureSource : public Mix::Plugin::IReader
		{
		private:
			const unsigned char* m_pMem;
			const unsigned char* m_pBegin;
			unsigned long long m_Pos;
			unsigned long long m_Size;

		public:
			TextureSource( const void* pMem, unsigned int memSize );

		public:
			virtual unsigned long long GetSize( void ) const;
			virtual unsigned long long GetPosition( void ) const;
			virtual unsigned int Read( void* pBuffer, unsigned int readSize );
			virtual const void* Read( unsigned int readSize, unsigned int& result );
			virtual unsigned long long Seek( Mix::Plugin::IReader::SEEK_METHOD method, unsigned long long distance );
		};

		static const wchar_t* FORMAT_TEXT_TABLE[Mix::Graphics::FMT_MAX];
		static unsigned int VERTEX_ELEMENT_SIZE_TABLE[Mix::Graphics::VERTEX_ELEMENT_FORMAT_MAX];

		typedef std::list<Device::TextureLoaderPlugin*> TextureLoaderPluginList;

		typedef std::map<Mix::String, Mix::Graphics::Common::DeviceObject*> DeviceObjectMap;
		typedef std::list<Mix::Graphics::Common::DeviceObject*> DeviceObjectList;

	private:
		TextureLoaderPluginList m_TexLoaderPluginList;
		DeviceObjectList m_List;

		TextureKit m_TexKit;

	protected:
		Mix::Engine* m_pEngine;
		Mix::IO::IManager* m_pFileMgr;
		Boolean m_bWaitVSync;

	protected:
		Device( Boolean bWaitVSync );
		virtual ~Device( void );

		Mix::Graphics::ITexture* CreateTextureFromPlugin( const wchar_t* pFilePath, const void* pSrc, UInt32 srcSize );

		virtual void OnDispose( void ) {}

		virtual Mix::Graphics::ITexture* OnCreateTexture(	const wchar_t* pFilePath,
															const Device::TEXTURE_DESC& desc,
															UInt32 subResourceCount,
															const Device::TEXTURE_SUBRESOURCE_DATA* subResources ) = 0;

		static UInt32 GetTextureDimension( const void* pSrc, UInt32 srcSize );
		static UInt32 GetVertexElementSize( UInt32 format );

	public:
		virtual Boolean Initialize( const Mix::Point& targetSize, Mix::Graphics::SHADER_MODEL shaderModel, Boolean bFullscreen, Mix::UserFile* pSysReport ) { return True; }
		virtual Boolean Update( void ) { return True; }
		virtual Boolean MessageProc( UInt32 msg, WPARAM wParam, LPARAM lParam ) { return True; }

		Boolean LoadPlugin( Mix::Plugin::TYPE type, HMODULE hModule );

		void Dispose( void );

		void AddDeviceObject( Mix::Graphics::Common::DeviceObject* pDeviceObject );
		void RemoveDeviceObject( Mix::Graphics::Common::DeviceObject* pDeviceObject );
		void InvalidateDeviceObject( void );
		void ValidateDeviceObject( void );

		static const wchar_t* GetFormatText( Mix::Graphics::FORMAT format );

	protected:
		static const wchar_t* FAILED_INITIALIZE;
		static const wchar_t* FAILED_CREATE_HULL_SHADER;
		static const wchar_t* FAILED_CREATE_DOMAIN_SHADER;
		static const wchar_t* FAILED_CREATE_GEOMETRY_SHADER;
		static const wchar_t* FAILED_CREATE_VERTEX_SHADER;
		static const wchar_t* FAILED_CREATE_PIXEL_SHADER;
		static const wchar_t* FAILED_CREATE_SHADER_CONSTANT;
		static const wchar_t* FAILED_CREATE_VERTEX_LAYOUT;
		static const wchar_t* FAILED_CREATE_VERTEX_BUFFER;
		static const wchar_t* FAILED_CREATE_INDEX_BUFFER;
		static const wchar_t* FAILED_CREATE_TEXTURE;
		static const wchar_t* FAILED_CREATE_TEXTURE_PLANE;
		static const wchar_t* FAILED_CREATE_TEXTURE_VOLUME;
		static const wchar_t* FAILED_CREATE_TEXTURE_CUBE;
		static const wchar_t* FAILED_CREATE_TEXTURE_DYNAMIC_PLANE;
		static const wchar_t* FAILED_CREATE_TEXTURE_TARGET_PLANE;
		static const wchar_t* FAILED_CREATE_TEXTURE_LOCKABLE_TARGET_PLANE;
		static const wchar_t* FAILED_CREATE_TEXTURE_TARGET_CUBE;
		static const wchar_t* FAILED_CREATE_TEXTURE_DEPTH;
		static const wchar_t* FAILED_CREATE_QUERY;
		static const wchar_t* FAILED_RESIZE_BACKBUFFER;
		static const wchar_t* FAILED_SET_TARGET;
		static const wchar_t* FAILED_SET_TEXTURE;
		static const wchar_t* FAILED_RESET_TEXTURE;
		static const wchar_t* FAILED_DRAW;
		static const wchar_t* FAILED_DRAW_INDEXED;
		static const wchar_t* FAILED_SAVE_SCREEN_SHOT;
		static const wchar_t* MSG_NOT_SET_DEVOBJ;
		static const wchar_t* MSG_MISSING_PT_VE;
		static const wchar_t* SAVE_SCREEN_SHOT;
	};

}}}
