/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	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 3 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, see <http://www.gnu.org/licenses/>.
*/

//
// TransDB.cpp
//

#include "stdAfx.h"
#include "transdb.h"
#include "Babylondlg.h"
#include "Babylon.h"
#include "assert.h"
#include "bin.h"
#include "list.h"

static char buffer[100*1024];

static List	DataBases;

static LANGINFO langinfo[] = 
	{  
		{	LANGID_US, "US", "us", "e"},
		{ LANGID_UK, "UK", "uk", "e" },
		{ LANGID_GERMAN, "German", "ge", "g" },
		{ LANGID_FRENCH, "French", "fr", "f" },
		{ LANGID_SPANISH, "Spanish", "sp", "s" },
		{ LANGID_ITALIAN, "Italian", "it", "i" },
		{ LANGID_JAPANESE, "Japanese", "ja", "j" },
		{ LANGID_KOREAN, "Korean", "ko", "k" },
		{ LANGID_CHINESE, "Chinese", "ch", "c" },
		{ LANGID_JABBER, "Jabberwockie", "jb", "e" },
		{ LANGID_UNKNOWN, "Unknown", NULL, NULL }
	};

LANGINFO *GetLangInfo ( int index )
{
	
	if ( (index >= 0) && (index < (sizeof ( langinfo ) / sizeof (LANGINFO )) -1) )
	{
		return &langinfo[index];
	}

	return NULL;
}

LANGINFO *GetLangInfo ( LangID langid )
{
	LANGINFO *item;

	item = langinfo;

	while ( item->langid != LANGID_UNKNOWN )
	{
		if ( item->langid == langid )
		{
			return item;
		}
		item++;
	}

	return NULL;
}

char *GetLangName ( LangID langid )
{
	LANGINFO *item;

	if ( ( item = GetLangInfo ( langid )) )
	{
		return item->name;
	}

	return "unknown";
}

LANGINFO *GetLangInfo ( char *language )
{
	LANGINFO *item;

	item = langinfo;

	while ( item->langid != LANGID_UNKNOWN )
	{
		if ( !stricmp ( language, item->name ) )
		{
			return item;
		}
		item++;
	}

	return NULL;
}

TransDB* FirstTransDB ( void )
{
	ListNode *first;

	first = DataBases.Next ();
	if ( first )
	{
		return (TransDB *) first->Item ();
	}
	return NULL;
}

TransDB::TransDB ( char *cname )
{
	text_bin = new Bin ();
	text_id_bin = new BinID ();
	label_bin = new Bin ();
	obsolete_bin = new Bin ();
	strncpy ( name, cname, sizeof ( name ) -1 );
	name[sizeof(name)-1] = 0;
	node.SetItem ( this );
	DataBases.AddToTail ( &node );
	next_string_id = -1;
	valid = TRUE;
	num_obsolete = 0;
	checked_for_errors = FALSE;
	flags = TRANSDB_OPTION_NONE | TRANSDB_OPTION_DUP_TEXT;
}

TransDB::	~TransDB ( )
{
	Clear ();
	node.Remove ();
	delete text_bin;
	delete text_id_bin;
	delete label_bin;
	delete obsolete_bin;

}

void					TransDB::AddLabel		( BabylonLabel *label )
{
	ListNode	*node = new ListNode ();

	node->SetItem ( label );

	label_bin->Add ( label, label->Name() );

	labels.AddToTail ( node );
	label->SetDB ( this );
	Changed ();

}

void					TransDB::AddText		( BabylonText *text )
{

	text_bin->Add ( text, text->Get() );
	if ( text->ID () > 0 )
	{
		text_id_bin->Add ( text, text->ID ());
	}

}

void					TransDB::AddObsolete		( BabylonText *text )
{
	ListNode	*node = new ListNode ();

	node->SetItem ( text );

	obsolete_bin->Add ( text, text->Get() );
	if ( text->ID () > 0 )
	{
		text_id_bin->Add ( text, text->ID ());
	}

	num_obsolete++;
	text->SetParent ( (DBAttribs *)this );
	text->Changed ();

	obsolete.AddToTail ( node );
	Changed ();

}

void					TransDB::RemoveLabel ( BabylonLabel *label )
{
	ListNode *node;

	if ( (node = labels.Find ( label )) )
	{
		node->Remove ();
		label->SetDB ( NULL );
		label_bin->Remove ( label );
		delete node;
		Changed ();
	}
}

void					TransDB::RemoveText ( BabylonText *text )
{
	text_bin->Remove ( text );
	text_id_bin->Remove ( text );
}

void					TransDB::RemoveObsolete ( BabylonText *text )
{
	ListNode *node;

	if ( (node = obsolete.Find ( text )) )
	{
		node->Remove ();
		obsolete_bin->Remove ( text );
		text_id_bin->Remove ( text );
		num_obsolete--;
		delete node;
		Changed ();
	}
}

