#include "../UI/RuUI_Font.h"

#include <ft2build.h>
#include FT_FREETYPE_H

#define FONT_CANONICAL_SIZE					64
#define FONT_BITMAP_SIZE					128

#define FONT_TEXTURE_WIDTH					2048
#define FONT_TEXTURE_HEIGHT					1024
#define FONT_TEXTURE_UNIT_SIZE				32

#pragma managed(push, off)

// --------------------------------------------------------------------------------------
// static member value
// CRuFontType
bool								CRuFontType::s_useFreeType = false;
CRuArrayList<CRuFontType*>			CRuFontType::s_fontType;
vector<RuFontBase*>					CRuFontType::s_fontBase;

HDC									CRuFontType::s_hDC;
HFONT								CRuFontType::s_hCurrentFont;
HBITMAP								CRuFontType::s_hbmBitmap;
DWORD*								CRuFontType::s_pBitmapBits;
CRuImageFX_Resampler_32				CRuFontType::s_imageResampler;

void*								CRuFontType::s_fontLibrary = NULL;

// CRuFontTexture
CRuHashMap<INT64, CRuFontTexture*>	CRuFontTexture::s_fontTexIndex(2048);
CRuStack<CRuFontTexture*>			CRuFontTexture::s_freeFont;
CRuFontTexture*						CRuFontTexture::s_unitTexture;
IRuTexture*							CRuFontTexture::s_pRuTexture;
bool								CRuFontTexture::s_isLockRally;
RuFontTextureCallBack				CRuFontTexture::s_rallyFunc;
DWORD*								CRuFontTexture::s_pPixels;
CRuArrayList<INT64>					CRuFontTexture::s_removedFontTexList;
CRuFontTexture*						CRuFontTexture::s_blockTexture;

// CRuFontChar
CRuHashMap<INT64, CRuFontChar*>		CRuFontChar::s_fontChar(4096);

// --------------------------------------------------------------------------------------
CRuFontType::CRuFontType()
{
	m_fontBase		= NULL;
	m_serial		= 0;
	m_height		= 0;
	m_fontFace		= NULL;
}

// --------------------------------------------------------------------------------------
CRuFontType::~CRuFontType()
{
	if(m_fontFace)
	{
		FT_Done_Face(reinterpret_cast<FT_Face>(m_fontFace));
	}
}

// --------------------------------------------------------------------------------------
void CRuFontType::Initialize()
{
	if(s_useFreeType)
	{
		// Initialize FreeType
		INT32 error = FT_Init_FreeType(reinterpret_cast<FT_Library *>(&s_fontLibrary));

		if(error)
		{
			OutputDebugString("FreeType init error\n");
		}
	}
	else
	{
		s_hDC			= NULL;
		s_hbmBitmap		= NULL;
		s_hCurrentFont	= NULL;
		s_pBitmapBits	= NULL;

		// Prepare to create a bitmap
		BITMAPINFO bmi;
		ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
		bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth       =  FONT_BITMAP_SIZE;
		bmi.bmiHeader.biHeight      = -FONT_BITMAP_SIZE;
		bmi.bmiHeader.biPlanes      = 1;
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biBitCount    = 32;

		// Create generic device contexts
		s_hDC				= CreateCompatibleDC( NULL );

		// Create a bitmap for the target font size
		s_hbmBitmap			= CreateDIBSection( s_hDC, &bmi, DIB_RGB_COLORS, (VOID**) &s_pBitmapBits, NULL, 0 );	

		// Setup target DC mapping mode and select target font bitmap into it
		SetMapMode(   s_hDC, MM_TEXT );
		SelectObject( s_hDC, s_hbmBitmap );

		// Set text properties
		SetTextColor( s_hDC, RGB(255, 255, 255) );
		SetBkColor(   s_hDC, 0x00000000 );
		SetTextAlign( s_hDC, TA_TOP );	
	}
}

// --------------------------------------------------------------------------------------
void CRuFontType::Shutdown()
{
	// Delete all CRuFontType objects
	for(INT32 i = 0; i < s_fontType.Count(); ++i)
	{
		delete s_fontType[i];
	}
	s_fontType.Clear();

	// Delete all RuFontBase structures
	for(vector<RuFontBase*>::iterator iter = s_fontBase.begin(); iter != s_fontBase.end(); iter++)
	{
		RuFontBase *fontBase = *iter;

		if ( fontBase->hFont )
		{
			DeleteObject(fontBase->hFont);
			fontBase->hFont = NULL;
		}

		ruSAFE_DELETE_ARRAY(fontBase->fontStr);
		ruSAFE_DELETE(fontBase->m_fontStream);
		ruSAFE_DELETE(fontBase);
	}
	s_fontBase.clear();

	// Release GDI objects
	s_hCurrentFont	= NULL;
	s_pBitmapBits	= NULL;

	if ( s_hbmBitmap )
	{
		DeleteObject( s_hbmBitmap );
		s_hbmBitmap = NULL;
	}	

	if ( s_hDC )
	{
		DeleteDC( s_hDC );
		s_hDC = NULL;
	}

	// Shutdown FreeType
	if ( s_fontLibrary )
	{
		FT_Done_FreeType(reinterpret_cast<FT_Library>(s_fontLibrary));
		s_fontLibrary = NULL;
	}
}

// --------------------------------------------------------------------------------------
int CALLBACK EnumFontProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, DWORD FontType, LPARAM lParam )
{
	int a = 0;
	return 0;
}

