#include "pch.hpp"
//==========================================================================//
// File:	 LocalizationManager.cpp										//
// Project:  GameOS					                                        //
// Contents: Localizer routines												//
//--------------------------------------------------------------------------//
// Date      Who    Modification                                            //
//																			//
//																			//
//--------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//
#include "LocalizationManager.hpp"
#include "Font3D_DBCS_Storage.hpp"
#include "Windows.hpp"
#include "locale.h"
#include "mbctype.h"

// for memory tracking
extern HGOSHEAP DefaultHeap;

// this is a list of all the GDI text current printed to the screen (via panes).
extern gos_DBCS * g_pDBCSList;

// IME-related variables;
extern float FontSize;
extern HGOSFONT3D FontHandle;
extern DWORD gLocalize;

// this locals the locale, the default GDI font, as well as any strings that
// GameOS may have to send to the user.
DWORD gLanguageDLL = NULL;

//
// 0=Normal character, 1=DBCS lead byte in current locale
//
BYTE LeadByteTable[256];



// This is the list of resources that have been loaded by the user.
hResource*	pResources;

char gApplicationLocale[MAX_PATH];
char gDefaultGDIFont[MAX_PATH];
LCID gLangID = 0x409;

int WINAPI SafeLoadString(HINSTANCE hinst, UINT uId, LPSTR pszBuffer, int nBufferMax);
//
//
//
void InitLocalization()
{
	pResources=NULL;
	g_pDBCSList=NULL;
	//
	//
	// Open the GameOS Resource.dll and check the GOS_LOCALE string in the string table
	// If Language.dll isn't found, set the locale the English (United States)
	//
	gLanguageDLL = gos_OpenResourceDLL("Language.dll");

	// The line above will have failed if the file wasn't there.
	strncpy(gApplicationLocale, gos_GetResourceString( gLanguageDLL, GOS_STR_LOCALE ), MAX_PATH-1 );

	//
	// Set all the the language "formatting"
	// to be in the resource's locale (LC_ALL).
	//
	setlocale(LC_ALL, gApplicationLocale);
	if (strcmp(gApplicationLocale, "English") == 0 && GetACP() != 1252)
	{
		char szCodePage[16];
		sprintf(szCodePage, ".%d", GetACP());
		setlocale(LC_CTYPE, szCodePage);
	}
	strncpy(gDefaultGDIFont, gos_GetResourceString( gLanguageDLL, GOS_STR_DEFAULTGDIFONT ), MAX_PATH-1 );
	gLangID = atoi( gos_GetResourceString( gLanguageDLL, GOS_STR_LOCALE_LANGID) );

	for( int t0=0; t0<256; t0++ )
		LeadByteTable[t0]=(BYTE)(isleadbyte(t0) ? 1 : 0);
}

//
// Unload all libraries loaded
//
void DestroyLocalization()
{
	if (gLanguageDLL)
	{
		gos_CloseResourceDLL( gLanguageDLL );
		gLanguageDLL = 0;
	}

	while( pResources )
	{
		hResource*	pNext=pResources->pNext;
		FreeLibrary( pResources->hInstance );
		gos_Free( pResources );
		pResources=pNext;
	}

	gos_DBCS * pTmp = g_pDBCSList;
	gos_DBCS * pNext = NULL;
	while (pTmp)
	{
		pNext = pTmp->m_pNext;
		delete pTmp;
		pTmp = pNext;
	}
	g_pDBCSList = 0;
}