int					TransDB::NumLabelsChanged ( void )
{
	BabylonLabel	*label;
	ListSearch sh;
	int changed = 0;

	label = FirstLabel ( sh );

	while ( label )
	{
		if ( label->IsChanged ())
		{
			changed++;
		}

		label = NextLabel ( sh );
	}

	return changed;
}

int					TransDB::NumLabels ( void )
{

	return labels.NumItems();
}

BabylonLabel*			TransDB::FirstLabel	( ListSearch& sh )
{
	ListNode *node;

	if ( ( node = sh.FirstNode ( &labels )))
	{
		return (BabylonLabel *) node->Item ();
	}

	return NULL;
}

BabylonLabel*			TransDB::NextLabel		( ListSearch& sh)
{
	ListNode *node;

	if ( ( node = sh.Next ()))
	{
		return (BabylonLabel *) node->Item ();
	}

	return NULL;
}

BabylonText*			TransDB::FirstObsolete	( ListSearch& sh )
{
	ListNode *node;

	if ( ( node = sh.FirstNode ( &obsolete )))
	{
		return (BabylonText *) node->Item ();
	}

	return NULL;
}

BabylonText*			TransDB::NextObsolete		( ListSearch& sh)
{
	ListNode *node;

	if ( ( node = sh.Next ()))
	{
		return (BabylonText *) node->Item ();
	}

	return NULL;
}

BabylonLabel*			TransDB::FindLabel		( OLECHAR *name )
{
	return (BabylonLabel *) label_bin->Get ( name );
}

BabylonText*			TransDB::FindText		( OLECHAR *text )
{

	return (BabylonText *) text_bin->Get ( text );
}

BabylonText*			TransDB::FindSubText		( OLECHAR *pattern, int item )
{
	BabylonLabel	*label;
	ListSearch sh;
	BabylonText		*text;
	ListSearch sh_text;
	int plen = wcslen ( pattern );

	label = FirstLabel ( sh );

	while ( label )
	{
		text = label->FirstText ( sh_text );

		while ( text )
		{
			
			if ( !wcsnicmp ( text->Get (), pattern, 15 ))
			{
				if ( !item )
				{
					return text;
				}

				item--;
			}

			text = label->NextText ( sh_text );
		}

		label = NextLabel ( sh );
	}

	return NULL;

}

BabylonText*			TransDB::FindText		( int id )
{

	return (BabylonText *) text_id_bin->Get ( id );
}

BabylonText*			TransDB::FindNextText		( void )
{

	return (BabylonText *) text_bin->GetNext ( );
}

BabylonText*			TransDB::FindObsolete		( OLECHAR *name )
{
	return (BabylonText *) obsolete_bin->Get ( name );
}

BabylonText*			TransDB::FindNextObsolete		( void )
{

	return (BabylonText *) obsolete_bin->GetNext ( );

}

int					TransDB::Clear				( void )
{
	ListSearch sh;
	BabylonLabel *label;
	BabylonText *text;
	ListNode *node;
	int count = 0;

	text_bin->Clear ();
	text_id_bin->Clear ();
	label_bin->Clear ();
	obsolete_bin->Clear ();

	while ( node = sh.FirstNode ( &labels ) )
	{
		node->Remove ();
		label = (BabylonLabel *) node->Item ();
		count++;
		delete label;
		delete node;
	}

	while ( node = sh.FirstNode ( &obsolete ) )
	{
		node->Remove ();
		text = (BabylonText *) node->Item ();
		count++;
		delete text;
		delete node;
	}

	num_obsolete = 0;

	if ( next_string_id != -1 )
	{
			next_string_id = START_STRING_ID;
	}	

	if ( count )
	{
		Changed ();
	}

	valid = TRUE;

	return count;
}

void					TransDB::ClearChanges				( void )
{
	ListSearch sh;
	BabylonLabel *label;

	label = FirstLabel ( sh );
	while ( label )
	{
		label->ClearChanges ();
		label = NextLabel ( sh );
	}

	BabylonText *text = FirstObsolete ( sh );
	while ( text )
	{
		text->ClearChanges ();
		text = NextObsolete ( sh );
	}

	NotChanged ();
}

void					TransDB::ClearProcessed				( void )
{
	ListSearch sh;
	BabylonLabel *label;

	label = FirstLabel ( sh );
	while ( label )
	{
		label->ClearProcessed ();
		label = NextLabel ( sh );
	}
	NotProcessed ();
}

void					TransDB::ClearMatched				( void )
{
	ListSearch sh;
	BabylonLabel *label;

	label = FirstLabel ( sh );
	while ( label )
	{
		label->ClearMatched ();
		label = NextLabel ( sh );
	}
	NotMatched ();
}

