/*
** Copyright(C) 1999 - 2002 Albert L. Faber
**  
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
**(at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
** 
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software 
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
*/

#include "StdAfx.h"
#include "Language.h"
#include "resource.h"
#include "Util.h"
#include "TextFile.h"
#include <locale.h>
#include <algorithm>


static int g_nReportMissing = QueryModuleDebugLevel( _T( "ReportMissingLangIDs" ) );

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

const CString CLanguage::m_strLangFileExt( _T( ".cdex.lang" ) );
const CString CLanguage::DEFAULT_LANG_SELECTION( _T( "english" ) );
const CString CLanguage::CODEPAGETAG( _T( "CODEPAGE=" ) );
const CString CLanguage::LANGTAG( _T( "LANGUAGE=" ) );
const CString CLanguage::SUBLANGTAG( _T( "SUBLANG=" ) );
const CString CLanguage::REVISIONTAG( _T( "REVISION=" ) );

CLanguage::CLanguage()
{
	m_strCurrentLanguage	= DEFAULT_LANG_SELECTION;
	m_dwCodePageID			= 1252;		// STANDARD CODE PAGE ANSI INTERNATIONAL
	m_dwSubLangID			= 1;		// ENGLISH
	m_dwLangID				= 9;		// ENGLISH_US


}

CLanguage::~CLanguage()
{
}


void CLanguage::ReportMissingID( int nID, const CString& strEnglish, int nType ) const
{
	if ( g_nReportMissing )
	{
		BOOL		bAdd = TRUE;
		CString		strWrite;
		CString		strRead;
		CStdioFile	cFile;

		switch ( nType )
		{
			case 0:
				strWrite.Format( _T( "#%04X# \"%s\"" ), nID, strEnglish );
			break;
			case 1:
				strWrite.Format( _T( "#M%04X# \"%s\"" ), nID, strEnglish );
			break;
			case 2:
				strWrite.Format( _T( "#D%08X# \"%s\"" ), nID, strEnglish );
			break;
		}

		EncodeTab( strWrite );

		if ( cFile.Open(	_T( "c:\\temp\\missing.txt" ),
							CFile::modeReadWrite | CFile::modeNoTruncate | CFile::modeCreate ) )
		{
			while ( cFile.ReadString( strRead ) )
			{
				if ( strWrite.Find( strRead ) >=0 )
				{
					bAdd = FALSE;
					break;
				}
			}
		}

		if ( bAdd )
		{
			cFile.WriteString( strWrite + _T( "\n" ) );
		}

		cFile.Close();
	}
}


CString	CLanguage::AsciiToUnicode( PCHAR lpszIn  )
{
	CString strRet;
#ifdef _UNICODE

	int nLen = strlen( lpszIn ) + 1;

	LPWSTR lpwConvert = new TCHAR[ nLen ];

	// convert ASCII string to WideCharacter string based on active code page
	MultiByteToWideChar(	m_dwCodePageID , 
							0,
							lpszIn,
							-1,
							lpwConvert,
							nLen  );

	strRet = lpwConvert;
	delete [] lpwConvert;

#else
	strRet = CString( lpszIn );
	#endif

	return strRet; 
}

CString	CLanguage::GetMainMenuItem( const DWORD nID, const DWORD nItem )
{
	CString strRet;

	strRet = GetSubMenuItem( nID );

	DWORD	dwIndex = 0;
	int		nStart  = 0;
	int		nStop   = strRet.GetLength();

	for ( dwIndex = 0; dwIndex < nItem; dwIndex++ )
	{
		nStart = strRet.Find( '#', nStart + 1 );

		if ( -1 == nStart )
		{
			break;
		}
	}

	if ( -1 != nStart )
	{
		nStop = strRet.Find( '#', nStart + 1 );

		if ( -1 == nStop )
		{
			nStop = strRet.GetLength();
		}
	}

	if ( nStop - nStart > 2 )
	{
		strRet = strRet.Mid( nStart + 1, nStop - nStart - 1 );
	}

	return strRet;
}