//
// Opens a DLL and returns a handle
//
DWORD __stdcall gos_OpenResourceDLL( const char* FileName )
{
	char search[MAX_PATH];
	hResource* pThis=pResources;

	while( pThis )
	{
		if( 0==stricmp( pThis->Name, FileName ) )
		{
			pThis->ReferenceCount++;
			return (DWORD)pThis;
		}
		pThis=pThis->pNext;
	}

	pThis=(hResource*)gos_Malloc(sizeof(hResource), DefaultHeap);

	strcpy(search, AssetsDirectory1);
	strcat(search, "\\");
	strcat(search, FileName);
	pThis->hInstance=LoadLibrary( search );

	if( NULL==pThis->hInstance )
	{
		if (strcmp("Language.dll", FileName) == 0)
		{
			strcpy(search, AssetsDirectory2);
			strcat(search, "\\Language.dll");
			pThis->hInstance=LoadLibrary(search);
			if (pThis->hInstance == NULL)
			{
				strcpy(search, AssetsDirectory1);
				strcat(search, "\\Assets\\Binaries\\Language.dll");
				pThis->hInstance = LoadLibrary(search);
				if (pThis->hInstance == NULL)
				{
					char Path[MAX_PATH*2];
					sprintf( Path, "Cannot find Language.dll in %s\\assets\\binaries or %s, this file is required",AssetsDirectory1,AssetsDirectory2 );
					MessageBox( hWindow, Path, ApplicationName, MB_OK | MB_ICONEXCLAMATION );
					status=AfterExit;
					exit(1);
				}
			}

			pThis->ReferenceCount=1;
			if( strlen(search)>255 )
				STOP(( "Name too long '%s'", search ));
			strcpy( pThis->Name, search );
			pThis->pNext=pResources;
			pResources=pThis;
			return (DWORD)pThis;
		}
		else
		{
			STOP(( "Could not find '%s'",FileName ));
			return 0;
		}
	}

	pThis->ReferenceCount=1;

	if( strlen(FileName)>255 )
		STOP(( "Name too long '%s'",FileName ));

	strcpy( pThis->Name, FileName );

	pThis->pNext=pResources;

	pResources=pThis;

	return (DWORD)pThis;
}

//
// Use to close a resource DLL
//
void __stdcall gos_CloseResourceDLL( DWORD Handle )
{
	hResource* pLast=0;
	hResource* pThis=pResources;

	while( pThis )
	{
		if( Handle==(DWORD)pThis )
		{
			pThis->ReferenceCount--;
	
			if( 0==pThis->ReferenceCount )
			{
				FreeLibrary( pThis->hInstance );
				if( pLast )
				{
					pLast->pNext=pThis->pNext;
				}
				else
				{
					pResources=pThis->pNext;
				}
				gos_Free( pThis );
			}
			return;

		}
		pLast=pThis;
		pThis=pThis->pNext;
	}
	STOP(( "Unknown handle 0x%x",Handle ));
}


//
// Returns a string from a resource (or a place holder if string not defined)
//
char* __stdcall gos_GetResourceString( DWORD Handle, DWORD Id )
{
	gosASSERT( Handle );

	static char Buffer[10][4096];
	static int iBufferNum=-1;

	iBufferNum++;
	if (iBufferNum == (sizeof(Buffer)/sizeof(Buffer[0])))
		iBufferNum = 0;

	hResource* pResource=(hResource*)Handle;
	
	if( gEnableLoc )
	{
		DWORD Time=((DWORD)(gos_GetHiResTime()*2))&1;
	
		if( 1==gEnableLoc || (2==gEnableLoc && Time) )
		{
			sprintf( Buffer[iBufferNum], "~%s:%d~", pResource->Name, Id );
			return Buffer[iBufferNum];
		}
	}

	int result=SafeLoadString( pResource->hInstance, Id, Buffer[iBufferNum], sizeof(Buffer[iBufferNum]) );

	if( 0==result )
	{
		sprintf( Buffer[iBufferNum], "%s:%d Not defined", pResource->Name, Id );
	}
	else
	{
		if( gLocalize )
		{
			int padding=result/3;
			memset( Buffer[iBufferNum], '*', padding );
			SafeLoadString( pResource->hInstance, Id, Buffer[iBufferNum]+padding, sizeof(Buffer[iBufferNum])-padding );
		}
	}
	
	return Buffer[iBufferNum];
}


//
// Returns a string from a resource for scripts (or a place holder if string not defined)
//
char* __stdcall gos_GetScriptResourceString( DWORD Handle, DWORD Id )
{
	gosASSERT( Handle );

	static char Buffer[10][4096];
	static int iBufferNum=-1;

	iBufferNum++;
	if (iBufferNum == (sizeof(Buffer)/sizeof(Buffer[0])))
		iBufferNum = 0;

	hResource* pResource=(hResource*)Handle;
	
	if( gEnableLoc )
	{
		DWORD Time=((DWORD)(gos_GetHiResTime()*2))&1;
	
		if( 1==gEnableLoc || (2==gEnableLoc && Time) )
		{
			sprintf( Buffer[iBufferNum], "~%s:%d~", pResource->Name, Id );
			return Buffer[iBufferNum];
		}
	}

	int result=SafeLoadString( pResource->hInstance, Id, Buffer[iBufferNum], sizeof(Buffer[iBufferNum]) );

	if( 0==result )
	{
		sprintf( Buffer[iBufferNum], "%s:%d Not defined", pResource->Name, Id );
	}
	else
	{
		if( gLocalize )
		{
			int padding=result/3;
			memset( Buffer[iBufferNum], '*', padding );
			SafeLoadString( pResource->hInstance, Id, Buffer[iBufferNum]+padding, sizeof(Buffer[iBufferNum])-padding );
		}
	}
	
	return Buffer[iBufferNum];
}