void					TransDB::AddToTree		( CTreeCtrl *tc, HTREEITEM parent, int changes, void (*cb) ( void ) )
{
	HTREEITEM		item;
	HTREEITEM		ilabels, iobsolete;
	ListSearch	sh;
	BabylonLabel		*label;
	BabylonText			*txt;
	
	sprintf ( buffer, "%s%c  (%d/%d)",name, ChangedSymbol(), NumLabelsChanged(), NumLabels() );
	item = tc->InsertItem ( buffer, parent );
	ilabels = tc->InsertItem ( "Labels", item );

	label = FirstLabel ( sh );

	while ( label )
	{
		if ( !changes || label->IsChanged ())
		{
			label->AddToTree ( tc, ilabels, changes );
		}

		if ( cb )
		{
			cb ( );
		}

		label = NextLabel ( sh );
	}

	if ( num_obsolete )
	{
		iobsolete = tc->InsertItem ( "Obsolete Strings", item );
		
		txt = FirstObsolete ( sh );
		
		while ( txt )
		{
			if ( !changes || txt->IsChanged ())
			{
				txt->AddToTree ( tc, iobsolete );
			}
		
			if ( cb )
			{
				cb ( );
			}
		
			txt = NextObsolete ( sh );
		}
	}


}

TransDB*			TransDB::Next				( void )
{
	ListNode *next;

	next = node.Next ();

	if ( next )
	{
		return (TransDB *) next->Item ();
	}

	return NULL;

}

void BabylonLabel::init ( void )
{
	db = NULL;
	comment = NULL;
	line_number = -1;
	max_len = 0;
	name = NULL;
}

BabylonLabel::BabylonLabel ( void )
{
	init ();
	name = new OLEString ( );
	comment = new OLEString ( );
	context = new OLEString ( );
	speaker = new OLEString ( );
	listener = new OLEString ( );


}

BabylonLabel::~BabylonLabel ( )
{
	Clear ();
	delete name;
	delete comment;
	delete context;
	delete speaker;
	delete listener;
}

void					BabylonLabel::Remove			( void )
{
	if ( db )
	{
		db->RemoveLabel ( this );
	}
}

void					BabylonLabel::RemoveText ( BabylonText *txt )
{
	ListNode *node;

	if ( (node = text.Find ( txt )) )
	{
		node->Remove ();
		txt->SetDB ( NULL );
		txt->SetLabel ( NULL );
		txt->SetParent ( NULL );
		delete node;
		Changed ();
	}
}

void					BabylonLabel::AddText			( BabylonText *new_text )
{
	TransDB *db = DB();
	ListNode	*node = new ListNode ();

	node->SetItem ( new_text );

	text.AddToTail ( node );
	Changed ();
	new_text->SetDB ( db );
	new_text->SetParent ( (DBAttribs *) this );
	new_text->SetLabel ( this );
}

int					BabylonLabel::Clear				( void )
{
	ListSearch sh;
	BabylonText *txt;
	ListNode *node;
	int count = 0;

	while ( node = sh.FirstNode ( &text ) )
	{
		node->Remove ();
		txt = (BabylonText *) node->Item ();
		delete txt;
		delete node;
		count++;
	}

	if ( count )
	{
		Changed ();
	}

	return count;
}

BabylonLabel*			BabylonLabel::Clone				( void )
{
	BabylonLabel *clone = new BabylonLabel();
	BabylonText *txt;
	ListSearch sh;

	clone->SetName ( Name());
	clone->SetComment ( Comment ());
	clone->SetListener ( Listener ());
	clone->SetSpeaker ( Speaker ());
	clone->SetMaxLen ( MaxLen ());
	clone->SetContext ( Context ());

	txt = FirstText ( sh );

	while ( txt )
	{
		clone->AddText ( txt->Clone ());

		txt = NextText ( sh );
	}

	return clone;
}

BabylonText*			BabylonLabel::FirstText		( ListSearch& sh )
{
	ListNode *node;

	if ( ( node = sh.FirstNode ( &text )))
	{
		return (BabylonText *) node->Item ();
	}

	return NULL;
}

BabylonText*			BabylonLabel::NextText		( ListSearch& sh)
{
	ListNode *node;

	if ( ( node = sh.Next (  )))
	{
		return (BabylonText *) node->Item ();
	}

	return NULL;

}

BabylonText*			BabylonLabel::FindText ( OLECHAR *find_text )
{
	ListSearch sh;
	BabylonText *txt;

	txt = FirstText ( sh );

	while ( txt )
	{
		if ( !wcscmp ( txt->Get(), find_text ))
		{
			return txt;
		}
		txt = NextText ( sh );
	}

	return NULL;
}


void					BabylonLabel::SetDB				( TransDB *new_db )
{
	BabylonText *ntext;
	ListSearch sh;

	db = new_db;
	SetParent ( (DBAttribs *) new_db );

	ntext = FirstText ( sh );

	while ( ntext )
	{
		ntext->SetDB ( new_db );
		
		ntext = NextText ( sh );
	}

}