// --------------------------------------------------------------------------------------
CRuFontType* CRuFontType::GetFontType(const char *fontNameStr, int height, RuFontWeight weight, RuFontOutline outline)
{
	static int lastSerial = 1;

	// Filter out invalid fonts
	if (fontNameStr == NULL || height < 6)
		return NULL;

	// Search for existing, matching font type
	for(INT32 i = 0; i < s_fontType.Count(); ++i)
	{
		RuFontBase *fontBase = s_fontType[i]->m_fontBase;
		if ( s_fontType[i]->m_height == height && stricmp(fontBase->fontStr, fontNameStr) == 0 && fontBase->outline == outline && fontBase->weight == weight )
		{
			return s_fontType[i];
		}
	}

	// Find an existing FontBase structure with the specified font name
	RuFontBase *fontBase = NULL;
	for(vector<RuFontBase*>::iterator iter = s_fontBase.begin(); iter != s_fontBase.end(); iter++)
	{
		RuFontBase* baseTmp = *iter;
		if ( stricmp(baseTmp->fontStr, fontNameStr) == 0 && baseTmp->weight == weight && baseTmp->outline == outline )
		{
			fontBase = baseTmp;
			break;
		}
	}	

	if(fontBase == NULL)
	{
		// Create new RuFontBase structure	
		fontBase					= ruNEW RuFontBase;
		fontBase->index				= s_fontBase.size();
		fontBase->fontStr			= ruNEW char[strlen(fontNameStr) + 1];
		strcpy(fontBase->fontStr, fontNameStr);
		fontBase->outline			= outline;
		fontBase->weight			= weight;
		fontBase->hFont				= NULL;
		fontBase->m_fontStream		= NULL;

		if(s_useFreeType)
		{
			// Freetype2
			IRuStream *fileStream = g_ruResourceManager->LoadStream(fontNameStr);

			if(fileStream == NULL)
			{
				fileStream = g_ruResourceManager->LoadStream("fonts\\dfheimdu.ttf");
			}

			if(fileStream)
			{
				fontBase->m_fontStream = ruNEW CRuMemoryStream();
				fontBase->m_fontStream->SetBufferSize(fileStream->GetStreamSize());
				*(fontBase->m_fontStream) = *fileStream;
				ruSAFE_DELETE(fileStream);
			}
			else
			{
				fontBase->m_fontStream = NULL;
			}
		}
		else
		{
			DWORD fw;
			switch ( weight )
			{
				case ruFONTWEIGHT_THIN:
					fw = FW_EXTRALIGHT;
					break;
				case ruFONTWEIGHT_NORMAL:
					fw = FW_SEMIBOLD;
					break;
				case ruFONTWEIGHT_BOLD:
					fw = FW_HEAVY;
					break;
			}

			wchar_t* wfontName = MultiByteToWChar(fontNameStr);
			
			// Create New Font
			int nHeight = FONT_CANONICAL_SIZE;

			fontBase->hFont = CreateFontW(nHeight, 0, 0, 0, fw, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, wfontName );
			ruSAFE_DELETE_ARRAY(wfontName);

			if ( fontBase->hFont == NULL )
			{
				ruSAFE_DELETE(fontBase);
				return NULL;
			}

			s_hCurrentFont = fontBase->hFont;
			SelectObject(s_hDC, s_hCurrentFont);

			TEXTMETRIC textMetric;
			GetTextMetrics(s_hDC, &textMetric);
		}

		s_fontBase.push_back(fontBase);
	}

	// Create CRuFontType object
	CRuFontType *fontType	= ruNEW CRuFontType;
	fontType->m_height		= height;
	fontType->m_fontBase	= fontBase;
	fontType->m_serial		= lastSerial++;

	if(s_useFreeType)
	{
		if(fontBase->m_fontStream)
		{
			// Create new font face based on the input font stream and the given font height
			FT_New_Memory_Face(reinterpret_cast<FT_Library>(s_fontLibrary), fontBase->m_fontStream->GetBuffer(), fontBase->m_fontStream->GetStreamSize(), 0, reinterpret_cast<FT_Face *>(&fontType->m_fontFace));
		}

		// Set transform based on weight
		switch(weight)
		{
			case ruFONTWEIGHT_THIN:
				{
	/*				FT_Matrix fontTransform;
					fontTransform.xx = (FT_Fixed) (0.95f * 0x10000L);
					fontTransform.xy = (FT_Fixed) (0.0f * 0x10000L);
					fontTransform.yx = (FT_Fixed) (0.0f * 0x10000L);
					fontTransform.yy = (FT_Fixed) (1.0f * 0x10000L);
					FT_Set_Transform(reinterpret_cast<FT_Face>(fontType->m_fontFace), &fontTransform, NULL);*/
				}

				break;

			case ruFONTWEIGHT_BOLD:
				{
	/*				FT_Matrix fontTransform;
					fontTransform.xx = (FT_Fixed) (2.5f * 0x10000L);
					fontTransform.xy = (FT_Fixed) (0.0f * 0x10000L);
					fontTransform.yx = (FT_Fixed) (0.0f * 0x10000L);
					fontTransform.yy = (FT_Fixed) (1.0f * 0x10000L);
					FT_Set_Transform(reinterpret_cast<FT_Face>(fontType->m_fontFace), &fontTransform, NULL);*/
				}

				break;

			default:
			case ruFONTWEIGHT_NORMAL:
				// Do nothing
				break;
		}
	}

	// Add font type to list of font types
	s_fontType.Add(fontType);

	return fontType;
}

