#include "font.h"
#include "fontmanager.h"
#include "texturemanager.h"
#include "buffermanager.h"

FontParam::FontParam(void)
{
	m_Height=12;
	m_Width=0;
	m_Escapement=0;
	m_Orientation=0;
	m_Weight=FW_NORMAL;
	m_Italic=FALSE;
	m_Underline=FALSE;
	m_StrikeOut=FALSE;
	m_CharSet=ANSI_CHARSET;
	m_OutPrecision=OUT_DEFAULT_PRECIS;
	m_ClipPrecision=CLIP_DEFAULT_PRECIS;
	m_Quality=ANTIALIASED_QUALITY;
	m_PitchAndFamily=VARIABLE_PITCH;
	m_Facename="Arial";
	m_Spacing=0;
}

bool FontParam::operator ==(FontParam&fnt)
{
	if( m_Height == fnt.m_Height &&
		m_Facename == fnt.m_Facename)
		return true;
	return false;
}

HFONT FontParam::Create(void)
{
	return ::CreateFont(m_Height,
		m_Width, 
		m_Escapement, 
		m_Orientation, 
		m_Weight, 
		m_Italic, 
		m_Underline, 
		m_StrikeOut,
		m_CharSet,
		m_OutPrecision,
		m_ClipPrecision,
		m_Quality,
		m_PitchAndFamily,
		m_Facename.getbuffer());
}

Font::Font(FontManager*manager,FontParam&fnt)
{
	m_Manager = manager;

	m_Param = fnt;

	//m_VertexBuffer = NULL;

	HFONT hFont = m_Param.Create();
    HDC hDC = NULL;

    hDC = CreateCompatibleDC( NULL );
    SetMapMode( hDC, MM_TEXT );
	SelectObject( hDC, hFont );

	// Calculate the spacing between characters based on line height
	char str[2] = "ö"; 
	SIZE size;
    if( 0 == GetTextExtentPoint32( hDC, str, 1, &size ) )
	{
    //    return E_FAIL;
	}
	
    m_Param.m_Spacing = (DWORD) ceil(size.cy * 0.3f);

    unsigned texxl = 64;
	unsigned texyl = 64;

	unsigned char c = 32;
	bool h = true;

    while(c)
    {
		if(h)
			texxl *= 2;
		else
			texyl *= 2;
		
		h = !h;

		SIZE size;


		DWORD x = m_Param.m_Spacing;
		DWORD y = 0;
	    
		// For each character, draw text on the DC and advance the current position
		for( c = 32; c; c++ )
		{
			str[0] = c;
			//wlog("%c",str[0]);

			if( 0 == GetTextExtentPoint32( hDC, str, 1, &size ) )
			{
				break;
			//	return E_FAIL;
			}

			//wlog("%d",size.cx);

			if( (DWORD)(x + size.cx + m_Param.m_Spacing) > texxl )
			{
				x  = m_Param.m_Spacing;
				y += size.cy + 1;
			}

			// Check to see if there's room to write the character here
			if( y + size.cy > texyl )
			{
				break; //c wont be 0 and so we increase the size and try again
				//return E_FAIL;
			}

			x += size.cx + (2 * m_Param.m_Spacing);
		}
    }

	m_Texture = m_Manager->GetDevice()->GetTextureManager()->CreateTexture(texxl,texyl,1,0,FMT_A4R4G4B4,POOL_MANAGED);

	DrawAlphabetTex();

	//m_Invalidated = true;

	//Restore();
}

Font::~Font(void)
{
	m_Manager->Release(this);
	//Invalidate();
	SAFERELEASE(m_Texture);
}