void					BabylonLabel::ClearChanges				( void )
{
	BabylonText *ntext;
	ListSearch sh;

	ntext = FirstText ( sh );

	while ( ntext )
	{
		ntext->ClearChanges();
		
		ntext = NextText ( sh );
	}

	NotChanged();

}

void					BabylonLabel::ClearProcessed				( void )
{
	BabylonText *ntext;
	ListSearch sh;

	ntext = FirstText ( sh );

	while ( ntext )
	{
		ntext->ClearProcessed();
		
		ntext = NextText ( sh );
	}

	NotProcessed();

}

void					BabylonLabel::ClearMatched				( void )
{
	BabylonText *ntext;
	ListSearch sh;

	ntext = FirstText ( sh );

	while ( ntext )
	{
		ntext->ClearMatched();
		
		ntext = NextText ( sh );
	}

	NotMatched();

}

int					BabylonLabel::AllMatched				( void )
{
	BabylonText *ntext;
	ListSearch sh;

	ntext = FirstText ( sh );

	while ( ntext )
	{
		if ( !ntext->Matched() )
		{
			return FALSE;
		}
		
		ntext = NextText ( sh );
	}

	return TRUE;
}

BabylonText::BabylonText( void )
{
	init ();
	text = new OLEString (  );
	wavefile = new OLEString (  );

}

int BabylonText::IsSent ( void )
{
	return sent;
}

void BabylonText::Sent ( int val )
{
	sent = val;
}

void					BabylonLabel::AddToTree		( CTreeCtrl *tc, HTREEITEM parent, int changes )
{
	HTREEITEM		litem;
	ListSearch	sh;
	BabylonText			*txt;

	sprintf ( buffer, "%s%c", NameSB(), ChangedSymbol() );
																							 
	litem = tc->InsertItem ( buffer, parent );

	txt = FirstText ( sh );

	while ( txt )
	{
		if ( !changes || txt->IsChanged ())
		{
			txt->AddToTree ( tc, litem );
		}

		txt = NextText ( sh );
	}

	if ( strcmp ( CommentSB(), "" ) )
	{
		sprintf ( buffer, "COMMENT : %s", CommentSB() );
		tc->InsertItem ( buffer, litem );
	}

	if ( strcmp ( ContextSB(), "" ) )
	{
		sprintf ( buffer, "CONTEXT : %s", ContextSB() );
		tc->InsertItem ( buffer, litem );
	}
		
	if ( strcmp ( SpeakerSB(), "" ) )
	{
		sprintf ( buffer, "SPEAKER : %s", SpeakerSB() );
		tc->InsertItem ( buffer, litem );
	}
		
	if ( strcmp ( ListenerSB(), "" ) )
	{
		sprintf ( buffer, "LISTENER: %s", ListenerSB() );
		tc->InsertItem ( buffer, litem );
	}

	if ( line_number != -1 )
	{
		sprintf ( buffer, "LINE    : %d", line_number );
		tc->InsertItem ( buffer, litem );
	}

	if ( max_len )
	{
		sprintf ( buffer, "MAX LEN : %d", max_len );
		tc->InsertItem ( buffer, litem );
	}

}

void BabylonText::init ( void )
{
	db = NULL;
	label = NULL;
	line_number = -1;
	revision = 1;
	text = NULL;
	wavefile = NULL;
	id = -1;
	retranslate = FALSE;
	sent = FALSE;


}

BabylonText::~BabylonText( )
{
	Clear();
	delete text;
	delete wavefile;

}

void					BabylonText::SetDB				( TransDB *new_db )
{
	Translation *trans;
	ListSearch sh;

	if ( db )
	{
		db->RemoveText ( this );
	}


	if ( (db = new_db) )
	{
		AssignID ();
		db->AddText ( this );
	}

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		trans->SetDB ( new_db );
		
		trans = NextTranslation ( sh );
	}

}

void					BabylonText::Remove			( void )
{
	if ( label )
	{
		label->RemoveText ( this );
	}
}

int						BabylonText::IsDialog ( void )
{

	return strcmp (WaveSB(), "" );

}

