#include "pch.hpp"
//===========================================================================//
// File:	 Pane.cpp														 //
// Project:  GOSScript				                                         //
// Contents: Representation of bitmap in 3D environment						 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 07/31/98  JTR    Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1997-1998, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include <math.h>

#include "Debugger.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "3DRasterizer.hpp"
#include "Texture Manager.hpp"
#include "Font3D_DBCS_Storage.hpp"

extern HGOSHEAP DefaultHeap;
extern HGOSHEAP Heap_Font;


bool dbcsShowFastPane;						// Show where fast PANE's are being used
bool dbcsShowFastRect;						// Show where fast RECT's are being used
bool dbcsEnableFastPath;						// When true enable fast paths in blade
extern DWORD FontColor;

gos_DBCS * g_pDBCSList = NULL;

gos_DBCS::gos_DBCS(const char * msg, int w, int h, bool colorChange)
{
	m_RefCount = 0;
	m_height = h;
	m_width = w;
	m_FontSize = 1.0f;
	m_FontBold = false;
	m_FontColor = FontColor;
	embeddedColorChange = colorChange;
	_mbscpy( (unsigned char *) m_msg, (const unsigned char *) msg);

	m_pTextures = NULL;
	m_pListOfQuads = NULL;

	if (w==0 || h==0)
	{
			if (colorChange)
				m_color = 0xffffffff;
			else
				m_color = FontColor;

			m_surf = 0;
	}
	else
	{
		m_surf = new(Heap_Font) DBCSSurface(w,h);

		MakeQuads();

		if (colorChange)
			Color(0xffffffff);
		else
			Color(FontColor);

		Update();

	}

	m_pNext = g_pDBCSList;
	if (g_pDBCSList)
		g_pDBCSList->m_pPrev = this;
	m_pPrev = NULL;
	g_pDBCSList = this;
}


gos_DBCS::~gos_DBCS()
{
	// kill all associated quads
	quad * this_q = m_pListOfQuads;
	while(this_q)
	{
		quad * tmp = this_q;
		this_q = this_q->pNext;
		gos_Free(tmp);
	}

	paneTextures* this_texture = m_pTextures;
	while( this_texture )
	{
		paneTextures* tmp = this_texture;
		this_texture = this_texture->pNext;
		gos_DestroyTexture(tmp->Handle);
		gos_Free(tmp);
	}
	
	if (m_surf)
	{
		delete m_surf;
		m_surf = 0;
	}


	// remove this from the list of DBCS's
	if (this == g_pDBCSList)
	{
		g_pDBCSList = this->m_pNext;
	}
	else
	{
		this->m_pPrev->m_pNext = 
			this->m_pNext;
	}
	if (this->m_pNext)
		this->m_pNext->m_pPrev = this->m_pPrev;
}







//
//
//






void gos_DBCS::Render()
{

//
// Let the DBCS manager know that this was rendered this loop
//
	m_bRendered = true;
//
// Save renderstates
//
	gos_PushRenderStates();
//
// Set them to script defaults
//
	gos_SetRenderState( gos_State_AlphaTest, 1 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_WireframeMode, 0 );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_Fog, 0 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );	
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );

	float TOffset=gos_GetMachineInformation(gos_Info_TextureOffset) ? 0.0f : 0.5f;
	
	if( NeedClip( m_FontX-TOffset, m_FontY-TOffset) ) 
	{
		gos_SetRenderState( gos_State_Clipping, 2 );	// This uses guard band clipping (-2048 to +2047 is valid) - or crash could happen
	}

	quad * itr = m_pListOfQuads;
	
	DWORD lasttexture = 0xffffffff;

	while(itr)
	{
		if (lasttexture != itr->texture )
		{
			gos_SetRenderState( gos_State_Texture, itr->texture );
			lasttexture = itr->texture;
		}
		gos_DrawQuads(itr->v,4);
		itr = itr->pNext;
	}

//
// Restore renderstates
//
	gos_PopRenderStates();
}



void gos_DBCS::Color(DWORD color)
{
	quad * itr = m_pListOfQuads;

	m_color = color;

	while(itr)
	{
		itr->v[0].argb = color;
		itr->v[1].argb = color;
		itr->v[2].argb = color;
		itr->v[3].argb = color;

		itr = itr->pNext;
	}
}