// --------------------------------------------------------------------------------------
void CRuFontType::PaintToMem(const WCHAR wchar, const WCHAR* toneSymbol, IRuTexture *dstTexture, INT32 x, INT32 y, SIZE &size)
{
	if(s_useFreeType)
	{
		FT_Face fontFace = reinterpret_cast<FT_Face>(m_fontFace);

		INT32 levels = dstTexture->GetLevelCount();

		for(INT32 curLevel = 0, levelUnitSize = FONT_TEXTURE_UNIT_SIZE; curLevel < levels; ++curLevel, levelUnitSize = levelUnitSize >> 1)
		{
			levelUnitSize -= 1;

			// Find a pixel size that will fit
			INT32 error;
			INT32 deduction = 0;
			INT32 height;
			do 
			{
				error = FT_Set_Pixel_Sizes(reinterpret_cast<FT_Face>(m_fontFace), 0, levelUnitSize - deduction);

				height = (fontFace->size->metrics.height >> 6);

				++deduction;
			} while (height > levelUnitSize);


			// Get character glyph
			INT32 glyphIdx = FT_Get_Char_Index(reinterpret_cast<FT_Face>(m_fontFace), wchar);

			// Load and render glyph
			error = FT_Load_Glyph(reinterpret_cast<FT_Face>(m_fontFace), glyphIdx, FT_LOAD_TARGET_LIGHT);
			error = FT_Render_Glyph(reinterpret_cast<FT_Face>(m_fontFace)->glyph, FT_RENDER_MODE_LIGHT);

			// Shortcuts
			FT_GlyphSlot glyphSlot = reinterpret_cast<FT_Face>(m_fontFace)->glyph;
			FT_Bitmap glyphBitmap = reinterpret_cast<FT_Face>(m_fontFace)->glyph->bitmap;

			if(curLevel == 0)
			{
				// Font size is the advance of the glyph as width and the entire height of the bitmap window as height
				size.cx = glyphSlot->advance.x >> 6;
				size.cy = levelUnitSize;
			}

			BYTE *dstBits, *srcBits;
			INT32 dstPitch, srcPitch;

			dstTexture->Lock(curLevel, (void **) &dstBits, &dstPitch);
			srcBits = glyphBitmap.buffer;
			srcPitch = glyphBitmap.pitch;

			dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + (x >> curLevel) * sizeof(BYTE) + (y >> curLevel) * dstPitch);

			// Clear the entire field
			BYTE *orgDstBits = dstBits;
			for ( int h = 0; h < levelUnitSize; h++ )
			{
				for (int w = 0; w < levelUnitSize; w++)
				{
					dstBits[w] = 0x00;
				}

				dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + dstPitch);
			}

			dstBits = orgDstBits;

			// Determine height placement - ascender minus horizontal bearing is the number of empty rows at the top of the glyph
			int rowsToSkip = (glyphSlot->face->size->metrics.ascender - glyphSlot->metrics.horiBearingY) >> 6;

			// Offset destination pointer so we start at the correct pixel
			if((glyphSlot->bitmap_left + glyphBitmap.width) > (glyphSlot->advance.x >> 6))
			{
				// This detects situations where the font is passing back bad metrics... where the advance is smaller than the damn glyph
				dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(orgDstBits) + dstPitch * rowsToSkip);
			}
			else
			{
				dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(orgDstBits) + dstPitch * rowsToSkip + glyphSlot->bitmap_left);
			}

			// Copy the glyph
			for(int h = 0; h < glyphBitmap.rows; ++h)
			{
				for(int w = 0; w < glyphBitmap.width; ++w)
				{
					dstBits[w] = srcBits[w];
				}

				srcBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(srcBits) + srcPitch);
				dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + dstPitch);
			}

			if(m_fontBase->weight == ruFONTWEIGHT_BOLD)
			{
				// Offset destination pointer so we start at the correct pixel
				if((glyphSlot->bitmap_left + glyphBitmap.width) > (glyphSlot->advance.x >> 6))
				{
					// This detects situations where the font is passing back bad metrics... where the advance is smaller than the damn glyph
					dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(orgDstBits) + dstPitch * rowsToSkip + 1);
				}
				else
				{
					dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(orgDstBits) + dstPitch * rowsToSkip + glyphSlot->bitmap_left + 1);
				}

				srcBits = glyphBitmap.buffer;

				// Copy the glyph
				for(int h = 0; h < glyphBitmap.rows; ++h)
				{
					for(int w = 0; w < glyphBitmap.width; ++w)
					{
						dstBits[w] = min(255, dstBits[w] + srcBits[w]);
					}

					srcBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(srcBits) + srcPitch);
					dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + dstPitch);
				}
			}

			dstTexture->Unlock(curLevel);

			levelUnitSize += 1;
		}
	}
	else
	{
		static DWORD outTemp[16384];
		assert(s_hDC);

		if ( s_hCurrentFont != m_fontBase->hFont )
		{
			s_hCurrentFont = m_fontBase->hFont;
			SelectObject( s_hDC, s_hCurrentFont );
		}

		//
		memset(s_pBitmapBits, 0, sizeof(DWORD)*16384);

		SIZE srcSize;
		BOOL getTextExtentResult;

		if ( toneSymbol == NULL )
		{
			// Output source font to DC
			if ( !ExtTextOutW(s_hDC, 0, 0, ETO_OPAQUE, NULL, &wchar, 1, NULL) )
				return;

			// Get text extents for target font
			getTextExtentResult = GetTextExtentPoint32W(s_hDC, &wchar, 1, &srcSize);			
		}
		else
		{
			WCHAR buf[256];
			swprintf_s(buf, 256, L"%c%s", wchar, toneSymbol);

			int length = wcslen(buf);

			// Output source font to DC
			if ( !ExtTextOutW(s_hDC, 0, 0, ETO_OPAQUE, NULL, buf, length, NULL) )
				return;

			// Get text extents for target font
			getTextExtentResult = GetTextExtentPoint32W(s_hDC, buf, length, &srcSize);
		}

		assert(getTextExtentResult);

		// 檢查繪出字型大小
		if ( srcSize.cx < 0 || srcSize.cx > 128 || srcSize.cy < 0 || srcSize.cy > 128 )
			return;

		if ( srcSize.cx > srcSize.cy )
		{
			size.cx = FONT_TEXTURE_UNIT_SIZE - 1;
			size.cy = size.cx * srcSize.cy / srcSize.cx;
		}
		else
		{
			size.cy = FONT_TEXTURE_UNIT_SIZE - 1;
			size.cx = size.cy * srcSize.cx / srcSize.cy;
		}

		INT32 levels = dstTexture->GetLevelCount();

		for(INT32 curLevel = 0, levelUnitSize = FONT_TEXTURE_UNIT_SIZE; curLevel < levels; ++curLevel, levelUnitSize = levelUnitSize >> 1)
		{
			levelUnitSize -= 1;

			// Clear output buffer
			memset(outTemp, 0, sizeof(DWORD) * 16384);
			
			// Re-sample source font into target font
			s_imageResampler.ResampleImage(s_pBitmapBits, srcSize.cx, srcSize.cy, FONT_BITMAP_SIZE, outTemp, size.cx >> curLevel, size.cy >> curLevel, levelUnitSize);

			BYTE *dstBits;
			INT32 dstPitch;
			dstTexture->Lock(curLevel, (void **) &dstBits, &dstPitch);

			dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + (x >> curLevel) * sizeof(BYTE) + (y >> curLevel) * dstPitch);

			DWORD *pSrc = outTemp;
			for ( int h = 0; h < levelUnitSize ; h++ )
			{
				for (int w = 0; w < levelUnitSize; w++)
				{
					if (pSrc[w] & 0xFF)
						dstBits[w] = static_cast<BYTE>(pSrc[w] & 0xFF);
					else
						dstBits[w] = 0;
				}

				pSrc = reinterpret_cast<DWORD *>(reinterpret_cast<BYTE *>(pSrc) + levelUnitSize * sizeof(DWORD));
				dstBits = reinterpret_cast<BYTE *>(reinterpret_cast<BYTE *>(dstBits) + dstPitch);
			}

			dstTexture->Unlock(curLevel);

			levelUnitSize += 1;
		}
	}
}