int						BabylonText::DialogIsValid ( const char *path, LangID langid, int check )
{
	LANGINFO	*linfo;
	CWaveInfo *winfo;
	DBAttribs *attribs;

	linfo = GetLangInfo ( langid );

	if ( langid == LANGID_US )
	{
		winfo = &WaveInfo;
		attribs = (DBAttribs *) this;
	}
	else
	{
		Translation *trans = GetTranslation ( langid );

		if ( !trans )
		{
			return FALSE;
		}

		attribs = (DBAttribs *) trans;
		winfo = &trans->WaveInfo;
	}


	if ( winfo->Valid () && check )
	{
		WIN32_FIND_DATA info;
		HANDLE	handle;
		
		winfo->SetValid ( FALSE );
		winfo->SetMissing ( TRUE );

		sprintf ( buffer, "%s%s\\%s%s.wav", path, linfo->character, WaveSB(), linfo->character );
		if ( (handle = FindFirstFile ( buffer, &info )) != INVALID_HANDLE_VALUE )
		{
			if ( ! (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
			{
				if ( winfo->Lo () == info.nFileSizeLow && winfo->Hi() == info.nFileSizeHigh )
				{
					winfo->SetValid ( TRUE );
				}
				winfo->SetMissing ( FALSE );
			}

			FindClose ( handle );
		}

	}

	return winfo->Valid ();
}

int						BabylonText::ValidateDialog ( const char *path, LangID langid )
{
	WIN32_FIND_DATA info;
	HANDLE	handle;
	CWaveInfo *winfo;
	LANGINFO *linfo;
	DBAttribs *attribs;

	linfo = GetLangInfo ( langid );

	if ( langid == LANGID_US )
	{
		winfo = &WaveInfo;
		attribs = (DBAttribs *) this;
	}
	else
	{
		Translation *trans = GetTranslation ( langid );

		if ( !trans )
		{
			return FALSE;
		}

		attribs = (DBAttribs *) trans;
		winfo = &trans->WaveInfo;
	}

	winfo->SetValid  ( FALSE );
	winfo->SetMissing ( TRUE );

	sprintf ( buffer, "%s%s\\%s%s.wav", path, linfo->character , WaveSB(), linfo->character );
	if ( (handle = FindFirstFile ( buffer, &info )) != INVALID_HANDLE_VALUE )
	{
		if ( ! (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
		{
			winfo->SetLo ( info.nFileSizeLow );
			winfo->SetHi ( info.nFileSizeHigh );
			winfo->SetValid  ( TRUE );
			winfo->SetMissing ( FALSE );
			attribs->Changed();
		}
		FindClose ( handle );
	}

	return winfo->Valid ();
}

int						BabylonText::DialogIsPresent ( const char *path, LangID langid )
{
			
	WIN32_FIND_DATA info;
	HANDLE	handle;
	int present = FALSE;
	LANGINFO	*linfo = GetLangInfo ( langid );

	sprintf ( buffer, "%s%s\\%s%s.wav", path, linfo->character , WaveSB(), linfo->character );
	if ( (handle = FindFirstFile ( buffer, &info )) != INVALID_HANDLE_VALUE )
	{
		if ( ! (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
		{
				present = TRUE;
		}

		FindClose ( handle );
	}

	return present;
}

void					BabylonText::AddTranslation			( Translation *trans )
{
	ListNode	*node = new ListNode ();

	node->SetItem ( trans );

	translations.AddToTail ( node );
	Changed ();
	trans->SetDB ( DB() );
	trans->SetParent ( (DBAttribs *) this );

}

Translation*			BabylonText::FirstTranslation		( ListSearch& sh )
{
	ListNode *node;

	if ( ( node = sh.FirstNode ( &translations )))
	{
		return (Translation *) node->Item ();
	}

	return NULL;
}

Translation*			BabylonText::NextTranslation		( ListSearch& sh)
{
	ListNode *node;

	if ( ( node = sh.Next (  )))
	{
		return (Translation *) node->Item ();
	}

	return NULL;
}

Translation*			BabylonText::GetTranslation		( LangID langid )
{
	ListSearch sh;
	Translation *trans;

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		if ( langid == trans->GetLangID())
		{
			break;
		}

		trans = NextTranslation ( sh );
	}


	return trans;
}

int					BabylonText::Clear				( void )
{
	ListSearch sh;
	Translation *trans;
	ListNode *node;
	int count = 0;

	while ( node = sh.FirstNode ( &translations ) )
	{
		node->Remove ();
		trans = (Translation *) node->Item ();
		delete trans;
		delete node;
		count++;
	}
	if ( count )
	{
		Changed ();
	}

	return count;
}

BabylonText*			BabylonText::Clone				( void )
{
	BabylonText *clone = new BabylonText();
	Translation *trans;
	ListSearch sh;

	clone->Set ( Get ());
	clone->SetWave ( Wave ());
	clone->SetRevision ( Revision ());

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		clone->AddTranslation ( trans->Clone ());

		trans = NextTranslation ( sh );
	}

	return clone;
}

void					BabylonText::ClearChanges				( void )
{
	Translation *trans;
	ListSearch sh;

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		trans->ClearChanges();
		
		trans = NextTranslation ( sh );
	}

	NotChanged();

}

void					BabylonText::ClearProcessed				( void )
{
	Translation *trans;
	ListSearch sh;

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		trans->ClearProcessed();
		
		trans = NextTranslation ( sh );
	}

	NotProcessed();

}

void					BabylonText::ClearMatched				( void )
{
	Translation *trans;
	ListSearch sh;

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		trans->ClearMatched();
		
		trans = NextTranslation ( sh );
	}

	NotMatched();

}

void					BabylonText::AssignID ( void )
{
	if ( id != -1 )
	{
		return;	// already assigned
	}
	if ( db )
	{
		SetID ( db->NewID ());
	}
}

void					BabylonText::Set ( OLECHAR *string )
{
	if ( db )
	{
		db->RemoveText ( this );
	}

	text->Set ( string );
	InvalidateWave ( );
	if ( db )
	{
		db->AddText ( this );
	}

	Changed ();
}

void					BabylonText::Set ( char *string )
{
	if ( db )
	{
		db->RemoveText ( this );
	}

	text->Set ( string );
	InvalidateWave ();
	if ( db )
	{
		db->AddText ( this );
	}

	Changed ();
}

void					BabylonText::InvalidateAllWaves			( void  )
{ 
	Translation *trans;
	ListSearch sh;

	WaveInfo.SetValid ( FALSE );

	trans = FirstTranslation ( sh );
	
	while ( trans )
	{
		trans->WaveInfo.SetValid ( FALSE );
	
		trans = NextTranslation ( sh );
	}

}

void					BabylonText::InvalidateWave			( void )
{ 

	WaveInfo.SetValid ( FALSE );

}

void					BabylonText::InvalidateWave			( LangID langid  )
{ 

	WaveInfo.SetValid ( FALSE );

	if ( langid == LANGID_US )
	{
		InvalidateWave ();
	}
	else
	{
		Translation *trans = GetTranslation ( langid );
		
		if ( trans )
		{
			trans->WaveInfo.SetValid ( FALSE );
		}

	}
}

void					BabylonText::AddToTree		( CTreeCtrl *tc, HTREEITEM parent, int changes )
{
	HTREEITEM		item;
	ListSearch	sh;
	Translation	*trans;

	sprintf ( buffer, "TEXT %c : %s", ChangedSymbol() ,GetSB ());
	item = tc->InsertItem ( buffer, parent );

	trans = FirstTranslation ( sh );

	while ( trans )
	{
		if ( !changes || trans->IsChanged ())
		{
			trans->AddToTree ( tc, item );
		}

		trans = NextTranslation ( sh );
	}

	if ( ID() != -1 )
	{
		sprintf ( buffer, "ID     : %d", ID ());
		tc->InsertItem ( buffer, item );
	}

	if ( strcmp ( WaveSB(), "" ) )
	{
		sprintf ( buffer, "WAVE   : %s", WaveSB() );
		tc->InsertItem ( buffer, item );
	}

	if ( line_number != -1 )
	{
		sprintf ( buffer, "LINE   : %d", line_number );
		tc->InsertItem ( buffer, item );
	}

	sprintf ( buffer, "REV    : %d", revision );
	tc->InsertItem ( buffer, item );

	sprintf ( buffer, "LEN    : %d", this->Len() );
	tc->InsertItem ( buffer, item );

}

Translation::Translation ( void )
{
	text = new OLEString (  );
	comment = new OLEString (  );
	revision = 0;
	sent = FALSE;
	
}

Translation::~Translation ( )
{
	delete text;
	delete comment;
}

int Translation::IsSent ( void )
{
	return sent;
}

void Translation::Sent ( int val )
{
	sent = val;
}

void					Translation::SetDB				( TransDB *new_db )
{
	db = new_db;
}

Translation*			Translation::Clone				( void )
{
	Translation *clone = new Translation();

	clone->Set ( Get ());
	clone->SetComment ( Comment ());
	clone->SetLangID ( GetLangID ());
	clone->SetRevision ( Revision ());

	return clone;
}

void					Translation::AddToTree		( CTreeCtrl *tc, HTREEITEM parent, int changes )
{
	HTREEITEM		item;

	sprintf ( buffer, "%s%c   : %s", Language(), ChangedSymbol(), GetSB ());

	item = tc->InsertItem ( buffer, parent );

	if ( strcmp ( CommentSB(), "" ) )
	{
		sprintf ( buffer, "COMMENT: %s", CommentSB() );
		tc->InsertItem ( buffer, item );
	}

	sprintf ( buffer, "REV    : %d", revision );
	tc->InsertItem ( buffer, item );

	sprintf ( buffer, "LEN    : %d", Len() );
	tc->InsertItem ( buffer, item );

}

int Translation::TooLong ( int maxlen )
{
	return maxlen != 0 && text->Len () > maxlen;

}

int Translation::ValidateFormat ( BabylonText *ntext )
{
	return SameFormat ( text->Get(), ntext->Get ());

}

int TransDB::Warnings ( CBabylonDlg *dlg )
{
	BabylonLabel *label;
	ListSearch sh_label;
	int count = 0;
	int warnings = 0;
	List	dups;

	if ( dlg )
	{
		dlg->InitProgress ( NumLabels ());
		dlg->Log ("");
		dlg->Log ("Generals.str Warnigs:");
		dlg->Status ( "Creating warnings report...", FALSE );
	}

	text_bin->Clear();

	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		BabylonText *existing_text;
		ListSearch sh_text;

		text = label->FirstText ( sh_text );

		while ( text )
		{
			if ( text->Len ( ) == 0 )
			{
				if ( dlg )
				{
					sprintf ( buffer, "Warning:: text at line %5d is NULL", 
								text->LineNumber());
					dlg->Log ( buffer );
				}
				warnings++;
			}
			else if ( !DuplicatesAllowed() && ( existing_text = FindText ( text->Get () )))
			{
				warnings++;
				if ( dlg )
				{
					DupNode *dup = new DupNode ( text, existing_text );
					dups.Add ( dup );
				}
			}
			else
			{
				text_bin->Add ( text, text->Get() );
			}

			text = label->NextText ( sh_text );
		}

		count++;
		if ( dlg )
		{
			dlg->SetProgress ( count );
		}
		label = NextLabel ( sh_label );
	}

	if ( dlg )
	{
		DupNode *dup;

		while ( (dup = (DupNode*)dups.LastNode ()))
		{
			sprintf ( buffer, "Warning:: text at line %5d is a duplicate of text on line %5d", 
									dup->Duplicate()->LineNumber(), dup->Original()->LineNumber());
			dlg->Log ( buffer );

			dup->Remove ();
			delete dup;
		}
		sprintf ( buffer, "Total warnings: %d", warnings );
		dlg->Log ( buffer );
		dlg->Ready();
	}

	return warnings;
}