void gos_DBCS::Update()
{
	TEXTUREPTR ptr;
	memset(&ptr,0,sizeof(TEXTUREPTR));
	int count = 0;

	DWORD lastHandle = 0xffffffff;
	char *sdata = 0;
	char *tdata = 0;

	long ty;

	DDSURFACEDESC2 ddsd;
	ddsd.dwSize = sizeof(ddsd);
	wLock(m_surf->m_lpdds7,NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );
	char * surfptr = (char *) ddsd.lpSurface;
	unsigned long sp = (unsigned long) ddsd.lPitch;

	quad * rect = m_pListOfQuads;

	while(rect)
	{
		if (rect->texture != lastHandle)
		{
			if (lastHandle != 0xffffffff)
			{
				gos_UnLockTexture(lastHandle);
			}
			lastHandle = rect->texture;
			gos_LockTexture(lastHandle,  0, 0, &ptr);
		}
		sdata = surfptr;
		sdata += (int) (rect->v[0].y*sp);
		tdata = (char *) ptr.pTexture;

		long top = ( long) (((float)rect->v[0].v*ptr.Pitch));
		long bottom = ( long) (((float)rect->v[2].v*ptr.Pitch)); 

		long left = ( long) (((float)rect->v[0].u*ptr.Pitch));
		long right = ( long) (((float)rect->v[2].u*ptr.Pitch));

		gosASSERT( right >= left);
		gosASSERT( bottom >= top);

		int tdy = ptr.Pitch*4;
		int sdy = ddsd.lPitch;
		tdata += top*tdy;

		int off = (int) rect->v[0].x;
		int tx;
		for(ty=top; ty < bottom; ty++)
		{
			for(tx = 0; tx < (right-left); tx ++)
			{
				if (!  *((DWORD*)sdata+((tx+off))) )
				{
					// key out
					*((DWORD*)tdata+((tx+left))) = 0x00;
				}
				else
				{
					if (embeddedColorChange)
					{
						// key in! it was printed in white so RGB's should be the same.
						// take the R channel and shift it into the A position, then make everything else white
						// we'll use alphainvalpha w/vertex colors to color
						*((DWORD*)tdata+((tx+left))) = 0xff000000 + *((DWORD*)sdata+((tx+off)));
					}
					else
					{	DWORD val = (DWORD) ((unsigned char) *((DWORD*)sdata+((tx+off))));
						if (val == 0xff)
							*((DWORD*)tdata+((tx+left))) = 0xffffffff; 
						else
							*((DWORD*)tdata+((tx+left))) = (((unsigned char) (val*.75f))<<24) + 0xffffff; //*((DWORD*)sdata+((tx+off)));
					}
				}
			}
			tdata += tdy;
			sdata += sdy;
		}
		count++;
		rect = rect->pNext;
	}
	if( lastHandle!=0xffffffff )
		gos_UnLockTexture(lastHandle);

	wUnlock(m_surf->m_lpdds7, NULL);

}

bool gos_DBCS::NeedClip(float x, float y)
{
	if (m_surf)
	{
		if( x + m_width > Environment.screenWidth || y + m_height + m_surf->m_offsety > Environment.screenHeight || x < 0 || y < 0)
			return true;
		else
			return false;
	}
	else
		return false;
}












//
// Add's a texture to the current pane
//
DWORD gos_DBCS::AddTexture( DWORD Size )
{
	DWORD Handle;
	
	Handle = gos_NewEmptyTexture( gos_Texture_Alpha, "DBCS String", Size, gosHint_DisableMipmap | gosHint_DontShrink );

	paneTextures* pTexture = (paneTextures*)gos_Malloc( sizeof(paneTextures), Heap_Font );
	pTexture->Size=Size;
	pTexture->Handle=Handle;
	pTexture->pNext=m_pTextures;
	m_pTextures=pTexture;

	return Handle;
}

