#pragma once

#include "Mix/Reference.h"
#include "Mix/Graphics/Utility/IFont.h"
#include "Mix/Private/Container.h"

namespace Mix{ namespace Graphics{ namespace Utility{ namespace Common{

	class TextPrinter : public Mix::Reference
	{
	public:
		struct QUAD_GROUP
		{
			Mix::Graphics::ITexture* pTexture;

			UInt32 start;
			UInt32 count;
		};

		struct QUAD
		{
			Mix::Vector2 points[4];
			Mix::Vector2 texCoords[4];
		};

	private:
		enum VALUE
		{
			TEX_MARGIN = 2,
			TEX_MARGIN2 = ( TEX_MARGIN + TEX_MARGIN ),

			PAGE_DEFSIZE = 8,
			PAGE_RESIZESTEP = 2,
			PAGE_SECT_DEFSIZE = 8,
			PAGE_SECT_RESIZESTEP = 2,
			CHAR_DEFSIZE = 1024,
			CHAR_RESIZESTEP	= 64,

			QUAD_GROUP_DEFSIZE = 2,
			QUAD_GROUP_RESIZESTEP = 1,
			QUAD_DEFSIZE = 512,
			QUAD_RESIZESTEP = 32,

			SF_CHAR_DEFSIZE = 256,
			SF_CHAR_RESIZESTEP = 16,
			SF_LINE_DEFSIZE = 32,
			SF_LINE_RESIZESTEP = 4,
		};

		struct PAGE
		{
			Mix::Graphics::ITexture* pTexture;

			UInt32 sectStart;
			UInt32 sectCount;

			MIX_DECLARE_ALLOCATOR();
		};

		struct PAGE_SECTION
		{
			Mix::Graphics::Utility::IFont* pFont;

			UInt32 charStart;
			UInt32 charCount;

			MIX_DECLARE_ALLOCATOR();
		};

		struct CHARACTER
		{
			Mix::Point texPos;
			Mix::Graphics::Utility::IFont::GLYPH fontGlyph;

			MIX_DECLARE_ALLOCATOR();
		};

		struct SF_CHARACTER_INFO
		{
			Mix::Graphics::Utility::IFont::GLYPH fontGlyph;
			Mix::Rectangle bounds;
			Mix::Rectangle cellBounds;

			MIX_DECLARE_ALLOCATOR();
		};

		struct SF_LINE_INFO
		{
			UInt32 charStart;
			UInt32 charCount;

			MIX_DECLARE_ALLOCATOR();
		};

		typedef Mix::ContainerT<TextPrinter::SF_LINE_INFO, Mix::Memory::SECTION_GRAPHICS> SFLineInfoList;
		typedef Mix::ContainerT<TextPrinter::SF_CHARACTER_INFO, Mix::Memory::SECTION_GRAPHICS> SFCharInfoList;

	public:
		static TextPrinter* CreateInstance( Mix::Graphics::IDevice* pGraphicsDev, Int32 tabSize, Int32 pageSize, Int32 defPageNum );

	private:
		Mix::Graphics::IDevice* m_pDevice;

		Mix::ContainerT<TextPrinter::PAGE, Mix::Memory::SECTION_GRAPHICS> m_PageList;
		Mix::ContainerT<TextPrinter::PAGE_SECTION, Mix::Memory::SECTION_GRAPHICS> m_PageSectList;

		Mix::ContainerT<TextPrinter::QUAD_GROUP, Mix::Memory::SECTION_GRAPHICS> m_QuadGroupList;
		Mix::ContainerT<TextPrinter::QUAD, Mix::Memory::SECTION_GRAPHICS> m_QuadList;

		Mix::ContainerT<TextPrinter::CHARACTER, Mix::Memory::SECTION_GRAPHICS> m_CharList;

		Int32 m_TabSize;

		Int32 m_TexSize;
		Float32 m_TexSizeInv;
		Float32 m_TexOffset;

		Mix::Point m_TexPos;
		Int32 m_TexLine;

		UInt32 m_PageCount;

		UInt32 m_SFModifyCharCount;
		TextPrinter::SFCharInfoList m_SFCharInfoList;
		TextPrinter::SFLineInfoList m_SFLineInfoList;

	private:
		TextPrinter( Mix::Graphics::IDevice* pGraphicsDev, Int32 taSize, Int32 pageSize );
		virtual ~TextPrinter( void );

		Boolean Initialize( Int32 defPageNum );

		TextPrinter::PAGE* AddPage( Mix::Graphics::Utility::IFont* pFont );
		TextPrinter::PAGE* GetCurrentPage( void );

		TextPrinter::PAGE_SECTION* AddPageSection( Mix::Graphics::Utility::IFont* pFont );

		void AddImage(	TextPrinter::PAGE** ppPage,
						TextPrinter::PAGE_SECTION** ppPageSect,
						TextPrinter::QUAD_GROUP** ppQuadGroup,
						Int32 dx, Int32 dy, Int32 dw, Int32 dh,
						Mix::Graphics::Utility::IFont* pFont,
						const Mix::Graphics::Utility::IFont::GLYPH& fontGlyph );

		Mix::Graphics::ITexture* CreateTexture( void );

		void SF_AddChar( Int32 x, Int32 y, wchar_t code, const Mix::Graphics::Utility::IFont::GLYPH& glyph );
		void SF_AddLine( void );
		void SF_DoCenterH( Int32 width );
		void SF_DoCenterV( Int32 height );

	public:
		Mix::Point Measure( Mix::Graphics::Utility::IFont* pFont, const wchar_t* pStr ) const;

		Boolean Append( Mix::Graphics::Utility::IFont* pFont, const Mix::Point& pos, const wchar_t* pText );
		Boolean Append( Mix::Graphics::Utility::IFont* pFont, const Mix::Rectangle& rect, const wchar_t* pStr, UInt32 flags );

		const Mix::ContainerT<TextPrinter::QUAD_GROUP, Mix::Memory::SECTION_GRAPHICS>& GetQuadGroups( void ) const;
		const Mix::ContainerT<TextPrinter::QUAD, Mix::Memory::SECTION_GRAPHICS>& GetQuads( void ) const;

		void Execute( void );

		UInt32 GetTextureCount( void ) const;
		Mix::Graphics::ITexture* GetTexturePtr( UInt32 index ) const;

	private:
		static const wchar_t* FAILED_INIT;
		static const wchar_t* FAILED_PROC;

		static const Int32 MIN_TEX_SIZE;
		static const Int32 MIN_TEX_COUNT;
		static const Int32 MIN_TAB_SIZE;
	};

}}}}