CString	CLanguage::GetSubMenuItem( const DWORD nID )
{
	CString strRet;
	DWORD	dwIndex;

	for ( dwIndex = 0; dwIndex < m_vMenus.size(); dwIndex++ )
	{
		if ( m_vMenus[ dwIndex ].nID == nID )
		{
			strRet = m_vMenus[ dwIndex ].strValue;
			break;
		}
	}

	return strRet;
}

CString	CLanguage::GetDialogString( const DWORD nID )
{
	CString strRet;

	DWORD	dwIndex;

	for ( dwIndex = 0; dwIndex < m_vDialogs.size(); dwIndex++ )
	{
		if ( m_vDialogs[ dwIndex ].nID == nID )
		{
			strRet = m_vDialogs[ dwIndex ].strValue;
			break;
		}
	}

	return strRet;
}

CString	CLanguage::GetString( const DWORD nID ) const
{
	CString strRet;

	DWORD	dwIndex;

	for ( dwIndex = 0; dwIndex < m_vStrings.size(); dwIndex++ )
	{
		if ( m_vStrings[ dwIndex ].nID == nID )
		{
			strRet = m_vStrings[ dwIndex ].strValue;
			break;
		}
	}

	if ( strRet.IsEmpty() )
	{
		strRet.LoadString( nID );
		ReportMissingID( nID, strRet, 0 );
	}

	return strRet;
}

const MENU_SEPARARTOR =	0;	// identifies the separator id
const MENU_POPUP = -1;		// identifies the popup id
const COMMON_IDMIN = 0;
const COMMON_IDMAX = 99;