// --------------------------------------------------------------------------------------
void CRuFontType::GetFontSize(WCHAR wchar, WCHAR* toneSymbol, SIZE& outSize)
{
	if(s_useFreeType)
	{
		FT_Face fontFace = reinterpret_cast<FT_Face>(m_fontFace);

		// Find a pixel size that will fit
		INT32 levelUnitSize = FONT_TEXTURE_UNIT_SIZE - 1;
		INT32 error;
		INT32 deduction = 0;
		INT32 height;
		do 
		{
			error = FT_Set_Pixel_Sizes(reinterpret_cast<FT_Face>(m_fontFace), 0, levelUnitSize - deduction);

			height = (fontFace->size->metrics.height >> 6);

			++deduction;
		} while (height > levelUnitSize);

		// Get character glyph
		INT32 glyphIdx = FT_Get_Char_Index(reinterpret_cast<FT_Face>(m_fontFace), wchar);

		// Load and render glyph
		error = FT_Load_Glyph(reinterpret_cast<FT_Face>(m_fontFace), glyphIdx, FT_LOAD_DEFAULT);
		error = FT_Render_Glyph(reinterpret_cast<FT_Face>(m_fontFace)->glyph, FT_RENDER_MODE_NORMAL);

		// Shortcuts
		FT_GlyphSlot glyphSlot = reinterpret_cast<FT_Face>(m_fontFace)->glyph;
		FT_Bitmap glyphBitmap = reinterpret_cast<FT_Face>(m_fontFace)->glyph->bitmap;

		// Font size is the advance of the glyph as width and the entire height of the bitmap window as height
		outSize.cx = glyphSlot->advance.x >> 6;
		outSize.cy = levelUnitSize;
	}
	else
	{
		if ( s_hCurrentFont != m_fontBase->hFont )
		{
			s_hCurrentFont = m_fontBase->hFont;
			SelectObject(s_hDC, s_hCurrentFont);
		}

		if ( toneSymbol == NULL )
		{
			// Get text extents for target font
			GetTextExtentPoint32W(s_hDC, &wchar, 1, &outSize);
		}
		else
		{
			WCHAR buf[256];
			swprintf_s(buf, 256, L"%c%s", wchar, toneSymbol);
			GetTextExtentPoint32W(s_hDC, buf, wcslen(buf), &outSize);
		}		
	}
}

// --------------------------------------------------------------------------------------
CRuFontTexture::CRuFontTexture()
{
	m_rect.Set(0.0f, 0.0f, 0.0f, 0.0f);

	m_toneSymbol = NULL;
}

// --------------------------------------------------------------------------------------
CRuFontTexture::~CRuFontTexture()
{
	if ( m_toneSymbol )
	{
		delete [] m_toneSymbol;
		m_toneSymbol = NULL;
	}
}

// --------------------------------------------------------------------------------------
bool CRuFontTexture::Initialize( RuFontTextureCallBack func )
{
	int size, row, col;
	s_fontTexIndex.Clear();	
	s_freeFont.Clear();
	s_isLockRally	= false;
	s_pRuTexture	= NULL;
	s_rallyFunc		= NULL;

	if ( (s_pRuTexture = g_ruUIRenderer->GetReservedDXT5Texture()) == NULL  )
		return false;
		
	// 計算貼圖總存放字元個數
	row = FONT_TEXTURE_HEIGHT / FONT_TEXTURE_UNIT_SIZE;
	col	= FONT_TEXTURE_WIDTH / FONT_TEXTURE_UNIT_SIZE;
	size = row * col;
	s_unitTexture = ruNEW CRuFontTexture[size];
	CRuFontTexture* fontTexture;
	for ( int i = 0; i < size; i++ )
	{
		fontTexture = s_unitTexture + i;
		fontTexture->m_rect.m_left		= (float)((i % col) * FONT_TEXTURE_UNIT_SIZE);
		fontTexture->m_rect.m_top		= (float)((i / col) * FONT_TEXTURE_UNIT_SIZE);
		fontTexture->m_rect.m_right		= fontTexture->m_rect.m_left;
		fontTexture->m_rect.m_bottom	= fontTexture->m_rect.m_top;

		// 第一個區塊特殊用途,當文字背景區塊用
		if ( i == 0 )
		{
			// 填成純白資料
			BYTE *pRow = NULL;
			INT32 pitch;
			INT32 levels = s_pRuTexture->GetLevelCount();
			for(INT32 curLevel = 0, levelUnitSize = FONT_TEXTURE_UNIT_SIZE; curLevel < levels; ++curLevel, levelUnitSize = levelUnitSize >> 1)
			{
				if ( s_pRuTexture->Lock(curLevel, (void**)&pRow, &pitch) )
				{
					for ( int h = 0; h < levelUnitSize; h++ )
					{
						memset(pRow, 0xFF, sizeof(BYTE) * levelUnitSize);
						pRow += pitch;
					}
					s_pRuTexture->Unlock(curLevel);
				}
			}

			fontTexture->m_rect.m_right		+= FONT_TEXTURE_UNIT_SIZE;
			fontTexture->m_rect.m_bottom	+= FONT_TEXTURE_UNIT_SIZE;
			s_blockTexture = fontTexture;
		}
		else
		{
			s_freeFont.Push(fontTexture);
		}
	}

	s_pPixels = ruNEW DWORD[FONT_TEXTURE_UNIT_SIZE * FONT_TEXTURE_UNIT_SIZE * 2];
	s_rallyFunc = func;
	return true;
}