int TransDB::Errors ( CBabylonDlg *dlg )
{
	BabylonLabel *label;
	BabylonLabel *existing_label;
	ListSearch sh_label;
	Bin	*tbin = new Bin ();
	int count = 0;
	int errors = 0;

	if ( dlg )
	{
		dlg->InitProgress ( NumLabels ());
		dlg->Log ("");
		dlg->Log ("Generals.str Errors:");
		dlg->Status ( "Creating error report...", FALSE );
	}



	label_bin->Clear();

	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		BabylonText *existing_text;
		ListSearch sh_text;

		if ( !MultiTextAllowed () && label->NumStrings () > 1 )
		{
			errors++;
			if ( dlg )
			{
				sprintf ( buffer, "Error  : Label \"%s\" at line %d is has more than 1 string defined", 
							label->NameSB(), label->LineNumber());
				dlg->Log ( buffer );
			}
	
		}

		if ( ( existing_label = FindLabel ( label->Name () )))
		{
			errors++;
			if ( dlg )
			{
				sprintf ( buffer, "Error  : Label \"%s\" at line %d is already defined on line %d", 
							label->NameSB(), label->LineNumber(), existing_label->LineNumber());
				dlg->Log ( buffer );
			}
		}

		label_bin->Add ( label, label->Name());

		tbin->Clear ();

		text = label->FirstText ( sh_text );

		while ( text )
		{
				if ( ( existing_text = (BabylonText *) tbin->Get ( text->Get () )))
				{
					errors++;
					if ( dlg )
					{
						sprintf ( buffer, "Error  : Label \"%s\" has duplicate text at line %d",
									label->NameSB(), text->LineNumber());
						dlg->Log ( buffer );
					}
				}

			tbin->Add ( text, text->Get() );

			// check string length against max len

			if ( label->MaxLen () )
			{
				if ( text->Len () > label->MaxLen ())
				{
					errors++;
					if ( dlg )
					{
						sprintf ( buffer, "Error  : The US text at line %d (for label \"%s\") exceeds the max length",
									text->LineNumber(), label->NameSB());
						dlg->Log ( buffer );
					}
				}
			}

			text = label->NextText ( sh_text );
		}

		count++;
		if ( dlg )
		{
			dlg->SetProgress ( count );
		}
		label = NextLabel ( sh_label );
	}

	if ( dlg )
	{
		sprintf ( buffer, "Total errors: %d", errors );
		dlg->Log ( buffer );

		dlg->Ready();
	}

	delete tbin;
	last_error_count = errors;
	checked_for_errors = TRUE;
	return errors;
}