void CLanguage::TranslateTab( CString& strModify )
{
	int nPos;
	
	while ( ( nPos = strModify.Find( _T( "\\t" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\t" ) + strModify.Right( strModify.GetLength() - nPos -2 );
	}
	while ( ( nPos = strModify.Find( _T( "\\n" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\n" ) + strModify.Right( strModify.GetLength() - nPos -2 );
	}
	while ( ( nPos = strModify.Find( _T( "\\r" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\r" ) + strModify.Right( strModify.GetLength() - nPos -2 );
	}

}

void CLanguage::EncodeTab( CString& strModify ) const
{
	int nPos;
	
	while ( ( nPos = strModify.Find( _T( "\t" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\\t" ) + strModify.Right( strModify.GetLength() - nPos -1 );
	}
	while ( ( nPos = strModify.Find( _T( "\n" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\\n" ) + strModify.Right( strModify.GetLength() - nPos -1 );
	}
	while ( ( nPos = strModify.Find( _T( "\r" ) ) ) >= 0 )
	{
		strModify = strModify.Left( nPos ) + _T( "\\r" ) + strModify.Right( strModify.GetLength() - nPos -1 );
	}
}

void CLanguage::ParseLanguageFile( const CString& strFileName )
{
	BOOL bResult		= TRUE;
	DWORD dwRevision	= 0;
	FILE* pFile			= NULL;

	USES_CONVERSION;


	pFile = fopen( W2A( m_strLanguageDir + _T("\\lang\\") + strFileName + m_strLangFileExt ) , "r" );

	if ( NULL != pFile )
	{
		CHAR lpszRead[ 1024 ];
		int nStart = 0;
		int nStop  = 0;

		CString strRead;

		memset( lpszRead, 0, sizeof( lpszRead ) );

		while ( fgets( lpszRead, sizeof( lpszRead ), pFile  ) )
		{
			bool         bHasNumber = false;
			bool         bIsMenu    = false;
			bool         bIsDialog  = false;
			bool	     bHasString = false;
			int			 nIndex = 0;
			CLangElement newElement;

			strRead = _T( "" );

			for ( nIndex = 0; nIndex< strlen( lpszRead ); nIndex++ )
			{
				if (	( lpszRead[ nIndex ] != 0x0A ) &&
						( lpszRead[ nIndex ] != 0x0D ) )
				{
					strRead +=  (TCHAR)lpszRead[ nIndex ];
				}
			}

			TranslateTab( strRead );

			nStart = strRead.Find( _T( '#' ) );

			if ( nStart >= 0 )
			{
				if ( ( nStart + 1 ) == strRead.Find( _T( 'M' ) ) )
				{
					nStart++;
					bIsMenu = TRUE;
				}
				else
				if ( ( nStart + 1 ) == strRead.Find( _T( 'D' ) ) )
				{
					nStart++;
					bIsDialog = TRUE;
				}


				nStop = strRead.Find( _T( '#' ), nStart + 1 );

				if ( nStop > 2 )
				{
					CString strID ( strRead.Mid( nStart + 1, nStop - nStart -1 ) );
					_stscanf( strID, _T( "%x" ), &newElement.nID );
					bHasNumber = true;
				}

			}
			else
			{
				if ( strRead.Find( CODEPAGETAG ) == 0 )
				{
					m_dwCodePageID =   _ttoi( strRead.Mid( CODEPAGETAG.GetLength() ) );
				}
				else if ( strRead.Find( LANGTAG ) == 0 )
				{
					m_dwLangID =   _ttoi( strRead.Mid( LANGTAG.GetLength() ) );
				}
				else if ( strRead.Find( SUBLANGTAG ) == 0 )
				{
					m_dwSubLangID =   _ttoi( strRead.Mid( SUBLANGTAG.GetLength() ) );
				}
				else if ( strRead.Find( REVISIONTAG ) == 0 )
				{
					int nPos;
					nPos = strRead.Find( _T( ",v " ) );
					if ( nPos >= 0 )
					{
						float fVersion = 1.0f;
						_stscanf( strRead.Mid( nPos+3,4 ), _T( "%f" ), &fVersion );
						dwRevision =   100 * ( fVersion + 0.005 );
					}

				}
			}

			nStart = strRead.Find( '"' );

			if ( nStart >= 0 )
			{
				nStop = strRead.Find( '"', nStart + 1 );

				if ( nStop > 2 )
				{
					lpszRead[ 0 ] = '\0';

					for ( nIndex = nStart + 1; nIndex < nStop; nIndex++)
					{
						lpszRead[ nIndex - nStart - 1] = strRead.GetAt( nIndex );
						lpszRead[ nIndex - nStart ] = '\0';
					}

					newElement.strValue = AsciiToUnicode( lpszRead );
					bHasString = true;
				}
			}

			if ( bHasString && bHasNumber )
			{
				if ( bIsMenu )
				{
					m_vMenus.push_back( newElement );
				}
				else if ( bIsDialog )
				{
					m_vDialogs.push_back( newElement );
				}
				else
				{
					m_vStrings.push_back( newElement );
				}
			}
			memset( lpszRead, 0, sizeof( lpszRead ) );	
		}

		fclose( pFile );
	}

#ifdef UNICODE
	TCHAR* pTest = _wsetlocale( LC_ALL, GetString( 99 ) );

#else
	TCHAR* pTest = setlocale( LC_ALL, GetString( 99 ) );

#endif
	SetThreadLocale( MAKELCID( m_dwLangID, m_dwSubLangID ) );
	setlocale( LC_NUMERIC, "English" );
}

CString CLanguage::GetRevisionLevel( const CString& strFileName )
{
	BOOL		bResult     = TRUE;
	CString		strRet = _T( "0.0" );
	CStdioFile	cLangFile;

	bResult = cLangFile.Open(	m_strLanguageDir + _T("\\lang\\") + strFileName + m_strLangFileExt,
								CFile::modeRead );

	if ( bResult )
	{
		int nStart = 0;
		int nStop  = 0;

		CString strRead;

		while ( cLangFile.ReadString( strRead ) )
		{
			TranslateTab( strRead );

			if ( 0 == strRead.Find( REVISIONTAG ) )
			{
				int nPos = strRead.Find( _T( ",v " ) );
				int nEndPos = strRead.Find( _T( " " ), nPos + 4 );

				if ( ( nPos >= 0 ) && ( nEndPos >= 0 ) )
				{
					strRet =strRead.Mid( nPos + 3, nEndPos - nPos - 3 );
					strRet.TrimLeft();
					strRet.TrimRight();
				}
				break;
			}	
		}
		cLangFile.Close();
	}
	return strRet;;
}

void CLanguage::SearchLanguageFiles( )
{
	CString		strSearchName;
	CFileFind	fileFind;
	BOOL		bFound = FALSE;

	// build the filename search string
    strSearchName.Format(	_T( "%s\\lang\\*%s" ),
							m_strLanguageDir,
							m_strLangFileExt );


	bFound = fileFind.FindFile( strSearchName );

	while ( bFound )
	{
		bFound = fileFind.FindNextFile();

		CString strFileName = fileFind.GetFileName();

		CString strLanguage = strFileName.Left( strFileName.GetLength() - m_strLangFileExt.GetLength() );

		m_vLangFiles.push_back( strLanguage );
	}
	sort( m_vLangFiles.begin(), m_vLangFiles.end() );
}


void CLanguage::Init( const CString& strLanguageDir, const CString& strLanguage )
{
	m_strLanguageDir = strLanguageDir;

	// remove all vector elements
	m_vLangFiles.clear();

	SearchLanguageFiles( );

	SetLanguage( strLanguage );
}


BOOL CLanguage::SetLanguage( const CString& strLanguage )
{
	BOOL	bReturn = TRUE;
	int		nIndex = 0;

	m_vMenus.clear();
	m_vStrings.clear();
	m_vDialogs.clear();

	m_strCurrentLanguage = DEFAULT_LANG_SELECTION;

	for ( nIndex = 0; nIndex < m_vLangFiles.size(); nIndex++ )
	{
		if ( 0 == m_vLangFiles[ nIndex ].CompareNoCase( strLanguage ) )
		{
			m_strCurrentLanguage = strLanguage;
			break;
		}
	}
	
	ParseLanguageFile( strLanguage );

	return bReturn;
}


CString CLanguage::GetLanguageString( const DWORD dwIndex ) const
{
	ASSERT( dwIndex < m_vLangFiles.size() );

	return m_vLangFiles[ dwIndex ];
}



BOOL CLanguage::TranslateMenu( CMenu* pMenu, const int nMenuID, BOOL bTopLevel )
{
	CMenu*			pChildMenu = NULL;
	static CString	strMissing;
	BOOL			bReturn = TRUE;
	static int		nPopUpIdx = 0;

	if ( bTopLevel )
	{
		nPopUpIdx = MAKELPARAM( 0, nMenuID );
		strMissing = _T( "" );
	}

	int i = pMenu->GetMenuItemCount();
	for( i = 0; i < pMenu->GetMenuItemCount(); i++ )
	{
		pChildMenu = pMenu->GetSubMenu( i );

		int nItemID = pMenu->GetMenuItemID( i );

		if ( MENU_SEPARARTOR != nItemID )
		{
			CString strLang;

			if ( bTopLevel )
			{
				strLang = GetMainMenuItem( nMenuID, i );
			}
			else
			{
				if ( MENU_POPUP != nItemID )
				{
					strLang = GetSubMenuItem( nItemID );
				}
				else
				{
					strLang = GetSubMenuItem( nPopUpIdx );
					nPopUpIdx++;
				}
			}
		
			if ( MENU_POPUP != nItemID )
			{
				if ( !strLang.IsEmpty() )
				{
					pMenu->ModifyMenu( nItemID, MF_STRING, nItemID, strLang );
				}
				else
				{
					CString strTmp;
					pMenu->GetMenuString( nItemID, strTmp, MF_BYCOMMAND );
					ReportMissingID( nItemID, strTmp, 1 );
				}
			}
			else
			{
				int nPosition = UINT( pChildMenu->m_hMenu );

				if ( !strLang.IsEmpty() )
				{
					int nPosition = UINT( pChildMenu->m_hMenu );

					pMenu->ModifyMenu(	UINT( pChildMenu->m_hMenu ),
										MF_STRING | MF_POPUP,
										UINT( pChildMenu->m_hMenu ),
										strLang );
				}
				else
				{

					if ( bTopLevel )
					{
						CString strTmp;
						pMenu->GetMenuString( i, strTmp, MF_BYPOSITION );
						strMissing += _T( "#" ) + strTmp;
					}
					else
					{
						CString strTmp;
						pMenu->GetMenuString( 0, strTmp, MF_BYPOSITION );
						ReportMissingID( nItemID, strTmp, 1 );
					}
				}

				TranslateMenu( pMenu->GetSubMenu(i), -1, FALSE );
			}
		}
	}

	if ( bTopLevel )
	{
		if ( !strMissing.IsEmpty() )
		{
			ReportMissingID( nMenuID, strMissing + _T( "#" ), 1 );
		}	
	}

	return bReturn;
}

BOOL CLanguage::InitDialogStrings( CDialog* pDialog, long lSection )
{
	short nCtrlID;
	CString sText;
	CWnd* pWndCtrl;
	DWORD	nIndex = 0;

	CString strLang = GetDialogString( MAKELPARAM( 0, lSection ) );

	if( !strLang.IsEmpty() )
	{
		pDialog->SetWindowText( strLang );
	}
	else
	{
		CString strMissing;
		pDialog->GetWindowText( strMissing );
		ReportMissingID( MAKELPARAM( 0, lSection ), strMissing, 2 );
	}

	// test if the dialog has child windows
	if( pWndCtrl = pDialog->GetWindow( GW_CHILD | GW_HWNDFIRST ) )
	{
		// lool through all controls (windows) and replace text
		do
		{
			nCtrlID = pWndCtrl->GetDlgCtrlID();

 
			if (	!pWndCtrl->IsKindOf( RUNTIME_CLASS( CEdit ) ) &&
					!pWndCtrl->IsKindOf( RUNTIME_CLASS( CListCtrl ) ) &&
					!pWndCtrl->IsKindOf( RUNTIME_CLASS( CSpinButtonCtrl) )
				)
			{

				if( nCtrlID > 0 )
				{
					// check if the Id is in the range of common strings
					if( (nCtrlID >= COMMON_IDMIN) && (nCtrlID <= COMMON_IDMAX) )
					{
						strLang = GetString( nCtrlID );
					}
					else
					{
						strLang = GetDialogString( MAKELPARAM( nCtrlID, lSection ) );
					}

					if( !strLang.IsEmpty() )
					{

						if ( pWndCtrl->IsKindOf( RUNTIME_CLASS( CComboBox ) ) )
						{
							CString strAdd;
							int		nPos = -1;

							// remove old strings
							((CComboBox*)pWndCtrl)->ResetContent();

							while ( ( nPos = strLang.Find( '\n' ) ) >= 0 )
							{
								((CComboBox*)pWndCtrl)->AddString( strLang.Left( nPos ) );
								strLang = strLang.Mid( nPos + 1 );
								nPos++;
							}

							if ( strLang.GetLength() > nPos )
							{
								((CComboBox*)pWndCtrl)->AddString( strLang.Mid( nPos ) );
							}
						}
						else
						{
							pDialog->SetDlgItemText( nCtrlID, strLang );
						}
					}
					else
					{
						CString strMissing;

						if ( pWndCtrl->IsKindOf( RUNTIME_CLASS( CComboBox ) ) )
						{
							int nIndex = 0;
							int	nItems = ((CComboBox*)pWndCtrl)->GetCount();

							for ( nIndex =0; nIndex < nItems; nIndex++ )
							{
								CString strTmp;
								
								((CComboBox*)pWndCtrl)->GetLBText( nIndex, strTmp );
								strMissing += strTmp;

								if ( nIndex < nItems - 1 )
								{
									strMissing += _T( "\n" );
								}
							}
						}
						else
						{
							pDialog->GetDlgItemText( nCtrlID, strMissing );
						}
						if ( !strMissing.IsEmpty() )
						{
							ReportMissingID( MAKELPARAM( nCtrlID, lSection ), strMissing, 2 );
						}
					}
				}
			}

			pWndCtrl = pWndCtrl->GetWindow( GW_HWNDNEXT );

		} while( (pWndCtrl != NULL) && (pWndCtrl != pDialog->GetWindow( GW_CHILD | GW_HWNDFIRST )) );
	}

	return TRUE;
}

CString CLanguage::GetLanguage( ) const
{
	return m_strCurrentLanguage;
}