//
// Add's a quad to the list for the current pane
//
void gos_DBCS::AddQuad( DWORD X, DWORD Y, DWORD Width, DWORD Height, DWORD Texture, DWORD U, DWORD V, DWORD TextureSize )
{
//
// Create a new quad
//
	quad* rect = (quad *) gos_Malloc(sizeof(quad), Heap_Font);
//
// Fill in quad information
//
	rect->texture=Texture;

	rect->v[0].x=(float)X;
	rect->v[0].y=(float)Y;

	rect->v[1].x=(float)X;
	rect->v[1].y=(float)(Y+Height);

	rect->v[2].x=(float)(X+Width);
	rect->v[2].y=(float)(Y+Height);

	rect->v[3].x=(float)(X+Width);
	rect->v[3].y=(float)Y;
//
// Now add texel coordinates
//
	float Texel=1.0f/TextureSize;
	
	rect->v[0].u=U*Texel;
	rect->v[0].v=V*Texel;

	rect->v[1].u=U*Texel;
	rect->v[1].v=(V+Height)*Texel;

	rect->v[2].u=(U+Width)*Texel;
	rect->v[2].v=(V+Height)*Texel;

	rect->v[3].u=(U+Width)*Texel;
	rect->v[3].v=V*Texel;
//
// Clear all unused fields
//
	rect->v[0].z=0;
	rect->v[0].z    = rect->v[1].z    = rect->v[2].z    = rect->v[3].z		= 0;
	rect->v[0].argb = rect->v[1].argb = rect->v[2].argb = rect->v[3].argb	= 0xffffffff;
	rect->v[0].frgb	= rect->v[1].frgb = rect->v[2].frgb	= rect->v[3].frgb	= 0x0;
	rect->v[0].rhw	= rect->v[1].rhw  =	rect->v[2].rhw	= rect->v[3].rhw	= 1.0f;
//
// Add to chain of quads
//
	rect->pNext=m_pListOfQuads;
	m_pListOfQuads = rect;
}