CWaveInfo::CWaveInfo ( void )
{
	wave_valid = FALSE;
	missing = TRUE;
}

void TransDB::VerifyDialog( LangID langid, void (*cb) (void) ) 
{
	BabylonLabel *label;
	ListSearch sh_label;
	int count = 0;
	LANGINFO *linfo = GetLangInfo ( langid );

	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		ListSearch sh_text;

		text = label->FirstText ( sh_text );

		while ( text )
		{
			if ( text->IsDialog ())
			{
				if ( text->DialogIsPresent ( DialogPath, langid ))
				{
					text->DialogIsValid ( DialogPath, langid );
				}
			}

			text = label->NextText ( sh_text );
		}

		if ( cb )
		{
			cb();
		}
		label = NextLabel ( sh_label );
	}

}

void TransDB::InvalidateDialog( LangID langid ) 
{
	BabylonLabel *label;
	ListSearch sh_label;

	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		ListSearch sh_text;

		text = label->FirstText ( sh_text );

		while ( text )
		{
			if ( text->IsDialog ())
			{
				text->InvalidateWave ( langid );
			}

			text = label->NextText ( sh_text );
		}

		label = NextLabel ( sh_label );
	}

}

int TransDB::ReportDialog( DLGREPORT *report, LangID langid, void (*print) ( const char *), PMASK pmask ) 
{
	BabylonLabel *label;
	ListSearch sh_label;
	int count = 0;
	DLGREPORT _info;
	DLGREPORT *info = &_info;
	int skip_verify = FALSE;
	LANGINFO *linfo = GetLangInfo ( langid );

	if ( report )
	{
		info = report;
	}

	memset ( info, 0, sizeof ( DLGREPORT ));


	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		ListSearch sh_text;

		text = label->FirstText ( sh_text );

		while ( text )
		{
			if ( text->IsDialog ())
			{
				if ( text->DialogIsPresent ( DialogPath, langid))
				{
					if ( !text->DialogIsValid ( DialogPath, langid, FALSE ) )
					{
						if ( print && pmask & PMASK_UNRESOLVED )
						{
							sprintf ( buffer, "%d: audio file \"%s%s.wav\" not verified", text->ID(), text->WaveSB (), linfo->character);
							
							print ( buffer );
						}
						info->unresolved++;
					}
					else
					{
						info->resolved++;
					}
				}
				else
				{
					if ( print && pmask & PMASK_MISSING )
					{
						sprintf ( buffer, "%d: audio file \"%s%s.wav\" missing", text->ID(), text->WaveSB (), linfo->character);
						
						print ( buffer );
					}
					info->missing++;
				}
				
				info->numdialog++;
			}

			text = label->NextText ( sh_text );
		}

		label = NextLabel ( sh_label );
	}

	return info->missing + info->unresolved + info->errors ;
}