void Font::Draw(XString text,float x, float y, DWORD dwColor,float maxwidth)
{
//	if(m_Invalidated)
//		return ;

	if(!m_Manager->m_FX->IsCompiled())
		return;
	//D3DXSaveTextureToFile("a.jpg", D3DXIFF_JPG , m_Texture->GetSurface() ,NULL);

	m_Manager->GetDevice()->SetFillmode(FILL_SOLID);

	/*m_Manager->GetDevice()->SetZEnable(D3DZB_FALSE);
	
	m_Manager->GetDevice()->SetCullmode(D3DCULL_CCW);*/

	/*m_Manager->GetDevice()->SetAlphaBlendEnable( false );
	m_Manager->GetDevice()->SetSrcBlend( D3DBLEND_SRCALPHA );
	m_Manager->GetDevice()->SetDestBlend( D3DBLEND_INVSRCALPHA );
	m_Manager->GetDevice()->SetAlphaTestEnable( false );
	m_Manager->GetDevice()->SetAlphaRef( 0x01 );
	m_Manager->GetDevice()->SetAlphaFunc( D3DCMP_GREATER );*/

	/*m_Manager->GetDevice()->SetTextureFactor(0);
	
	m_Manager->GetDevice()->SetLighting(false);
	//device->GetDevice()->LightEnable(0,false);

	m_Manager->GetDevice()->SetStencilEnalbe( FALSE );
	m_Manager->GetDevice()->SetClipping( TRUE );
	//m_Param.Device->SetRenderState( D3DRS_EDGEANTIALIAS,    FALSE );
	m_Manager->GetDevice()->SetClipplaneEnable( FALSE );
	m_Manager->GetDevice()->SetVertexblend(D3DVBF_DISABLE );
	m_Manager->GetDevice()->SetIndexedVertexblendEnable( FALSE );
	m_Manager->GetDevice()->SetFogEnable( FALSE );
	m_Manager->GetDevice()->SetColorWriteEnable( D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_GREEN |
		D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
	//m_Manager->GetDevice()->SetStageAlphaOperation(0,D3DTA_TEXTURE,D3DTOP_MODULATE,D3DTA_DIFFUSE);
	//m_Manager->GetDevice()->SetStageColorOperation(0,D3DTA_TEXTURE,D3DTOP_MODULATE,D3DTA_DIFFUSE);
	m_Manager->GetDevice()->SetStageAlphaOperation(0,D3DTA_TEXTURE,D3DTOP_SELECTARG1,D3DTA_DIFFUSE);
	m_Manager->GetDevice()->SetStageColorOperation(0,D3DTA_TEXTURE,D3DTOP_SELECTARG1,D3DTA_DIFFUSE);
	m_Manager->GetDevice()->SetTextureCoordIndex(0,0);
	m_Manager->GetDevice()->GetDevice()->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
	m_Manager->GetDevice()->SetColorOperation(1, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetAlphaOperation(1, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetColorOperation(2, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetAlphaOperation(2, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetColorOperation(3, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetAlphaOperation(3, D3DTOP_DISABLE );
	m_Manager->GetDevice()->SetTextureFilter(0,D3DTEXF_LINEAR,D3DTEXF_LINEAR,D3DTEXF_NONE);
	m_Manager->GetDevice()->SetTextureAddressing(0, D3DTADDRESS_CLAMP, D3DTADDRESS_CLAMP, D3DTADDRESS_CLAMP);

	m_Manager->GetDevice()->SetFVF( D3DFVF_FONTVERTEX );
    m_Manager->GetDevice()->SetPixelShader( (PixelShader*)NULL );
	m_Manager->GetDevice()->SetVertexShader( (VertexShader*)NULL );
    m_Manager->GetDevice()->SetStreamSource( 0, m_VertexBuffer, 0, sizeof(FONTVERTEX) );
	m_Manager->GetDevice()->SetTexture( 0, (DXTexture*)NULL );
	m_Manager->GetDevice()->SetTexture( 0, m_Texture );
	m_Manager->GetDevice()->SetTexture( 1, (DXTexture*)NULL );
	m_Manager->GetDevice()->SetTexture( 2, (DXTexture*)NULL );
	m_Manager->GetDevice()->SetTexture( 3, (DXTexture*)NULL );*/

	m_Manager->GetDevice()->SetVertexDeclaration(m_Manager->m_VDecl);

	m_Manager->m_FX->GetShader()->SetTexture("Texture", m_Texture->GetSurface());
	m_Manager->m_FX->SetTechnique("RenderTexVertColor");
	m_Manager->m_FX->GetShader()->SetBool("AlphaTest", true);
	m_Manager->m_FX->GetShader()->SetBool("AlphaBlend", true);
	
	x -= m_Param.m_Spacing;
    float fStartX = x;

    // Fill vertex buffer
    FONTVERTEX* pVertices = NULL;
    DWORD         dwNumTriangles = 0;
    //m_VertexBuffer->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );

	int numVert = text.getlen() * 6; //6 vertices per letter

	if(numVert < 1)
		return;

	pVertices = (FONTVERTEX*)m_Manager->m_VB->LockVB(numVert * sizeof(FONTVERTEX));

	int charsperline = 0;
	int lineheight = (m_TexCoords[0][3]-m_TexCoords[0][1])*m_Texture->GetYl();

	int i;
    for(i=0;i<text.getlen();i++)
    {
        unsigned char c = text.getbuffer()[i];

		//wlog("%c",c);

        if( c == '\n' )
        {
            x = fStartX;
            y += lineheight;
			charsperline = 0;
			continue;
        }

		if(c == '\t')
			x += (int)x + 16 - (((int)x)%16);

        if( (c-32) < 0 || (c-32) >= 256-32 )
            continue;

        FLOAT tx1 = m_TexCoords[c-32][0];
        FLOAT ty1 = m_TexCoords[c-32][1];
        FLOAT tx2 = m_TexCoords[c-32][2];
        FLOAT ty2 = m_TexCoords[c-32][3];

        FLOAT w = (tx2-tx1) * m_Texture->GetXl();
        FLOAT h = (ty2-ty1) * m_Texture->GetYl();

		float charwidth = w - (2 * m_Param.m_Spacing);

		if(charsperline && x + charwidth > fStartX + maxwidth)
		{
			y += lineheight;
			x = fStartX;
			charsperline = 0;
		}

        if( c != ' ')
        {
			pVertices->p = DXVector4(x+0-0.5f,y+h-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx1;
			pVertices->tv = ty2;
			pVertices++;

			pVertices->p = DXVector4(x+0-0.5f,y+0-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx1;
			pVertices->tv = ty1;
			pVertices++;

			pVertices->p = DXVector4(x+w-0.5f,y+h-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx2;
			pVertices->tv = ty2;
			pVertices++;

			pVertices->p = DXVector4(x+w-0.5f,y+0-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx2;
			pVertices->tv = ty1;
			pVertices++;

			pVertices->p = DXVector4(x+w-0.5f,y+h-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx2;
			pVertices->tv = ty2;
			pVertices++;

			pVertices->p = DXVector4(x+0-0.5f,y+0-0.5f,1.f,1.0f);
            pVertices->color = dwColor;
			pVertices->tu = tx1;
			pVertices->tv = ty1;
			pVertices++;

            dwNumTriangles += 2;

          /*  if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
            {
                // Unlock, render, and relock the vertex buffer
                m_VertexBuffer->Unlock();
                m_Manager->GetDevice()->DrawPrimitive( PT_TRIANGLELIST, 0, dwNumTriangles );
                pVertices = NULL;
                m_VertexBuffer->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
                dwNumTriangles = 0L;
            }*/
        }

		x += charwidth;
		charsperline++;
    }

	m_Manager->m_VB->UnlockVB();
	m_Manager->m_VB->SetVBStream(sizeof(FONTVERTEX),0);
    // Unlock and render the vertex buffer
    //m_VertexBuffer->Unlock();

    if( dwNumTriangles > 0 )
	{
		RENDERFX(m_Manager->m_FX, 
			m_Manager->GetDevice()->DrawPrimitive( PT_TRIANGLELIST, 0, dwNumTriangles );
		);
	}
}
/*
void Font::Restore(void)
{
	if(!m_Invalidated)
		return ;

	//m_Manager->GetDevice()->GetDevice()->CreateVertexBuffer(sizeof(FONTVERTEX)*MAX_NUM_VERTICES,D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, D3DFVF_FONTVERTEX,D3DPOOL_DEFAULT, &m_VertexBuffer,NULL);

    

	//////////////////
	m_Invalidated=false;
}

void Font::Invalidate(void)
{
	if(m_Invalidated)
		return ;

	//if(m_Texture)
	//	m_Texture->Invalidate();

	//SAFERELEASE(m_VertexBuffer);

	m_Invalidated = true;
}*/

DXVector2 Font::TextSize(const XString& Text, const float maxwidth)
{
	float lineheight = (m_TexCoords[0][3]-m_TexCoords[0][1])*m_Texture->GetYl();
	float xl=0;
	float yl=lineheight;

	bool hasmaxwidth = false;
	int charsperline = 0; //at least 1 char per line

	for(int i=0;i<Text.getlen();i++)
    {
        unsigned char c = Text.getbuffer()[i];

		if( c == '\n' )
		{
			charsperline = 0;
			hasmaxwidth = true;
			yl += lineheight;
			continue;
		}

        if( (c-32) < 0 || (c-32) >= 256-32 )
            continue;

        float tx1 = m_TexCoords[c-32][0];
        float tx2 = m_TexCoords[c-32][2];

        float w = (tx2-tx1) * m_Texture->GetXl();

		float charxl = w - (2 * m_Param.m_Spacing);

		if(charsperline && xl + charxl > maxwidth) //this char is in next line
		{
			yl += lineheight;
			xl = charxl;
			charsperline = 0;
			hasmaxwidth = true;
		}
		else
			xl += charxl;

		charsperline++;
    }

	return DXVector2(hasmaxwidth?maxwidth:xl,yl);
}

FontParam* Font::GetParam(void)
{
	return &m_Param;
}

HRESULT Font::PaintAlphabet( HDC hDC)
{
	SIZE size;
    char str[2] = "ö"; // One-character, null-terminated XString
    
    // Set the starting point for the drawing
    DWORD x = m_Param.m_Spacing;
    DWORD y = 0;
    
    // For each character, draw text on the DC and advance the current position
    for( unsigned char c = 32; c; c++ )
    {
        str[0] = c;
		//wlog("%c",str[0]);

        if( 0 == GetTextExtentPoint32( hDC, str, 1, &size ) )
            return E_FAIL;

		//wlog("%d",size.cx);

        if( (DWORD)(x + size.cx + m_Param.m_Spacing) > m_Texture->GetXl() )
        {
            x  = m_Param.m_Spacing;
            y += size.cy + 1;
        }

        // Check to see if there's room to write the character here
        if( y + size.cy > m_Texture->GetYl() )
            return E_FAIL;
              
        // Perform the actual drawing
        if( 0 == ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL ) )
            return E_FAIL;

        m_TexCoords[c-32][0] = ((float)(x + 0       - m_Param.m_Spacing))/m_Texture->GetXl();
        m_TexCoords[c-32][1] = ((float)(y + 0       + 0          ))/m_Texture->GetYl();
        m_TexCoords[c-32][2] = ((float)(x + size.cx + m_Param.m_Spacing))/m_Texture->GetXl();
        m_TexCoords[c-32][3] = ((float)(y + size.cy + 0          ))/m_Texture->GetYl();

		//wlog("%f %f %f %f",m_fTexCoords[c-32][0],m_fTexCoords[c-32][1],m_fTexCoords[c-32][2],m_fTexCoords[c-32][3]);
		//wlog("%d %d",m_dwTexxl,m_dwTexyl);
    
        x += size.cx + (2 * m_Param.m_Spacing);
    }

    return S_OK;
}

void Font::DrawAlphabetTex(void)
{
	HFONT hFont = m_Param.Create();
    HDC hDC = NULL;
    HBITMAP hbmBitmap = NULL;
    HGDIOBJ hbmOld = NULL;

    hDC = CreateCompatibleDC( NULL );
    SetMapMode( hDC, MM_TEXT );

	SelectObject( hDC, hFont );

    // Prepare to create a bitmap
    DWORD*      pBitmapBits;
    BITMAPINFO bmi;
    ZeroMemory( &bmi.bmiHeader, sizeof(BITMAPINFOHEADER) );
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth       =  m_Texture->GetXl();
    bmi.bmiHeader.biHeight      =  -(int)m_Texture->GetYl();
    bmi.bmiHeader.biPlanes      = 1;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biBitCount    = 32;

    // Create a bitmap for the font
    hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
                                  (void**)&pBitmapBits, NULL, 0 );

    hbmOld = SelectObject( hDC, hbmBitmap );
    
    // Set text properties
    SetTextColor( hDC, RGB(255,255,255) );
    SetBkColor(   hDC, 0x00000000 );
    SetTextAlign( hDC, TA_TOP );

    // Paint the alphabet onto the selected bitmap
    PaintAlphabet( hDC );
    
    // Lock the surface and write the alpha values for the set pixels
    D3DLOCKED_RECT d3dlr;
    m_Texture->GetTextureSurface()->LockRect( 0, &d3dlr, 0, 0 );
    BYTE* pDstRow;
    pDstRow = (BYTE*)d3dlr.pBits;
    WORD* pDst16;
    BYTE bAlpha; // 4-bit measure of pixel intensity
    DWORD x, y;

    for( y=0; y < m_Texture->GetYl(); y++ )
    {
        pDst16 = (WORD*)pDstRow;
        for( x=0; x < m_Texture->GetXl(); x++ )
        {
			//*pDst16++ = rand()%0xffff;
			
            bAlpha = (BYTE)((pBitmapBits[m_Texture->GetXl()*y + x] & 0xff) >> 4);
            if (bAlpha > 0)
            {
                *pDst16++ = (WORD) ((bAlpha << 12) | 0x0fff);
			//	wlog("adfadfasdf");
            }
            else
            {
                *pDst16++ = 0x0000;
            }
        }
        pDstRow += d3dlr.Pitch;
    }

    // Done updating texture, so clean up used objects
    m_Texture->GetTextureSurface()->UnlockRect(0);

	//D3DXSaveTextureToFile("a.jpg", D3DXIFF_JPG , m_Texture->GetSurface() ,NULL);

    SelectObject( hDC, hbmOld );
    DeleteObject( hbmBitmap );
    DeleteObject( hFont );
    DeleteDC( hDC );
}