//
// Returns a block of memory from a resource (or a NULL if not defined)
//
void* __stdcall gos_GetResourceData( DWORD Handle, const char* rType, DWORD id, size_t* size )
{
	gosASSERT( Handle );
	gosASSERT( rType );

	hResource* pResource=(hResource*)Handle;

	HRSRC hrsrc=FindResource( pResource->hInstance, MAKEINTRESOURCE(id), rType );

	if( 0==hrsrc )
	{
		if (size)
			*size = 0;
		return NULL;
	}

	//
	// How long is the resource in bytes?
	//
	size_t rSize = SizeofResource( pResource->hInstance, hrsrc );
	
	//
	// if client wants to know the size, return it in the 3rd parameter.
	//
	if (size)
		*size = rSize;

	//
	// Load the resource and copy it into a freshly malloc'd area
	// of memory. Note that the alloc will be assigned to the
	// currently active heap.
	//
	char * memPtr = (char *) LoadResource( pResource->hInstance, hrsrc );
	char * dst = (char *) gos_Malloc(rSize);
	memcpy(dst, memPtr, rSize);

	//
	// Unload the dll copy...
	// We had to copy the data in case the user shuts down the resource dll
	//
	FreeResource(hrsrc);

	return dst;
}

void LocalizationSetPurgeFlags()
{
	gos_DBCS * pTmp = g_pDBCSList;
	while (pTmp)
	{
		pTmp->m_bRendered = false;
		pTmp = pTmp->m_pNext;
	}
}

DWORD TTFCached=0;
DWORD TTFDiscarded=0;
DWORD TTFUsed=0;
DWORD TTFMemory=0;
DWORD TTF256=0;
DWORD TTF128=0;
DWORD TTF64=0;
DWORD TTF32=0;
DWORD TTF16=0;


void LocalizationPurge()
{
	TTFCached=0;
	TTFUsed=0;
	TTFMemory=0;
	TTF256=0;
	TTF128=0;
	TTF64=0;
	TTF32=0;
	TTF16=0;
	TTFDiscarded=0;

	gos_DBCS * pTmp = g_pDBCSList;
	gos_DBCS * pNext = NULL;
	while (pTmp)
	{
		pNext = pTmp->m_pNext;
		if ((pTmp->m_bRendered == false) && (!pTmp->RefCount ()))
		{
			delete pTmp;
			TTFDiscarded++;
		}
		else
		{
			#ifdef LAB_ONLY
			TTFCached++;

			paneTextures* pThis=pTmp->m_pTextures;
			
			while( pThis )
			{
				TTFUsed++;

				TTFMemory+=pThis->Size*pThis->Size*2;

				if( pThis->Size==256 )
					TTF256++;
				if( pThis->Size==128 )
					TTF128++;
				if( pThis->Size==64 )
					TTF64++;
				if( pThis->Size==32 )
					TTF32++;
				if( pThis->Size==16 )
					TTF16++;

				pThis=pThis->pNext;
			}
			#endif
		}
		pTmp = pNext;
	}

	TTFMemory>>=10;
}

//
// Returns a localized string describing a date as either short form "02/03/2000" or Verbose form "Wednesday, May 3rd, 1999"
//
// If the Year, Month and Day are -1 the current time is used
//
char* __stdcall gos_GetFormattedDate( bool Verbose, WORD Year, WORD Month, WORD Day )
{
	static Buffer[128];
	SYSTEMTIME sysTime;

	if( Year==(WORD)-1 && Month==(WORD)-1 && Day==(WORD)-1 )
	{
		GetLocalTime( &sysTime);
	}
	else
	{
		memset( &sysTime, 0, sizeof(sysTime) );
		sysTime.wYear=Year;
		sysTime.wMonth=Month;
		sysTime.wDay=Day;
	}

	//
	// Get the format using the language ID
	//
	GetDateFormat( gLangID, Verbose ? DATE_LONGDATE : 0, &sysTime, NULL, (char*)Buffer, sizeof(Buffer) );

	return (char*)Buffer;
}

