#include "font_d3d9.h"

static unsigned int _pow2(unsigned int i)
{
	register unsigned int p2;
	for (p2=1; p2<i; p2<<=1) ;
	return p2;
}

FontD3D9::FontD3D9(LPDIRECT3DDEVICE9 dev,TrueTypeFont *ttf): dev(dev)
{
	is_valid=true;
	pt=ttf->pt;

	wids=(float *)malloc(sizeof(float)*255);
	bears=(float *)malloc(sizeof(float)*255);
	hoss=(float *)malloc(sizeof(float)*255);
	qvws=(int *)malloc(sizeof(int)*255);
	qvhs=(int *)malloc(sizeof(int)*255);
	qtws=(float *)malloc(sizeof(float)*255);
	qths=(float *)malloc(sizeof(float)*255);

	memset(textures,0,sizeof(LPDIRECT3DTEXTURE9)*255);

	LPDIRECT3DSURFACE9 surface;
	D3DLOCKED_RECT lockrect;  // surface locked image rect (to copy stuff to)
	D3DSURFACE_DESC surfdesc; // surface descriptor (for information)

	for (register unsigned char ch=0; ch<255; ch++)
	{
		register int i,j;
		FT_Face face=ttf->face;
		FT_BitmapGlyph bitmap_glyph;
		FT_Glyph glyph;
		FT_Bitmap bitmap;
		unsigned int width,height;
		UINT8 *expanded_data;

		FT_Load_Glyph(face,FT_Get_Char_Index(face,ch),FT_LOAD_DEFAULT);
		FT_Get_Glyph(face->glyph,&glyph);

		FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
		bitmap_glyph=(FT_BitmapGlyph)glyph;
		bitmap=bitmap_glyph->bitmap;

		width=_pow2(bitmap.width);
		height=_pow2(bitmap.rows);

		expanded_data=new UINT8[width*height];

		for (j=0; j<(int)height;j++)
		{
			for (i=0; i<(int)width; i++)
			{
				expanded_data[i+j*width]=
					(i>=bitmap.width||j>=bitmap.rows)?
					0:bitmap.buffer[i+bitmap.width*j];
			}
		}

		LPDIRECT3DTEXTURE9 tex;
		if (dev->CreateTexture(width,height,0,0,D3DFMT_A8,D3DPOOL_MANAGED,&tex,NULL)!=D3D_OK)
			is_valid=false;

		textures[ch]=tex;
		textures[ch]->GetLevelDesc(0,&surfdesc);
		textures[ch]->GetSurfaceLevel(0,&surface);
		if (surface->LockRect(&lockrect,NULL,0)==D3D_OK)
		{
			memcpy(lockrect.pBits,expanded_data,width*height);
			if (surface->UnlockRect()!=D3D_OK) is_valid=false;
		}
		else
			is_valid=false;

		delete[] expanded_data;

		this->wids[ch]=(float)(face->glyph->advance.x>>6);
		this->hoss[ch]=(float)(face->glyph->metrics.horiBearingY>>6);
		this->bears[ch]=(float)(face->glyph->metrics.horiBearingX>>6);

		this->qvws[ch]=bitmap.width;
		this->qvhs[ch]=bitmap.rows;

		this->qtws[ch]=(float)bitmap.width/(float)width;
		this->qths[ch]=(float)bitmap.rows/(float)height;
	}

	color=Vector4(1.0f);
}

FontD3D9::~FontD3D9()
{
	for (register unsigned int i=0; i<255; i++)
	{
		if (textures[i]) textures[i]->Release();
	}

	free((void *)wids);
	free((void *)bears);
	free((void *)hoss);
	free((void *)qvws);
	free((void *)qvhs);
	free((void *)qtws);
	free((void *)qths);
}

struct __fontquadvertex
{
	float x,y,z,rhw; // x, y, and z pre-transformed
	DWORD color; // 32 bit ARGB color
	float s,t; // tex coords
};

void FontD3D9::Render(int x,int y,const char *text)
{
	if (!is_valid) return;

	// create a new state block. a lot like glPushAttrib
	IDirect3DStateBlock9 *stateblock=NULL;
	dev->CreateStateBlock(D3DSBT_ALL,&stateblock);
	if (!stateblock) return;

	// set up font render state
	dev->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
	dev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
	dev->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
	dev->SetRenderState(D3DRS_ALPHATESTENABLE,FALSE);
	dev->SetRenderState(D3DRS_ZENABLE,FALSE);

	dev->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
	dev->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
	dev->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_NONE);

	dev->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP);
	dev->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_CLAMP);

	dev->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
	dev->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_DIFFUSE);
	dev->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TEXTURE);

	dev->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
	dev->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
	dev->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);

	unsigned int len=strlen(text);
	__fontquadvertex verts[4];
	memset(verts,0,sizeof(__fontquadvertex)*4);
	float widaccum=0.0f;
	for (char *ch=(char *)text; *ch; ch++)
	{
		verts[0].rhw=1.0f;
		verts[1].rhw=1.0f;
		verts[2].rhw=1.0f;
		verts[3].rhw=1.0f;

		float ex=(widaccum+x+bears[*ch])-0.5f;
		float wy=y+(pt-hoss[*ch])-0.5f;

		verts[0].x=ex; verts[0].y=wy;
		verts[1].x=ex; verts[1].y=wy+(float)qvhs[*ch];
		verts[2].x=ex+(float)qvws[*ch]; verts[2].y=wy;
		verts[3].x=ex+(float)qvws[*ch]; verts[3].y=wy+(float)qvhs[*ch];

		verts[1].t=qths[*ch]; verts[2].s=qtws[*ch];
		verts[3].s=qtws[*ch]; verts[3].t=qths[*ch];

		verts[0].color=verts[1].color=
			verts[2].color=verts[3].color=
			D3DCOLOR_COLORVALUE(color.x,color.y,color.z,color.w);

		dev->SetTexture(0,textures[*ch]);
		dev->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
		dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,verts,sizeof(__fontquadvertex));

		widaccum+=wids[*ch];
	}

	// restore the old state. a lot like glPopAttrib
	stateblock->Apply();
	stateblock->Release();
}