// --------------------------------------------------------------------------------------
void CRuFontTexture::Shutdown()
{
	ReleaseAllFontIndex();

	s_rallyFunc = NULL;
	s_fontTexIndex.Clear();	
	s_freeFont.Clear();
	
	if ( s_unitTexture )
	{
		delete [] s_unitTexture;
		s_unitTexture = NULL;
	}

	if ( s_pPixels )
	{
		delete[] s_pPixels;
		s_pPixels = NULL;
	}
}

// --------------------------------------------------------------------------------------
void CRuFontTexture::ReleaseAllFontIndex()
{
	Rally(true);
}

// --------------------------------------------------------------------------------------
CRuFontTexture*	CRuFontTexture::GetRuFontTexture(CRuFontType* fontType, WCHAR wchar, WCHAR* toneSymbol)
{
	CRuFontTexture* fontTexture = NULL;
	if ( fontType && wchar >= 0x20 )
	{
		INT64 toneSymbolNumber = CRuFontChar::GetToneSymbolNumber(toneSymbol);
		INT64 key = toneSymbolNumber << 32 | (fontType->GetFontBaseIndex() & 0xFFFF) << 16 | wchar;
		DWORD keyHash = RuGlobalHashGen().GetHashCode(key);		
		if ( !s_fontTexIndex.Get(key, keyHash, fontTexture) )
		{
			fontTexture = GetFreeFontTexture();
			if ( fontTexture )
			{
				// Paint character to texture
				SIZE fontSize;
				fontType->PaintToMem(wchar, toneSymbol, s_pRuTexture, static_cast<INT32>(fontTexture->m_rect.m_left), static_cast<INT32>(fontTexture->m_rect.m_top), fontSize);

				// 設定聲調符號
				fontTexture->SetToneSymbol(toneSymbol);

				// Set texture size according to the size structure returned by PaintToMem
				fontTexture->m_rect.m_right = fontTexture->m_rect.m_left + fontSize.cx;
				fontTexture->m_rect.m_bottom = fontTexture->m_rect.m_top + fontSize.cy;

				// 設定資料已被取用
				s_fontTexIndex.Add(key, keyHash, fontTexture);
			}
		}
	}

	return fontTexture;
}

// --------------------------------------------------------------------------------------
void CRuFontTexture::PaintBegin()
{		
	g_ruUIRenderer->SetTexture( s_pRuTexture, FALSE );	
	g_ruUIRenderer->SetEffectType( ru2DFXTYPE_TEXTBLIT_FILTER );
	g_ruUIRenderer->SetEffectParameter( ru2DFXPARAM_ROTATE, ru2DROTATE_NONE );
}

// --------------------------------------------------------------------------------------
void CRuFontTexture::Clear()
{
	if ( m_toneSymbol )
	{
		delete [] m_toneSymbol;
		m_toneSymbol = NULL;
	}
}

// --------------------------------------------------------------------------------------
void CRuFontTexture::SetToneSymbol(const WCHAR* toneSymbol)
{
	if ( m_toneSymbol )
	{
		delete [] m_toneSymbol;
		m_toneSymbol = NULL;
	}

	if ( toneSymbol )
	{
		m_toneSymbol = ruNEW WCHAR[wcslen(toneSymbol) + 1];
		wcscpy(m_toneSymbol, toneSymbol);
	}
}

// --------------------------------------------------------------------------------------
CRuFontTexture* CRuFontTexture::GetFreeFontTexture()
{	
	if ( s_freeFont.IsEmpty() )
		Rally();

	if ( s_freeFont.IsEmpty() == FALSE )
		return s_freeFont.Pop();

	return NULL;
}

// --------------------------------------------------------------------------------------
bool CRuFontTexture::Recycle( CRuFontTexture* pTexture )
{
	/*
	if ( pTexture->m_pFontChar )
	{
		pTexture->m_pFontChar->m_pFontTex = NULL;
		pTexture->m_pFontChar = NULL;
	}
	*/

	if ( pTexture == NULL || pTexture == s_blockTexture )
		return false;	

	pTexture->Clear();

	s_freeFont.Push(pTexture);
	return true;
}

// --------------------------------------------------------------------------------------
// 清除元索引值
void CRuFontTexture::Rally(bool removeAll)
{
	if ( !s_isLockRally )
	{
		s_isLockRally = true;

		int val = rand() % 3;
		if ( removeAll )
			val = -1;

		s_removedFontTexList.Clear();

		s_fontTexIndex.ForAllElements(RallyCallback, reinterpret_cast<void *>(&val));

		for(INT32 i = 0; i < s_removedFontTexList.Count(); ++i)
		{
			INT64 key = s_removedFontTexList[i];
			DWORD keyHash = RuGlobalHashGen().GetHashCode(key);
			s_fontTexIndex.Remove(key, keyHash);
		}

		if ( s_rallyFunc )
			s_rallyFunc();

		s_isLockRally = false;
	}
}

