/*
**	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/>.
*/

//
// loadsave.cpp
//

#include "stdAfx.h"
#include "iff.h"
#include "TransDB.h"
#include "Babylondlg.h"

#define FORM_BABYLONDB			MakeID ('N','X','D','B')
#define FORM_LABEL			MakeID ('N','L','B','L')
#define FORM_TEXT				MakeID ('N','T','X','T')
#define FORM_TRANS			MakeID ('N','T','R','N')
#define CHUNK_COMMENT		MakeID ('C','M','N','T')
#define CHUNK_CONTEXT		MakeID ('C','T','X','T')
#define CHUNK_SPEAKER		MakeID ('S','P','K','R')
#define CHUNK_LISTENER	MakeID ('L','T','N','R')
#define CHUNK_TEXT			MakeID ('T','E','X','T')
#define CHUNK_WAVE			MakeID ('W','A','V','E')
#define CHUNK_WAVE_INFO	MakeID ('W','V','I','N')
#define CHUNK_INFO			MakeID ('I','N','F','O')
#define CHUNK_NAME			MakeID ('N','A','M','E')

#define MAX_BUFFER			(100*1024)

static OLECHAR	buffer[MAX_BUFFER];
typedef struct 
{
	int num_labels;
	int next_id;

} DBINFO;

typedef struct 
{
	int max_len;

} LBINFO;

typedef struct 
{
	int id;
	int revision;

} TXINFO;

typedef struct 
{
	LangID lang;
	int revision;

} TRINFO;

typedef struct 
{
	int valid;
	DWORD lo;
	DWORD hi;

} WVINFO;


static int writeString ( IFF_FILE *iff, OLECHAR *string, int chunk_id )
{
	int len = (wcslen ( string ) );
	int bytes = (len+1)*sizeof(OLECHAR);

	if ( len )
	{
		IFF_NEWCHUNK ( iff, chunk_id, error );
		IFF_WRITE ( iff, string, bytes, error);
		IFF_CloseChunk ( iff );

	}

	return TRUE;

error:

	return FALSE;
}

static int readString ( IFF_FILE *iff, OLECHAR *string )
{

	*string = 0;

	IFF_READ ( iff, string, iff->ChunkSize, error);

	return TRUE;

error:

	return FALSE;
}

static int writeTransForm ( IFF_FILE *iff, Translation *trans )
{
			TRINFO		trinfo;
			WVINFO		wvinfo;

			if ( !IFF_NewForm ( iff, FORM_TRANS ))
			{
				goto error;
			}
	
			trinfo.lang = trans->GetLangID ();
			trinfo.revision = trans->Revision ();
	
			if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
			{
				goto error;
			}
			
			IFF_Write ( iff, &trinfo, sizeof ( trinfo ));
			
			IFF_CloseChunk ( iff );

			writeString ( iff, trans->Get (), CHUNK_TEXT );
			writeString ( iff, trans->Comment (), CHUNK_COMMENT );

			if ( (wvinfo.valid = trans->WaveInfo.Valid()) )
			{
				wvinfo.lo = trans->WaveInfo.Lo ();
				wvinfo.hi = trans->WaveInfo.Hi ();
				
				if ( !IFF_NewChunk ( iff, CHUNK_WAVE_INFO ))
				{
					goto error;
				}
				
				IFF_Write ( iff, &wvinfo, sizeof ( wvinfo ));
				
				IFF_CloseChunk ( iff );
			}

			IFF_CloseForm ( iff );

	return TRUE;
error:

	return FALSE;

}

static int writeTextForm ( IFF_FILE *iff, BabylonText *text )
{
			TXINFO		txinfo;
			WVINFO		wvinfo;

			if ( !IFF_NewForm ( iff, FORM_TEXT ))
			{
				goto error;
			}
	
			txinfo.id = text->ID ();
			txinfo.revision = text->Revision ();
	
			if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
			{
				goto error;
			}
			
			IFF_Write ( iff, &txinfo, sizeof ( txinfo ));
			
			IFF_CloseChunk ( iff );
			writeString ( iff, text->Get (), CHUNK_TEXT );
			writeString ( iff, text->Wave (), CHUNK_WAVE );

			if ( (wvinfo.valid = text->WaveInfo.Valid()) )
			{
				wvinfo.lo = text->WaveInfo.Lo ();
				wvinfo.hi = text->WaveInfo.Hi ();
				
				if ( !IFF_NewChunk ( iff, CHUNK_WAVE_INFO ))
				{
					goto error;
				}
				
				IFF_Write ( iff, &wvinfo, sizeof ( wvinfo ));
				
				IFF_CloseChunk ( iff );
			}

			IFF_CloseForm ( iff );

	return TRUE;
error:

	return FALSE;

}

