//-------------------------------------------------------------------------------------
//
// JGE++ is a hardware accelerated 2D game SDK for PSP/Windows.
//
// Licensed under the BSD license, see LICENSE in JGE root for details.
//
// Copyright (c) 2007 James Hui (a.k.a. Dr.Watson) <jhkhui@gmail.com>
// Copyright (c) 2007 Sijiu Duan (a.k.a. Chi80) <sijiu49@gmail.com>
//
//-------------------------------------------------------------------------------------

#include <stdio.h>

#include "../include/JGBKFont.h"
#include "../include/JFileSystem.h"

JRenderer* JGBKFont::mRenderer = NULL;

JGBKFont::JGBKFont()
{
	mRenderer = JRenderer::GetInstance();

	mChnFont = NULL;
	mEngFont = NULL;

	mCurr = 0;

	mColor = ARGB(255,255,255,255);
	mBgColor = ARGB(0,0,0,0);

	mScale = 1.0f;

	mRotation = 0.0f;

	mCount = 0;
	mTexture = NULL;
	mSprites = NULL;
	mGBCode = NULL;

	mCharBuffer = NULL;
}



JGBKFont::~JGBKFont()
{
	SAFE_DELETE(mEngFont);

	SAFE_DELETE(mChnFont);

	SAFE_DELETE(mTexture);

	if (mSprites)
	{
		for (int i=0;i<mCacheSize;i++)
		{
			if (mSprites[i])
				delete mSprites[i];
		}

		delete [] mSprites;
	}

	if (mGBCode)
		delete [] mGBCode;

	if (mCharBuffer)
		delete[] mCharBuffer;


}


bool JGBKFont::Init(const char* engFileName, const char* chnFileName, int fontsize, bool smallEnglishFont)
{
	mSmallEnglishFont = smallEnglishFont;
	mFontSize = fontsize;

	mCacheImageWidth = 256;
	mCacheImageHeight = 256;

	mBytesPerRow = (mFontSize+7)/8;
	mBytesPerChar = mBytesPerRow*mFontSize;

	mCol = mCacheImageWidth/mFontSize;
	mRow = mCacheImageHeight/mFontSize;
	mCacheSize = mCol*mRow;

	mSprites = new JQuad*[mCacheSize];
	mGBCode = new int[mCacheSize];

#if defined (WIN32) || defined (LINUX)
	mCharBuffer = new DWORD[mFontSize*mFontSize];
#endif

	mTexture = mRenderer->CreateTexture(mCacheImageWidth, mCacheImageHeight, true);

	int index = 0;
	for (int y=0;y<mRow;y++)
	{
		for (int x=0;x<mCol;x++)
		{
			mGBCode[index] = -1;

			mSprites[index] = new JQuad(mTexture, x*mFontSize, y*mFontSize, mFontSize, mFontSize);
			mSprites[index]->SetHotSpot(mFontSize/2, mFontSize/2);

			index++;
		}
	}

	int size;

	JFileSystem *fileSys = JFileSystem::GetInstance();
	if (!fileSys->OpenFile(engFileName))
		return false;

	size = fileSys->GetFileSize();
	mEngFont = new BYTE[size];

	fileSys->ReadFile(mEngFont, size);
	fileSys->CloseFile();


	if (!fileSys->OpenFile(chnFileName))
		return false;

	size = fileSys->GetFileSize();

	mChnFont = new BYTE[size];

	fileSys->ReadFile(mChnFont, size);
	fileSys->CloseFile();

	return true;
}


#if defined (WIN32) || defined (LINUX)
#else
void SwizzlePlot(u8* out, PIXEL_TYPE color, int i, int j, unsigned int width)
{
	unsigned int rowblocks = (width >> 4);

	unsigned int blockx = (i >> 4);
	unsigned int blocky = (j >> 3);

	unsigned int x = (i - (blockx<<4));
	unsigned int y = (j - (blocky<<3));
	unsigned int block_index = blockx + ((blocky) * rowblocks);
	unsigned int block_address = block_index << 7;

	u8* p = out + (block_address + x + (y << 4));
	PIXEL_TYPE* dest = (PIXEL_TYPE *)p;
	*dest = color;

}
#endif