// --------------------------------------------------------------------------------------
BOOL CRuFontTexture::RallyCallback(const void *key, const void *data, void *userData)
{
	CRuFontTexture *fontTexture = *((CRuFontTexture **) data);

	int &val = *reinterpret_cast<int *>(userData);

	// 移除全部索引值
	if ( val < 0 )
	{
		if ( Recycle( fontTexture ) )
			s_removedFontTexList.Add(*((INT64 *) key));
	}
	// 釋放1/3索引值
	else if ( val > 1 )
	{
		val = 0;
		if ( Recycle( fontTexture ) )
			s_removedFontTexList.Add(*((INT64 *) key));
	}
	else
		val++;

	return TRUE;
}

// --------------------------------------------------------------------------------------
CRuFontChar::CRuFontChar()
{
	m_pFontType			= NULL;
	m_char				= 0;
	m_x					= 0;
	m_y					= 0;
	m_toneSymbol		= NULL;
}

// --------------------------------------------------------------------------------------
CRuFontChar::~CRuFontChar()
{
	if ( m_toneSymbol )
	{
		delete [] m_toneSymbol;
		m_toneSymbol = NULL;
	}
}

// --------------------------------------------------------------------------------------
void CRuFontChar::Initialize( RuFontTextureCallBack func )
{
	CRuFontType::Initialize();
	CRuFontTexture::Initialize( func );

	s_fontChar.Clear();
}

// --------------------------------------------------------------------------------------
void CRuFontChar::Shutdown()
{
	s_fontChar.Clear();

	CRuFontTexture::Shutdown();
	CRuFontType::Shutdown();	
}

// --------------------------------------------------------------------------------------
int CRuFontChar::GetToneSymbolNumber( WCHAR *toneSymbol )
{
	if ( toneSymbol == NULL )
		return 0;

	int toneNumber = 0;
	for ( int i = 0; i < 4 && toneSymbol[i] != 0; i++ )
	{
		// 泰文
		if ( toneSymbol[i] >= 0x0E00 && toneSymbol[i] <= 0x0E7F )
			toneNumber += toneSymbol[i] - 0x0E00;
	}

	return toneNumber;
}

// --------------------------------------------------------------------------------------
// 取得字元結構
// 參數 : pFontType 字型類型
// 參數 : wchar 顯示單字元
// 參數 : toneSymbol 聲調字元串需要零結尾(泰文會有聲調符號)
CRuFontChar* CRuFontChar::GetFontChar( CRuFontType *pFontType, WCHAR wchar, WCHAR *toneSymbol )
{
	assert( pFontType );

	if ( toneSymbol && toneSymbol[0] == 0 )
		toneSymbol = NULL;

	INT64 toneSymbolNumber = CRuFontChar::GetToneSymbolNumber(toneSymbol);
	INT64 key = toneSymbolNumber << 32 | (pFontType->GetSerial() & 0xFFFF) << 16 | wchar;
	DWORD keyHash = RuGlobalHashGen().GetHashCode(key);
	CRuFontChar *fontChar = NULL;
	if(s_fontChar.Get(key, keyHash, fontChar))
	{
		return fontChar;
	}	

	fontChar = ruNEW CRuFontChar();
	fontChar->m_char = wchar;

	// 是否有聲調符號
	if ( toneSymbol != NULL )
	{
		fontChar->m_toneSymbol = ruNEW WCHAR[wcslen(toneSymbol) + 1];
		wcscpy(fontChar->m_toneSymbol, toneSymbol);
	}
	else
	{
		fontChar->m_toneSymbol = NULL;
	}

	// 取得字型寬高
	SIZE fontSize;
	pFontType->GetFontSize(wchar, toneSymbol, fontSize);

	fontChar->m_pFontType		= pFontType;
	fontChar->m_y				= (float)pFontType->GetHeight();
	fontChar->m_x				= (float)fontChar->m_y * fontSize.cx / fontSize.cy;
	s_fontChar.Add(key, keyHash, fontChar);
	// CRuFontTexture::ReadFontTexture(fontChar);
	return fontChar;
}

// --------------------------------------------------------------------------------------
// 繪出字串
bool CRuFontChar::DrawText( CRuFontType* pFontType, float x, float y, int color, const WCHAR* textStr )
{	
	if ( pFontType == NULL || textStr == NULL )
		return false;

	float x_draw = x;
	float y_draw = y;

	PaintBegin();
	for ( int i = 0; textStr[i]; i++ )
	{
		if ( textStr[i] == L'\n' )
		{
			x_draw = (float)x;
			y_draw += (float)pFontType->GetHeight();
		}
		else if ( textStr[i] >= L' ' )
		{
			CRuFontChar* pChar = GetFontChar( pFontType, textStr[i] );
			if ( pChar )
			{
				pChar->Paint( x_draw, y_draw, 1.0f, color );
				x_draw += (float)pChar->GetXSize();
			}
		}
	}
	return true;
}

// ----------------------------------------------------------------------------------
bool CRuFontChar::DrawTextA( CRuFontType* pFontType, float x, float y, int color, const char* textStr )
{
	// 轉換為雙字元組
	WCHAR* tmp = TransToWChar(textStr);
	bool result = DrawText( pFontType, x, y, color, tmp );
	if ( tmp ) delete [] tmp;
	return result;
}