int TransDB::ReportTranslations( TRNREPORT *report, LangID langid, void (*print) ( const char *buffer), PMASK pmask ) 
{
	BabylonLabel *label;
	ListSearch sh_label;
	int count = 0;
	int first_error = FALSE;
	TRNREPORT _info;
	TRNREPORT *info = &_info;

	if ( report )
	{
		info = report;
	}

	memset ( info, 0, sizeof ( TRNREPORT ));

	label = FirstLabel ( sh_label );

	while ( label )
	{
		BabylonText *text;
		ListSearch sh_text;
		int maxlen = label->MaxLen ();

		text = label->FirstText ( sh_text );

		while ( text )
		{
			int textnum = 0;
			Translation *trans;
			int too_big = FALSE;

			if ( text->Len ())
			{
				info->numstrings++;
				if ( langid != LANGID_US )
				{
					if ( (trans = text->GetTranslation ( langid ) ))
					{
						if ( maxlen && trans->Len() > maxlen )
						{
							if ( print && pmask & PMASK_TOOLONG )
							{
								sprintf ( buffer, "%d: translation is too long by %d characters", text->ID (), trans->Len() - maxlen);
							
								print ( buffer );
							}
							too_big = TRUE;
						}
				
						if ( text->Revision () > trans->Revision ())
						{
							if ( print && pmask & PMASK_RETRANSLATE )
							{
								sprintf ( buffer, "%d: needs re-translation", text->ID () );
							
								print ( buffer );
							}
							info->retranslate++;
						}
						else
						{
							info->translated++;
							if ( !trans->ValidateFormat ( text ) )
							{
								if ( print && pmask & PMASK_BADFORMAT )
								{
									sprintf ( buffer, "%d: translation has differring formating to original", text->ID () );
						
									print ( buffer );
								}
								info->bad_format++;
							}
						}
					}
					else
					{
						if ( print && pmask & PMASK_MISSING )
						{
							sprintf ( buffer, "%d: not translated", text->ID ());
						
							print ( buffer );
						}
						info->missing++;
					}
				}
				else
				{
					// check maxlen
					if ( maxlen )
					{
						if ( text->Len() > maxlen )
						{
							if ( print && pmask & PMASK_TOOLONG )
							{
								sprintf ( buffer, "%d: is too long by %d characters", text->ID (), text->Len() - maxlen);
							
								print ( buffer );
							}
							too_big = TRUE;
						}
					}
				}
				
			}

			if ( too_big )
			{
				info->too_big++;
			}
			text = label->NextText ( sh_text );
		}

		info->numlabels++;

		label = NextLabel ( sh_label );
	}

	info->errors = info->too_big + info->bad_format;

	return info->missing + info->too_big + info->retranslate + info->bad_format;
}