void __stdcall gos_GetUnformattedDate(  WORD *Year, WORD *Month, WORD *Day )
{
	
	SYSTEMTIME sysTime;

	GetLocalTime( &sysTime);

	*Year = sysTime.wYear;
	*Month = sysTime.wMonth;
	*Day = sysTime.wDay;

}

//
// Returns a localized string describing a time "11:45:32 PM"
//
// If the Hours, Minutes and Seconds are -1 the current time is used
//
char* __stdcall gos_GetFormattedTime( WORD Hour, WORD Minute, WORD Second )
{
	static Buffer[64];
	SYSTEMTIME sysTime;

	if( Hour==(WORD)-1 && Minute==(WORD)-1 && Second==(WORD)-1 )
	{
		GetLocalTime( &sysTime );
	}
	else
	{
		memset( &sysTime, 0, sizeof(sysTime) );
		sysTime.wHour=Hour;
		sysTime.wMinute=Minute;
		sysTime.wSecond=Second;
	}
	
	//
	// Get the format using the language ID
	//
	GetTimeFormat( gLangID, 0, &sysTime, NULL, (char *) Buffer, 32 );

	return (char*)Buffer;
}

char* __stdcall gos_GetFormattedNumber( char * fmt, ... )
{
	static char Buffer[64];
	static char Buffer2[64];
#ifdef _DEBUG
	gosASSERT(fmt);
#endif
	void * ptr = (void *) (&fmt+1);
	_vsnprintf(Buffer, sizeof(Buffer), fmt, (char *) ptr);
	Buffer[sizeof(Buffer)-1]=0;

	bool isDecimal = false;
	if (_mbschr((const unsigned char *) Buffer,'.'))
		isDecimal = true;



	
	GetNumberFormat( gLangID, 0, Buffer, 0, Buffer2, 63);

	if (!isDecimal)
	{
		char * itr = Buffer2 + strlen(Buffer2);
		while (itr != Buffer2)
		{
			if (*itr <= '0' || *itr >= '9')
				itr--;
			else
			{
				// for integers, don't display .00 at end...
				*itr = 0;
				break;
			}
				
		}
	}
	return Buffer2;

}

//
// For LoadStringA() bugs in Japanese, Chinese, and Korean Windows.
// See MSDN "Q140452"
//
// Created by Kazuyuki Shibuya
// Reviewed by Yutaka Hasegawa
// Adapted, added to GameOS by Jim Reichert
//
// o In Win95, LoadStringA() has buffer overrun problem when resource string has DBCS character.
//   The function treats the 4th paramter nBufferMax as the number of characters, but it's actually
//   the number of bytes.
// o In Win95 OSR2 or later, LoadStringA() may replace DBCS trail byte with NUL terminator when
//   resource string is too long for the buffer.
// o In NT4, LoadStringA() doesn't put NUL terminator when resource string is too long for the
//   buffer.
//
int WINAPI SafeLoadString(HINSTANCE hinst, UINT uId, LPSTR pszBuffer, int nBufferMax)
{
	int iRc;

	SetLastError(ERROR_NOT_ENOUGH_MEMORY);

	if (nBufferMax<=0 || !pszBuffer)
		return 0;

	pszBuffer[0] = '\0';

	SetLastError(ERROR_SUCCESS);
	iRc = LoadString(hinst, uId, pszBuffer, nBufferMax);
	if (!iRc)
	{
		//
		// o NT's LoadString doesn't set LastError when the length of loaded string is 0.
		//   i.e. LastError doesn't change after the function call.
		// o Win9x's LoadString never sets LastError.
		//
		DWORD lastError = GetLastError();
		SetLastError(lastError);
		return iRc;
	}

	pszBuffer[nBufferMax - 1] = '\0';

	char * psz = pszBuffer;
	while (*psz)
	{
		if (LeadByteTable[(unsigned char)*psz])
		{
			if (psz[1] == '\0')
			{
				*psz = '\0';
				break;
			}
			psz++;
		}
		psz++;
	}
	SetLastError(ERROR_SUCCESS);
	return psz - pszBuffer;
}


						