// ----------------------------------------------------------------------------------
void CRuFontChar::Paint( float x, float y, float scale, DWORD color, RuFontShadow *pShadow, DWORD blockColor, bool bOutline )
{
	CRuFontTexture* fontTexture = CRuFontTexture::GetRuFontTexture(m_pFontType, m_char, m_toneSymbol);	
	
	if ( fontTexture )
	{
		float insert_x = 0.0f;
		float insert_y = 0.0f;

		if( bOutline == true )
		{
			insert_x = m_x * scale * 0.083f;
			insert_y = m_y * scale * 0.083f;
		}
		else
		{
			switch ( m_pFontType->GetOutLine() )
			{
			case ruFONTOUTLINE_NORMAL:
				insert_x = m_x * scale * 0.083f;
				insert_y = m_y * scale * 0.083f;
				break;

			case ruFONTOUTLINE_THICK:
				insert_x = m_x * scale * 0.083f * 2.0f;
				insert_y = m_y * scale * 0.083f * 2.0f;
				break;
			}
		}

		if ( insert_x > 1.0f ) insert_x = 1.0f;
		if ( insert_y > 1.0f ) insert_y = 1.0f;

		INT32 rectIdx = 0;

		CRuRectangle destRect;

		if ( bOutline == true )
		{
			destRect.m_left		= x + insert_x;
			destRect.m_top		= y + insert_y;
			destRect.m_right	= x + (m_x * scale) - insert_x;
			destRect.m_bottom	= y + (m_y * scale) - insert_y;
		}
		else
		{
			destRect.m_left		= x;
			destRect.m_top		= y;
			destRect.m_right	= x + (m_x * scale);
			destRect.m_bottom	= y + (m_y * scale);
		}		

		if ( blockColor != 0 )
		{
			m_runtime_SrcRects[rectIdx]				= CRuFontTexture::GetBlockTexture()->GetRectangle();

			m_runtime_DestRects[rectIdx].m_left		= x;
			m_runtime_DestRects[rectIdx].m_top		= y;
			m_runtime_DestRects[rectIdx].m_right	= x + (m_x * scale);
			m_runtime_DestRects[rectIdx].m_bottom	= y + (m_y * scale);
			
			m_runtime_Colors[rectIdx]				= blockColor;

			++rectIdx;
		}

		if ( insert_x > 0.01f && insert_y > 0.01f )
		{
			CRuRectangle inset;
			DWORD blackColor = (color & 0xFF000000) | 0x00;
			
			// 左
			m_runtime_SrcRects[rectIdx]				= fontTexture->GetRectangle();

			m_runtime_DestRects[rectIdx].m_top		= destRect.m_top;
			m_runtime_DestRects[rectIdx].m_left		= destRect.m_left - insert_x;
			m_runtime_DestRects[rectIdx].m_bottom	= destRect.m_bottom;
			m_runtime_DestRects[rectIdx].m_right	= destRect.m_right - insert_x;

			m_runtime_Colors[rectIdx]				= blackColor;

			++rectIdx;

			// 右
			m_runtime_SrcRects[rectIdx]				= m_runtime_SrcRects[rectIdx - 1];

			m_runtime_DestRects[rectIdx].m_top		= destRect.m_top;
			m_runtime_DestRects[rectIdx].m_left		= destRect.m_left + insert_x;
			m_runtime_DestRects[rectIdx].m_bottom	= destRect.m_bottom;
			m_runtime_DestRects[rectIdx].m_right	= destRect.m_right + insert_x;

			m_runtime_Colors[rectIdx]				= blackColor;

			++rectIdx;

			// 下
			m_runtime_SrcRects[rectIdx]				= m_runtime_SrcRects[rectIdx - 1];

			m_runtime_DestRects[rectIdx].m_top		= destRect.m_top - insert_y;
			m_runtime_DestRects[rectIdx].m_left		= destRect.m_left;
			m_runtime_DestRects[rectIdx].m_bottom	= destRect.m_bottom - insert_y;
			m_runtime_DestRects[rectIdx].m_right	= destRect.m_right;

			m_runtime_Colors[rectIdx]				= blackColor;

			++rectIdx;

			// 上
			m_runtime_SrcRects[rectIdx]				= m_runtime_SrcRects[rectIdx - 1];

			m_runtime_DestRects[rectIdx].m_top		= destRect.m_top + insert_y;
			m_runtime_DestRects[rectIdx].m_left		= destRect.m_left;
			m_runtime_DestRects[rectIdx].m_bottom	= destRect.m_bottom + insert_y;
			m_runtime_DestRects[rectIdx].m_right	= destRect.m_right;

			m_runtime_Colors[rectIdx]				= blackColor;

			++rectIdx;
		}
		else if ( pShadow && (pShadow->color >> 24) > 0x08 )
		{
			m_runtime_SrcRects[rectIdx]				= fontTexture->GetRectangle();

			m_runtime_DestRects[rectIdx].m_left		= destRect.m_left + pShadow->x;
			m_runtime_DestRects[rectIdx].m_top		= destRect.m_top + pShadow->y;
			m_runtime_DestRects[rectIdx].m_right	= destRect.m_right + pShadow->x;
			m_runtime_DestRects[rectIdx].m_bottom	= destRect.m_bottom + pShadow->y;

			m_runtime_Colors[rectIdx]				= pShadow->color & 0xFFFFFF;

			++rectIdx;
		}

		m_runtime_SrcRects[rectIdx]					= fontTexture->GetRectangle();

		m_runtime_DestRects[rectIdx].m_left			= destRect.m_left;
		m_runtime_DestRects[rectIdx].m_top			= destRect.m_top;
		m_runtime_DestRects[rectIdx].m_right		= destRect.m_right;
		m_runtime_DestRects[rectIdx].m_bottom		= destRect.m_bottom;

		/*
		m_runtime_DestRects[rectIdx].m_left			= x;
		m_runtime_DestRects[rectIdx].m_top			= y;
		m_runtime_DestRects[rectIdx].m_right		= x + m_x * scale;
		m_runtime_DestRects[rectIdx].m_bottom		= y + m_y * scale;
		*/

		m_runtime_Colors[rectIdx]					= color;

		++rectIdx;

		g_ruUIRenderer->Submit(rectIdx, m_runtime_SrcRects, m_runtime_DestRects, m_runtime_Colors);





/*
		CRuRectangle srcRect, destRect;

		destRect.m_left		= x;
		destRect.m_top		= y;
		destRect.m_right	= x + m_x * scale;
		destRect.m_bottom	= y + m_y * scale;
		srcRect				= fontTexture->GetRectangle();

		if ( blockColor != 0 )
		{
			CRuRectangle blockRect = CRuFontTexture::GetBlockTexture()->GetRectangle();
			g_ruUIRenderer->Submit(1, &blockRect, &destRect, &blockColor);
		}

		float insert_x = 0.0f;
		float insert_y = 0.0f;

		if( bOutline == true )
		{
			insert_x = m_x * scale * 0.083f;
			insert_y = m_y * scale * 0.083f;
		}
		else
		{
			switch ( m_pFontType->GetOutLine() )
			{
			case ruFONTOUTLINE_NORMAL:
				insert_x = m_x * scale * 0.083f;
				insert_y = m_y * scale * 0.083f;
				break;

			case ruFONTOUTLINE_THICK:
				insert_x = m_x * scale * 0.083f * 2.0f;
				insert_y = m_y * scale * 0.083f * 2.0f;
				break;
			}
		}

		if ( insert_x > 0.01f && insert_y > 0.01f )
		{
			if ( insert_x > 1.0f ) insert_x = 1.0f;
			if ( insert_y > 1.0f ) insert_y = 1.0f;

			CRuRectangle inset;
			DWORD blackColor = (color & 0xFF000000) | 0x00;
			// 左
			inset.m_top		= destRect.m_top;
			inset.m_left	= destRect.m_left - insert_x;
			inset.m_bottom	= destRect.m_bottom;
			inset.m_right	= destRect.m_right - insert_x;
			g_ruUIRenderer->Submit(1, &srcRect, &inset, &blackColor);

			// 右
			inset.m_top		= destRect.m_top;
			inset.m_left	= destRect.m_left + insert_x;
			inset.m_bottom	= destRect.m_bottom;
			inset.m_right	= destRect.m_right + insert_x;
			g_ruUIRenderer->Submit(1, &srcRect, &inset, &blackColor);

			// 下
			inset.m_top		= destRect.m_top - insert_y;
			inset.m_left	= destRect.m_left;
			inset.m_bottom	= destRect.m_bottom - insert_y;
			inset.m_right	= destRect.m_right;
			g_ruUIRenderer->Submit(1, &srcRect, &inset, &blackColor);

			// 上
			inset.m_top		= destRect.m_top + insert_y;
			inset.m_left	= destRect.m_left;
			inset.m_bottom	= destRect.m_bottom + insert_y;
			inset.m_right	= destRect.m_right;
			g_ruUIRenderer->Submit(1, &srcRect, &inset, &blackColor);
		}
		else if ( pShadow && (pShadow->color >> 24) > 0x08 )
		{
			CRuRectangle shadowRect;
			DWORD shadowColor = pShadow->color & 0xFFFFFF;

			shadowRect.m_left	= destRect.m_left + pShadow->x;
			shadowRect.m_top	= destRect.m_top + pShadow->y;
			shadowRect.m_right	= destRect.m_right + pShadow->x;
			shadowRect.m_bottom	= destRect.m_bottom + pShadow->y;

			shadowColor |= ((pShadow->color >> 24) * (color >> 24) / 255) << 24;
			g_ruUIRenderer->Submit(1, &srcRect, &shadowRect, &shadowColor);
		}

		g_ruUIRenderer->Submit(1, &srcRect, &destRect, &color);
*/
	}
}