//
//
// Given a surface, this routine breaks it into QUADs in as many textures as are required.
//
// It is important that no T junctions are created and the minimum number of vertices are used.
//
//
void gos_DBCS::MakeQuads()
{
//
// Round up texture size to closest multiple of 64
//
	unsigned int fakewidth = m_width;
	unsigned int fakeheight = m_height;
	if (m_width&0x0f)
		fakewidth +=  + (16- (m_width&0x0f));
	if (m_height&0x0f)
		fakeheight +=  + (16- (m_height&0x0f));
//
// Find minimum width/height of blocks
//
	m_latticeX = 0;
	for( int i=8; i > 3 && 0==m_latticeX; i-- )
	{
		if (fakeheight % (int) pow(2,i) == 0)
		{
			m_latticeY = (int) pow(2,i);
			for(int j=8; j > 3; j--)
			{
				if (fakewidth % (int) pow(2,j) == 0)
				{
					m_latticeX = (int) pow(2,j);
					break;
				}
			}
		}
	}
//
// Do x,y's
//
	for( unsigned long y = 0; y < fakeheight; y += m_latticeY)
	{
		for( unsigned long x = 0; x < fakewidth; x += m_latticeX)
		{
			if (x > m_width || y > m_height)
				continue;
			quad* rect = (quad *) gos_Malloc(sizeof(quad), Heap_Font);
//
// Add quads to the END of the list (previously was the start)
//
			if (m_pListOfQuads == NULL)
			{
				m_pListOfQuads = rect;
			}
			else
			{
				quad* pQuad=m_pListOfQuads;
				quad* pFound=m_pListOfQuads;

				while( pQuad )
				{
					pFound=pQuad;
					pQuad=pQuad->pNext;	
				}		
				
				pFound->pNext=rect;
			}

			rect->pNext = NULL;

			rect->texture = 0;

			rect->v[0].z = 0;
			rect->v[1].z = 0;
			rect->v[2].z = 0;
			rect->v[3].z = 0;

			rect->v[0].x = (float) x;
			rect->v[0].y = (float) y;

			rect->v[1].x = (float) (x + (float)m_latticeX);
			rect->v[1].y = (float) y;

			rect->v[2].x = (float) (x + (float)m_latticeX);
			rect->v[2].y = (float) (y + (float)m_latticeY);

			rect->v[3].x = (float) x;
			rect->v[3].y = (float) (y + (float)m_latticeY);
	
		}
	}
//
// Do u,v's
//
	int 
		offX = 0,
		offY = 0;

	int 
		spaceLeft = m_height * m_width;
	
	int 
		textureSize = 0;

	DWORD 
		composedTexture = 0;

	quad* rect = m_pListOfQuads;
	quad* prev = NULL;
	
	while( rect )
	{
		// do we need to create a new texture?
		if (offY == textureSize)
		{
			offX = 0;
			// if so, how big?
			int size = (m_latticeX > m_latticeY) ? m_latticeX : m_latticeY;
			int tmp = size;

			for(int j=8; pow(2,j) >= size; j--)
			{
				tmp = (int) pow(2,j);
				if (spaceLeft - (tmp*tmp) >= 0) 
					break;
			}

			size = tmp;

			spaceLeft -= (size*size);
			textureSize = size;

			composedTexture=AddTexture( textureSize );
			
			offX = offY = 0;
		}
		// texture ptr and offsets ready, map it!

		float tsize=1.0f/textureSize;
	
		float u1 = (offX) * tsize;
		float v1 = (offY) * tsize;
		float u2 = (offX+m_latticeX) * tsize;
		float v2 = (offY+m_latticeY) * tsize;
	  
		if ((int)rect->v[2].x > (int) m_width)
		{

			rect->v[1].x = (float) m_width;
			rect->v[2].x = (float) m_width;
			u2 = u1 + ((float)(rect->v[2].x - rect->v[0].x) * tsize);
		}
		
		if ((int)rect->v[2].y > (int) m_height)
		{
			rect->v[2].y = (float) m_height;
			rect->v[3].y = (float) m_height;
			v2 = v1 + ((float)(rect->v[2].y - rect->v[0].y) * tsize);
		}

		gosASSERT(u1 >= 0.0f && u1 <= 1.0f);
		gosASSERT(u2 >= 0.0f && u2 <= 1.0f);

		gosASSERT(v1 >= 0.0f && v1 <= 1.0f);
		gosASSERT(v2 >= 0.0f && v2 <= 1.0f);

		rect->texture = composedTexture;
		rect->v[0].u = u1;
		rect->v[0].v = v1;

		rect->v[1].u = u2;
		rect->v[1].v = v1;
		
		rect->v[2].u = u2;
		rect->v[2].v = v2;
		
		rect->v[3].u = u1;
		rect->v[3].v = v2;

		rect->v[0].argb = rect->v[1].argb = rect->v[2].argb = rect->v[3].argb	= 0xffffffff;
		rect->v[0].frgb	= rect->v[1].frgb = rect->v[2].frgb	= rect->v[3].frgb	= 0x0;
		rect->v[0].rhw	= rect->v[1].rhw  =	rect->v[2].rhw	= rect->v[3].rhw	= 1.0f;

		offX += m_latticeX;

		if ( offX >= textureSize )
		{
			offX = 0;
			offY += m_latticeY;
		}
		prev = rect;
		rect = rect->pNext;
	}
//
// Optimize it
//
	quad* pQuad=m_pListOfQuads;

	while( pQuad )
	{
//
// Is there a next quad?
//
		quad* pNext=pQuad->pNext;
		if( pNext )
		{
//
// Does it share the same texture?
//
			if( pQuad->texture==pNext->texture )
			{
//
// Is the right edge of the first quad touching the left edge of the next quad?
//
				if( pQuad->v[1].x==pNext->v[0].x &&
					pQuad->v[2].x==pNext->v[3].x &&
					pQuad->v[1].y==pNext->v[0].y &&
					pQuad->v[2].y==pNext->v[3].y &&
					pQuad->v[1].u==pNext->v[0].u &&
					pQuad->v[2].u==pNext->v[3].u &&
					pQuad->v[1].v==pNext->v[0].v &&
					pQuad->v[2].v==pNext->v[3].v )
				{
//
// If yes, delete the 2nd quad and make the first one bigger
//
					pQuad->v[1]=pNext->v[1];
					pQuad->v[2]=pNext->v[2];
					pQuad->pNext=pNext->pNext;
					gos_Free( pNext );
					pNext=pQuad;
				}
			}
		}
		pQuad=pNext;
	}
}

void gos_DBCS::ShiftCoordinates(float x, float y)
{
	float TOffset=gos_GetMachineInformation(gos_Info_TextureOffset) ? 0.0f : 0.5f;

	quad * itr = m_pListOfQuads;
	while(itr)
	{
		itr->v[0].x += -TOffset + x;
		itr->v[0].y += -TOffset + y + m_surf->m_offsety;
		itr->v[1].x += -TOffset + x;
		itr->v[1].y += -TOffset + y + m_surf->m_offsety;
		itr->v[2].x += -TOffset + x;
		itr->v[2].y += -TOffset + y + m_surf->m_offsety;
		itr->v[3].x += -TOffset + x;
		itr->v[3].y += -TOffset + y + m_surf->m_offsety;

		itr = itr->pNext;
	}
}