int WriteMainDB(TransDB *db, const char *filename, CBabylonDlg *dlg )
{
	BabylonText *text;
	BabylonLabel *label;
	ListSearch sh_label;
	ListSearch sh_text;
	int count = 0;
	IFF_FILE	*iff = NULL;
	DBINFO		dbinfo;
	int ok = FALSE;

	if ( dlg )
	{								 
		dlg->InitProgress ( db->NumLabels ());
	}


	if ( !( iff = IFF_New ( filename )))
	{
		goto error;
	}

	if ( !IFF_NewForm ( iff, FORM_BABYLONDB ) )
	{
		goto error;
	}
	
	dbinfo.next_id = db->ID ();
	dbinfo.num_labels = db->NumLabels ();

	if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
	{
		goto error;
	}

	IFF_Write ( iff, &dbinfo, sizeof ( dbinfo ));

	IFF_CloseChunk ( iff );
	IFF_CloseForm ( iff );

	text = db->FirstObsolete ( sh_text );

	while ( text )
	{
		if ( !writeTextForm ( iff, text ) )
		{
				goto error;
		}	
		text = db->NextObsolete ( sh_text );
	}

	

	label = db->FirstLabel ( sh_label );

	while ( label )
	{
		LBINFO		lbinfo;

		if ( !IFF_NewForm ( iff, FORM_LABEL ))
		{
			goto error;
		}

		lbinfo.max_len = label->MaxLen ();

		if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
		{
			goto error;
		}
		
		IFF_Write ( iff, &lbinfo, sizeof ( lbinfo ));
		
		IFF_CloseChunk ( iff );

		writeString ( iff, label->Name (), CHUNK_NAME );
		writeString ( iff, label->Comment (), CHUNK_COMMENT );
		writeString ( iff, label->Context (), CHUNK_CONTEXT );
		writeString ( iff, label->Speaker (), CHUNK_SPEAKER );
		writeString ( iff, label->Listener (), CHUNK_LISTENER );
		IFF_CloseForm ( iff );

		text = label->FirstText ( sh_text );

		while ( text )
		{
			Translation *trans;
			ListSearch sh_trans;
			if ( !writeTextForm ( iff, text ) )
			{
				goto error;
			}
			
			trans = text->FirstTranslation ( sh_trans );

			while ( trans )
			{
				if ( !writeTransForm ( iff, trans ) )
				{
					goto error;
				}

				trans = text->NextTranslation ( sh_trans );
			}

			text = label->NextText ( sh_text );
		}

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

	ok = TRUE;
	db->ClearChanges ();
		
error:
	if ( iff )
	{
		IFF_Close ( iff );
	}

	return ok;
}

int LoadMainDB(TransDB *db, const char *filename, void (*cb) (void) )
{
	BabylonLabel	*label = NULL;
	BabylonText		*text = NULL;
	Translation *trans = NULL;
	int count = 0;
	IFF_FILE	*iff = NULL;
	DBINFO		dbinfo;
	int ok = FALSE;


	if ( !(iff = IFF_Load ( filename ) ) )
	{
		goto error;
	}

	if ( !IFF_NextForm ( iff ) || iff->FormID != FORM_BABYLONDB )
	{
		goto error;
	}

	dbinfo.next_id = -1;
	dbinfo.num_labels = 0;

	while ( IFF_NextChunk ( iff ))
	{
		switch (iff->ChunkID )
		{
			case CHUNK_INFO:
				IFF_READ ( iff, &dbinfo, sizeof ( dbinfo ), error );
				break;
		}
	}

	db->SetID ( dbinfo.next_id );

	while ( IFF_NextForm ( iff ) )
	{
		switch ( iff->FormID )
		{
			case FORM_LABEL:
			{
				LBINFO		lbinfo;
				// new label

				if ( text )
				{
					if ( label )
					{
						label->AddText ( text );
					}
					else
					{
						db->AddObsolete ( text );
					}

					text = NULL;
				}

				if ( label )
				{
					count++;
					db->AddLabel ( label );
					label = NULL;
					if ( cb )
					{
						cb ();
					}
				}

				if ( ! (label = new BabylonLabel ()))
				{
					goto error;
				}

				while ( IFF_NextChunk ( iff ))
				{
					switch ( iff->ChunkID )
					{
						case CHUNK_INFO:
							IFF_READ ( iff, &lbinfo, sizeof (lbinfo), error );
							label->SetMaxLen ( lbinfo.max_len );
							break;
						case CHUNK_COMMENT:
							readString ( iff, buffer );
							label->SetComment ( buffer );
							break;
						case CHUNK_CONTEXT:
							readString ( iff, buffer );
							label->SetContext ( buffer );
							break;
						case CHUNK_SPEAKER:
							readString ( iff, buffer );
							label->SetSpeaker ( buffer );
							break;
						case CHUNK_LISTENER:
							readString ( iff, buffer );
							label->SetListener ( buffer );
							break;
						case CHUNK_NAME:
							readString ( iff, buffer );
							label->SetName ( buffer );
							break;
					}
				}
				break;
			}
			case FORM_TEXT:
			{
				TXINFO		txinfo;

				if ( text )
				{
					if ( label )
					{
						label->AddText ( text );
					}
					else
					{
						db->AddObsolete ( text );
					}

					text = NULL;
				}

				if ( ! (text = new BabylonText ()))
				{
					goto error;
				}

				while ( IFF_NextChunk ( iff ))
				{
					switch ( iff->ChunkID )
					{
						case CHUNK_INFO:
							IFF_READ ( iff, &txinfo, sizeof (txinfo), error );
							text->SetID ( txinfo.id );
							text->SetRevision ( txinfo.revision );
							break;
						case CHUNK_TEXT:
							readString ( iff, buffer );
							text->Set ( buffer );
							break;

						case CHUNK_WAVE:
							readString ( iff, buffer );
							text->SetWave ( buffer );
							break;
						case CHUNK_WAVE_INFO:
						{
							WVINFO		wvinfo;
							IFF_READ ( iff, &wvinfo, sizeof (wvinfo), error );
							text->WaveInfo.SetValid ( wvinfo.valid );
							text->WaveInfo.SetLo ( wvinfo.lo );
							text->WaveInfo.SetHi ( wvinfo.hi );
							break;
						}
					}

				}
				break;
			}
			case FORM_TRANS:
			{
				TRINFO		trinfo;

				if ( ! (trans = new Translation ()))
				{
					goto error;
				}

				while ( IFF_NextChunk ( iff ))
				{
					switch ( iff->ChunkID )
					{
						case CHUNK_INFO:
							IFF_READ ( iff, &trinfo, sizeof (trinfo), error );
							trans->SetLangID ( trinfo.lang );
							trans->SetRevision ( trinfo.revision );
							break;
						case CHUNK_TEXT:
							readString ( iff, buffer );
							trans->Set ( buffer );
							break;

						case CHUNK_COMMENT:
							readString ( iff, buffer );
							trans->SetComment ( buffer );
							break;
						case CHUNK_WAVE_INFO:
						{
							WVINFO		wvinfo;
							IFF_READ ( iff, &wvinfo, sizeof (wvinfo), error );
							trans->WaveInfo.SetValid ( wvinfo.valid );
							trans->WaveInfo.SetLo ( wvinfo.lo );
							trans->WaveInfo.SetHi ( wvinfo.hi );
						}
							break;

					}

				}
				if ( text )
				{
					text->AddTranslation ( trans );
				}
				else
				{
					delete trans;
				}
				trans = NULL;

				break;
			}
		}
	}

	if ( text )
	{
		if ( label )
		{
			label->AddText ( text );
		}
		else
		{
			db->AddObsolete ( text );
		}

		text = NULL;
	}

	if ( label )
	{
		count++;
		db->AddLabel ( label );
		label = NULL;
		if ( cb )
		{
			cb ();
		}
	}

	ok = TRUE;
		
error:

	if ( label )
	{
		delete label;
	}

	if ( text )
	{
		delete text;
	}

	if ( trans )
	{
		delete trans;
	}

	if ( iff )
	{
		IFF_Close ( iff );
	}

	db->ClearChanges ();

	if ( !ok )
	{
		db->Clear ();
	}

	return ok;
}


int	GetLabelCountDB ( char *filename )
{
	IFF_FILE	*iff = NULL;
	DBINFO		dbinfo;
	int count = 0;


	if ( !(iff = IFF_Open ( filename ) ) )
	{
		goto error;
	}

	if ( !IFF_NextForm ( iff ) || iff->FormID != FORM_BABYLONDB )
	{
		goto error;
	}

	dbinfo.next_id = -1;
	dbinfo.num_labels = 0;

	while ( IFF_NextChunk ( iff ))
	{
		switch (iff->ChunkID )
		{
			case CHUNK_INFO:
				IFF_READ ( iff, &dbinfo, sizeof ( dbinfo ), error );
				break;
		}
	}


	count = dbinfo.num_labels;

error:
	if ( iff )
	{
		IFF_Close ( iff );
	}
	return count;
}