// ----------------------------------------------------------------------------------
char *TransToChar(const WCHAR* text)
{
	char* tmp = NULL;
	if (text)
	{
		int nLen = WideCharToMultiByte(CP_ACP, 0, text, -1, NULL, NULL, NULL, NULL);
		char* tmp = ruNEW char[nLen];
		WideCharToMultiByte(CP_ACP, 0, text, -1, tmp, nLen, NULL, NULL);
		return tmp;
	}
	return tmp;
}

// ----------------------------------------------------------------------------------
WCHAR *TransToWChar(const char* text)
{	
	WCHAR *wbuff = NULL;
	if (text)
	{
		int nLen = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, NULL);
		wbuff = ruNEW WCHAR[nLen];
		MultiByteToWideChar(CP_ACP, 0, text, -1, wbuff, nLen);
	}
	return wbuff;
}

// ----------------------------------------------------------------------------------
WCHAR* Utf8ToWChar(const char* text)
{
	WCHAR *wbuff = NULL;
	if ( text )
	{
		int nLen = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, NULL);
		wbuff = ruNEW WCHAR[nLen];
		MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuff, nLen);
	}
	return wbuff;
}
// ----------------------------------------------------------------------------------
WCHAR* MultiByteToWChar(const char* s)
{
	WCHAR* ws = NULL;
	int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, NULL, NULL);
	if ( n > 0 ) {
		ws = ruNEW WCHAR[n];
		MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, n);
	} else {
		int n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, NULL);
		ws = ruNEW WCHAR[n];
		MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
	}
	return ws;
}

// ----------------------------------------------------------------------------------
bool IsToneSymbol(WCHAR wch)
{
	// 泰文聲調或特殊符號
	switch(wch)
	{
	case 0x0E31:
	case 0x0E33:
	case 0x0E34:
	case 0x0E35:
	case 0x0E36:
	case 0x0E37:
	case 0x0E38:
	case 0x0E39:
	case 0x0E3A:
	case 0x0E47:
	case 0x0E48:
	case 0x0E49:
	case 0x0E4A:
	case 0x0E4B:
	case 0x0E4C:
	case 0x0E4D:
	case 0x0E4E:
		return true;
		break;
	}
	return false;
}

#pragma managed(pop)