int JGBKFont::PreCacheChar(const BYTE *ch)
{
	int code;
	BOOL isChinese = true;
	if (*ch > 0x80)
	{
		// get offset to the proper character bits (GBK encoding)
		code = (((DWORD)(*ch - 0x81)) * 0xBF + ((DWORD)(*(ch + 1) - 0x40)));
	}
	else
	{
		code = ((DWORD)*ch)|0x10000;
		isChinese = false;
	}

	if (mGBCode[mCurr] != -1)
	{

		for (int i=0;i<mCacheSize;i++)
		{
			if (mGBCode[i] == code)
				return i;
		}
	}

	mCount++;

	int index = mCurr++;
	if (mCurr >= mCacheSize)
		mCurr = 0;

#if defined (WIN32) || defined (LINUX)
	int x = 0;
	int y = 0;

	memset(mCharBuffer, 0, sizeof(DWORD)*mFontSize*mFontSize);
#else
	u8* pTexture = (u8*) mTexture->mBits;
	int x;
	int y = (int)mSprites[index]->mY;
#endif


	BYTE* src;
	BYTE bits;
	int bitCount;

	if (isChinese)
	{

		src = mChnFont +  code * mBytesPerChar;
		for (int i=0;i<mBytesPerChar;)
		{

			bitCount = mFontSize;

#if defined (WIN32) || defined (LINUX)
			x = 0;
#else
			x = (int)mSprites[index]->mX;
#endif

			for (int j=0;j<mBytesPerRow;j++)
			{
				bits = src[i++];
				BYTE bitMask = 0x80;
				for (int z=0;z<8&&bitCount;z++)
				{
#if defined (WIN32) || defined (LINUX)
					if ((bits & bitMask) != 0)
						mCharBuffer[y*mFontSize+x] = ARGB(255,255,255,255);
					else
						mCharBuffer[y*mFontSize+x] = ARGB(0,0,0,0);
#else
					if ((bits & bitMask) != 0)
						SwizzlePlot(pTexture, ARGB(255,255,255,255), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
					else
						SwizzlePlot(pTexture, ARGB(0,0,0,0), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
#endif
					bitMask >>= 1;
					x++;

					bitCount--;

				}

			}
			y++;
		}
	}
	else
	{
		int size = (mFontSize <= 16)?mFontSize:mFontSize*2;
		src = mEngFont +  (code-0x10000) * size;

		int n;

		for (int i=0;i<size;)
		{

#if defined (WIN32) || defined (LINUX)
			x = 0;
#else
			x = (int)mSprites[index]->mX;
#endif
			// width of the English fonts is only half of the Chinese ones
			// put char in the middle
			if (mFontSize <= 16)
			{

				for (n=0;n<(mFontSize-8)/2;n++)
				{
	#if defined (WIN32) || defined (LINUX)
					mCharBuffer[y*mFontSize+x] = ARGB(0,0,0,0);
	#else
					SwizzlePlot(pTexture, ARGB(0,0,0,0), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
	#endif
					x++;
				}
			}

			int count = (mFontSize <= 16)?1:2;
			bitCount = mFontSize;
			for (int k=0;k<count;k++)
			{
				bits = src[i++];
				BYTE bitMask = 0x80;
				for (int z=0;z<8&&bitCount;z++)
				{
				#if defined (WIN32) || defined (LINUX)
					if ((bits & bitMask) != 0)
						mCharBuffer[y*mFontSize+x] = ARGB(255,255,255,255);
					else
						mCharBuffer[y*mFontSize+x] = ARGB(0,0,0,0);
				#else
					if ((bits & bitMask) != 0)
						SwizzlePlot(pTexture, ARGB(255,255,255,255), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
					else
						SwizzlePlot(pTexture, ARGB(0,0,0,0), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
				#endif
					x++;
					bitMask >>= 1;
					bitCount--;
				}
			}

			if (mFontSize <= 16)
			{

				for (n=0;n<(mFontSize-8)/2;n++)
				{
				#if defined (WIN32) || defined (LINUX)
					mCharBuffer[y*mFontSize+x] = ARGB(0,0,0,0);
				#else
					SwizzlePlot(pTexture, ARGB(0,0,0,0), x*PIXEL_SIZE, y, mTexture->mTexWidth*PIXEL_SIZE);
				#endif
					x++;
				}
			}

			y++;

		}
	}

	mGBCode[index] = code;

#if defined (WIN32) || defined (LINUX)
	x = (int)mSprites[index]->mX;
	y = (int)mSprites[index]->mY;

	glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, mFontSize, mFontSize, GL_RGBA, GL_UNSIGNED_BYTE, mCharBuffer);

#else
	sceKernelDcacheWritebackAll();
	//sceKernelDcacheWritebackInvalidateAll();
#endif
	return index;
}


int JGBKFont::PrepareString(BYTE* str, int* dest)
{
	mRenderer->BindTexture(mTexture);

	BYTE* src = str;
	int count = 0;
	while (*src != 0)
	{
		if (*src > 0x80)
		{
			dest[count++] = PreCacheChar(src);
			src += 2;
		}
		else if (*src >= ' ')
		{
			dest[count++] = PreCacheChar(src);
			src += 1;
		}
		else	// control characters
		{
			dest[count++] = 0x80000 | *src;
			src += 1;
		}
	}

	return count;
}


void JGBKFont::RenderEncodedString(const int* text, int count, float x, float y)
{
	int n = 0;
	float xx = x;
	float yy = y;
	bool isChinese;

	for (int i=0;i<count;i++)
	{
		if (text[n] == 0x8000a)
		{
			xx = x;
			yy += (mFontSize*mScale);
		}
		else if (text[n] == 0x8000d)
		{

		}
		else
		{
			isChinese = ((text[n] & 0x10000) == 0x10000);
			mSprites[text[n]]->SetColor(mColor);
			mRenderer->RenderQuad(mSprites[text[n]], xx, yy, mRotation, mScale, mScale);
			if (mSmallEnglishFont && !isChinese)
				xx += (mFontSize*mScale)/2;
			else
				xx += (mFontSize*mScale);
			if (xx >= 480)
			{
				xx = x;
				yy += (mFontSize*mScale);
			}
		}
		n++;
	}
}
int JGBKFont::GetStringWidth( BYTE* str )
{
	int w=0;
	int h=0;
	GetStringArea(str, &w, &h);
	return w;
}

int JGBKFont::GetStringHeight( BYTE* str )
{
	int w=0;
	int h=0;
	GetStringArea(str, &w, &h);
	return h;
}
void JGBKFont::GetStringArea( BYTE* str,  int *w, int *h)
{
	BYTE* src = str;
	float len= 0;
	float xx = 0;
	float yy = mFontSize*mScale;
	bool isChinese=true;

	while (*src != 0)
	{
		if (yy + mFontSize < 0.0f)		// don't render when outside viewport
		{
			if (*src < ' ')				// control characters
			{
				if (*src == 0x0a)		// NEWLINE
				{
					if(xx>len)
						len=xx;
					xx = 0;
					yy += (mFontSize*mScale);
				}
				src += 1;
			}
			else
			{
				if (*src > 0x80)
				{
					src += 2;
				}
				else if (*src >= ' ')
				{
					src += 1;
				}

				xx += (mFontSize*mScale);

				if (xx >= 480)
				{
					if(xx>len)
						len=xx;
					xx = 0;
					yy += (mFontSize*mScale);
				}
			}
		}
		else if (yy > SCREEN_HEIGHT_F)
		{
			break;
		}
		else
		{

			if (*src < ' ')			// control characters
			{
				if (*src == 0x0a)	// NEWLINE
				{
					if(xx>len)
						len=xx;
					xx = 0;
					yy += (mFontSize*mScale);
				}
				src += 1;
			}
			else
			{
				if (*src > 0x80)
				{
					//index = PreCacheChar(src);
					src += 2;
					if (isChinese==false)
						xx+=5*(mFontSize*mScale)/16;
					isChinese = true;
				}
				else if (*src >= ' ')
				{
					src += 1;
					if(isChinese==true)
						xx-=3*(mFontSize*mScale)/16;
					isChinese = false;
				}

				if (mSmallEnglishFont && !isChinese)
					xx += (mFontSize*mScale)/2;
				else
					xx += (mFontSize*mScale);

				if (xx >= 480)
				{
					if(xx>len)
						len=xx;
					xx = 0;
					yy += (mFontSize*mScale);
				}
			}
		}

	}
	if (xx>len)
	{
		len=xx;
	}
	*w=(int)len;
	*h=(int)yy;

}

void JGBKFont::RenderString(BYTE* str, float x, float y, int alignment)
{
	int w=0;
	int h=0;

	switch(alignment)
	{
	case JGETEXT_RIGHT:
		GetStringArea(str,&w,&h);
		x-=w;
		break;
	case JGETEXT_CENTER:
		GetStringArea(str,&w,&h);
		x-=w/2;
		break;
	case JGETEXT_LEFT:
	default:
		break;
	}


	mRenderer->BindTexture(mTexture);

	BYTE* src = str;
	float xx = x;
	float yy = y;
	int index;

	bool isChinese=true;

	while (*src != 0)
	{
		if (yy + mFontSize < 0.0f)		// don't render when outside viewport
		{
			if (*src < ' ')				// control characters
			{
				if (*src == 0x0a)		// NEWLINE
				{
					xx = x;
					yy += (mFontSize*mScale);
				}
				src += 1;
			}
			else
			{
				if (*src > 0x80)
				{
					src += 2;
				}
				else if (*src >= ' ')
				{
					src += 1;
				}

				xx += (mFontSize*mScale);

				if (xx >= 480)
				{
					xx = x;
					yy += (mFontSize*mScale);
				}
			}
		}
		else if (yy > SCREEN_HEIGHT_F)
		{
			return;
		}
		else
		{

			if (*src < ' ')			// control characters
			{
				if (*src == 0x0a)	// NEWLINE
				{
					xx = x;
					yy += (mFontSize*mScale);
				}
				src += 1;
			}
			else
			{
				if (*src > 0x80)
				{
					index = PreCacheChar(src);
					src += 2;
					if (isChinese==false)
						xx+=5*(mFontSize*mScale)/16;
					isChinese = true;
				}
				else if (*src >= ' ')
				{
					index = PreCacheChar(src);
					src += 1;
					if(isChinese==true)
						xx-=3*(mFontSize*mScale)/16;
					isChinese = false;
				}

				mSprites[index]->SetColor(mColor);
				mRenderer->RenderQuad(mSprites[index], xx, yy, mRotation, mScale, mScale);
				if (mSmallEnglishFont && !isChinese)
					xx += (mFontSize*mScale)/2;
				else
					xx += (mFontSize*mScale);

				if (xx >= 480)
				{
					xx = x;
					yy += (mFontSize*mScale);
				}
			}
		}
	}

}

void JGBKFont::SetScale(float scale) { mScale = scale; }
void JGBKFont::SetRotation(float rot) { mRotation = rot; }
void JGBKFont::SetColor(PIXEL_TYPE color) { mColor = color; }
void JGBKFont::SetBgColor(PIXEL_TYPE color) { mBgColor = color; }

