/*
 ***********************************************************************
 *
 *                           Coryright (c)
 *    © Digital Equipment Corporation 1996, 1998. All rights reserved.
 *
 *    Restricted Rights: Use, duplication, or disclosure by the U.S.
 *    Government is subject to restrictions as set forth in subparagraph
 *    (c) (1) (ii) of DFARS 252.227-7013, or in FAR 52.227-19, or in FAR
 *    52.227-14 Alt. III, as applicable.
 *
 *    This software is proprietary to and embodies the confidential
 *    technology of Digital Equipment Corporation and other parties.
 *    Possession, use, or copying of this software and media is authorized
 *    only pursuant to a valid written license from Digital or an
 *    authorized sublicensor.
 *
 ***********************************************************************
 *    File Name:	usedic.c
 *    Author:		
 *    Creation Date:
 *
 *    Functionality:
 *		User dictionary compiler ...
 *
 ***********************************************************************
 *    Revision History:
 *
 * Rev	Who		Date		Description
 * ---	----	-----------	--------------------------------------------
 * 001	TQL		04/02/97	For BATS #302.  Fixed the d_dentalized symbol.
 * 							For BATS #325.  Added ptab and arpabet tables for
 *							SPANISH and GERMAN. Also changed the upper_case[]
 *							table to handle all languages.
 * 002	GL		07/18/97	For BATS#372 and 373 to allow all the alphabet and "."
 *							also use "unsigned char" for legal_alpha() argument
 * 003	GL		10/20/97	Add ENGLISH_UK support.
 * 004  GL              10/28/97        Add ir,er,ar,or,ur,df for US.
 * 005	MGs		01/071998	Converted to ML support
 * 006  JAW     02/04/1998  For BATS #252.  When compiling, dictionary files are now closed before
 *                          the Success box is displayed.
 * 007  MFG             02/06/98        BATS #592 had the legal char ignore appostrophy "'"
 */

#include <windows.h>
#include <stddef.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
#include <malloc.h>
#include "port.h"
#include "cmd.h"
#include "tts.h"
#include "ttsapi.h"

U32 TextToSpeechReserved3(LPTTS_HANDLE_T phTTS, int type,char *data,int max_size);

/*
 * general defines ...
 */

#define	true	1
#define	false   0
#define	failure 1
#define	success 0

/*
 *  User dictionary structure and size defines ...
 */

#define  AVERAGE_WORD_LENGTH  20

typedef struct dict_link
{
	U32     	text;
	unsigned int	link;
} DICT_LINK_T;

unsigned int first_ent;

unsigned int uiMaxDictEntries;
unsigned int uiMaxTextLength;

unsigned char *	pDictText;
unsigned int text_bytes;

DICT_LINK_T * pEntries;    /* sort array */
unsigned int free_ent;
unsigned int first_ent;

unsigned int line_number;
unsigned char line[256];
int curr_char;
FILE	*ftab,*fdic;
HWND hWnd;

/* the phtts pointer to be bale to get the phoneme tables */
extern LPTTS_HANDLE_T phTTS[10];
extern int tts_cur;

unsigned char	*arpabet=NULL;

/*
 *  single character phoneme conversion ...
 */

typedef	struct	PTAB {
	char	p_graph;		/* Graphic code.		*/
	char	p_phone;		/* Phonemic code.		*/
}	PTAB;


static PTAB	*ptab=NULL;			/* Phoneme name table (again)	*/


int arpa_size;
int NPTAB;
int asky_size;

/*
 *  prototypes ...
 */

BOOL to_ascky();
BOOL lookup_ascky();
BOOL to_arpabet();
int lookup_arpabet();
BOOL push_entry();
unsigned char non_white();
unsigned char get_alpha();
BOOL scan_entry();
BOOL sort_entry();
BOOL get_entry();
BOOL scan_error( char *);
BOOL legal_alpha( unsigned char );

/*
 *  Upper case folding table ...  sort of like above but only the 
 *  lower case ascii is folded to upper and the full eight bits are
 *  kept.
 */
unsigned char case_upper[] = {
	0x00,		0x01,		0x02,		0x03,		0x04,		0x05,		0x06,		0x07,
	0x08,		0x09,		0x0a,		0x0b,		0x0c,		0x0d,		0x0e,		0x0f,
	0x10,		0x11,		0x12,		0x13,		0x14,		0x15,		0x16,		0x17,
	0x18,		0x19,		0x1a,		0x1b,		0x1c,		0x1d,		0x1e,		0x1f,
	0x20,		0x21,		0x22,		0x23,		0x24,		0x25,		0x26,		0x27,
	0x28,		0x29,		0x2a,		0x2b,		0x2c,		0x2d,		0x2e,		0x2f,
	0x30,		0x31,		0x32,		0x33,		0x34,		0x35,		0x36,		0x37,
	0x38,		0x39,		0x3a,		0x3b,		0x3c,		0x3d,		0x3e,		0x3f,
	0x40,		0x41,		0x42,		0x43,		0x44,		0x45,		0x46,		0x47,
	0x48,		0x49,		0x4a,		0x4b,		0x4c,		0x4d,		0x4e,		0x4f,
	0x50,		0x51,		0x52,		0x53,		0x54,		0x55,		0x56,		0x57,
	0x58,		0x59,		0x5a,		0x5b,		0x5c,		0x5d,		0x5e,		0x5f,
	0x60,		'A',		'B',		'C',		'D',		'E',		'F',		'G',
	'H',		'I',		'J',		'K',		'L',		'M',		'N',		'O',
	'P',		'Q',		'R',		'S',		'T',		'U',		'V',		'W',
	'X',		'Y',		'Z',		0x7b,		0x7c,		0x7d,		0x7e,		0x7f,
	0x80,		0x81,		0x82,		0x83,		0x84,		0x85,		0x86,		0x87,
	0x88,		0x89,		0x8a,		0x8b,		0x8c,		0x8d,		0x8e,		0x8f,
	0x90,		0x91,		0x92,		0x93,		0x94,		0x95,		0x96,		0x97,
	0x98,		0x99,		0x9a,		0x9b,		0x9c,		0x9d,		0x9e,		0x9f,
	0xa0,		0xa1,		0xa2,		0xa3,		0xa4,		0xa5,		0xa6,		0xa7,
	0xa8,		0xa9,		0xaa,		0xab,		0xac,		0xad,		0xae,		0xaf,
	0xb0,		0xb1,		0xb2,		0xb3,		0xb4,		0xb5,		0xb6,		0xb7,
	0xb8,		0xb9,		0xba,		0xbb,		0xbc,		0xbd,		0xbe,		0xbf,
	0xc0,		0xc1,		0xc2,		0xc3,		0xc4,		0xc5,		0xc6,		0xc7,
	0xc8,		0xc9,		0xca,		0xcb,		0xcc,		0xcd,		0xce,		0xcf,
	0xd0,		0xd1,		0xd2,		0xd3,		0xd4,		0xd5,		0xd6,		0xd7,
	0xd8,		0xd9,		0xda,		0xdb,		0xdc,		0xdd,		0xde,		0xdf,
	'À',		'Á',		'Â',		'Ã',		'Ä',		'Å',		0xe6,		'Ç',
	'È',		'É',		'Ê',		'Ë',		'Ì',		'Í',		'Î',		'Ï',
	0xf0,		'Ñ',		'Ò',		'Ó',		'Ô',		'Õ',		'Ö',		0xf7,
	'Ø',		'Ù',		'Ú',		'Û',		'Ü',		'Ý',		0xfe,		0xff
};


//main( int argc, *argv[])
BOOL UserDic( HWND hWindow,
              UINT uiDictEntries,
              UINT uiTextLength,
              LPSTR fnam_t,
              LPSTR fnam_d )
{

//	char	fnam_t[80];
//	char	fnam_d[80];
	int	i;
	unsigned int next;
	U32     tl;
	U32     sizes;
	char    szMsg[256];

	hWnd = hWindow;

	arpa_size=TextToSpeechReserved3(phTTS[tts_cur],1,NULL,0);
	
	if (arpabet!=NULL)
	{
		free(arpabet);
		arpabet=NULL;
	}
	arpabet=(unsigned char *)malloc(arpa_size*sizeof(char));
	if (arpabet==NULL)
	{
	    sprintf(szMsg, "Unable to allocate enough memory."
		        "\n\nCompile was unsuccessful.", fnam_t );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return(FALSE);
	}
	if (TextToSpeechReserved3(phTTS[tts_cur],2,arpabet,arpa_size)<0)
	{
	    sprintf(szMsg, "Unable to retrieve phoneme table."
		        "\n\nCompile was unsuccessful.", fnam_t );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return(FALSE);
	}
	asky_size=TextToSpeechReserved3(phTTS[tts_cur],3,NULL,0);
	if (ptab!=NULL)
	{
		free(ptab);
		ptab=NULL;
	}
	ptab=(PTAB *)malloc(asky_size*sizeof(char));
	if (ptab==NULL)
	{
	    sprintf(szMsg, "Unable to allocate enough memory."
		        "\n\nCompile was unsuccessful.", fnam_t );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return(FALSE);
	}
	if (TextToSpeechReserved3(phTTS[tts_cur],4,(char *)ptab,asky_size)<0)
	{
	    sprintf(szMsg, "Unable to retrieve phoneme table."
		        "\n\nCompile was unsuccessful.", fnam_t );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return(FALSE);
	}
	NPTAB=asky_size/sizeof(PTAB);






  /********************************************************************/
  /*  Allocate memory for dictionary data and entries.                */
  /********************************************************************/

  uiMaxDictEntries = uiDictEntries;
  uiMaxTextLength = uiTextLength;

  pEntries = (DICT_LINK_T *)
    malloc( uiDictEntries * sizeof(DICT_LINK_T));
  pDictText = (unsigned char *)
    malloc( uiTextLength * sizeof(char));

  if (( pEntries == NULL ) || ( pDictText == NULL ))
  {
    sprintf(szMsg, "Unable to allocate enough memory."
            "\n\nCompile was unsuccessful.", fnam_t );
    MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );

    if ( pEntries != NULL )
      free( pEntries );
    if ( pDictText != NULL )
      free( pDictText );
    return( FALSE );
  }

/*
 *  check out the input line parameters ...
 */
/*	printf("DECtalk user dictionary compiler version 1.0\n");
	if (argc < 2)
		{
		printf("\n Usage : userdic <input text file>");
		printf(" <output dictionary file>\n");
		exit(1);
		}
*/
/*
 *  open the build file ...
 */

/*	strcpy(fnam_t,argv[1]);
	i = strcspn(fnam_t,".");
	strcpy(&fnam_t[i],".tab");
*/	if ((ftab=fopen(fnam_t,"r")) == NULL)
		{
		sprintf(szMsg, "Input dictionary table file %s\ncan not be found."
			       "\n\nCompile was unsuccessful.", fnam_t );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return( FALSE );
		}

/*	if(argc > 2)
		strcpy(fnam_d,argv[2]);
	else
		strcpy(fnam_d,argv[1]);
*/
	i = strcspn(fnam_d,".");
	strcpy(&fnam_d[i],".dic");
	if ((fdic=fopen(fnam_d,"w+b")) == NULL)
		{
		sprintf( szMsg, "Can't open user dictionary file %s\nCompile was unsuccessful.",
				fnam_d );
		MessageBox( hWnd, szMsg, "Error", MB_OK | MB_ICONSTOP );
		return( FALSE );
		}


	text_bytes = free_ent = line_number = 0;
	first_ent = 0xFFFFFFFF;

	while(get_entry())
		{
                  if( !scan_entry())
                  {
                    free( pEntries );
                    free( pDictText );
                    return( FALSE );
                  }
                  if( !sort_entry())
                  {
                    free( pEntries );
                    free( pDictText );
                    return( FALSE );
                  }
		}
/*
 *  convert the sizes to longs for the write	...
 */
	sizes = free_ent;
	fwrite(&sizes,4,1,fdic);
	sizes = text_bytes;
	fwrite(&sizes,4,1,fdic);

/*
 *  expand the links to far pointer ... when this is loaded, the first
 *  text byte will be 4*number of links, so add this to each offset and
 *  write a zero out for the segment to be fixed up by the loader.
 */
	for( next=first_ent;next!= 0xFFFFFFFF;next=(pEntries+next)->link )
		{
		tl = (pEntries+next)->text-4;
		fwrite(&tl,4,1,fdic);
		}
	fwrite(pDictText,text_bytes,1,fdic);

	fclose(fdic);
	fclose(ftab);
	
	sprintf( szMsg, "Saving text file as %s\n\nSaving dictionary file as %s\n\n",
			fnam_t, fnam_d );
	sprintf( &szMsg[strlen( szMsg)],
		"Compiled %d user dictionary entries into %d bytes.",
		free_ent,text_bytes);
	MessageBox( hWnd, szMsg, "Success!", MB_OK );

    free(pEntries);
    free(pDictText);

	return( TRUE );
}

/*
 *  sort_entry() sets the flink/blink for the current entry ...
 */


BOOL sort_entry()
{
	unsigned char *ct,*dt;
	unsigned int *blink,next;

	blink = &first_ent;
	next = first_ent;
	while(next != 0xFFFFFFFF)
		{
		ct = pDictText+((pEntries+free_ent)->text);
		dt = pDictText+((pEntries+next)->text);
		while (*ct != 0x00 && *dt != 0x00)
			{
			if(case_upper[*ct] != case_upper[*dt])
				break;
			ct += 1;
			dt += 1;
			}
		if(*ct == 0x00 && *dt == 0x00)
			return( scan_error("Duplicate entry"));
		if((case_upper[*ct] < case_upper[*dt]) || (*ct == 0x00))
			break;
		if(case_upper[*ct] > case_upper[*dt])
			{
			blink = &((pEntries+next)->link);
			next = (pEntries+next)->link;
			}
		}
	(pEntries+free_ent)->link = next;
	*blink = free_ent;
	free_ent+=1;
	return( TRUE );
}

/*
 *  scan_entry_line() takes the input text and makes it into a
 *  valid dictionary entry ...
 *     entry format is ...
 *
 *  <entry> [2 char phonemes]
 *  <entry> {1 char phonemes}
 */

BOOL scan_entry()
{
	unsigned char c;

	if(free_ent == uiMaxDictEntries )
		return( scan_error("Too many entries"));

	(pEntries+free_ent)->text = text_bytes;
	while(true)
		{
		c = get_alpha();
		if(c == ' ')
		    break;
		else
		    if(c == (unsigned char)NULL )
		       return( scan_error("Bad alphabetic character") );
		else
			if( !push_entry(c))
			  return( FALSE );
		}
	if( !push_entry(0x00))
	  return( FALSE );
	c = non_white();
	if(c == '[')
		return( to_arpabet() );
	else if(c == '{')
		return( to_ascky());
	else
		return( scan_error("Unknown phoneme type") );
	return( TRUE );
}

/*
 *  get_entry() reads in a line from the file and returns it to
 *  the caller if it is a dictionary entry.
 */

BOOL get_entry()
{
	curr_char = 0;
	while(fgets(line,sizeof(line),ftab))
		{
		line_number += 1;
		if(legal_alpha(line[0]))
			return( TRUE );
		}
	return( FALSE );
}

BOOL to_ascky()
{
	unsigned char ph;

	while(line[curr_char] != '\0')
		{
		ph = non_white();
		if(ph == '}')
		  return( TRUE );
		if( !lookup_ascky(ph))
		  return( FALSE );
		}
	return( scan_error("No ascky terminator found")) ;
}

BOOL lookup_ascky(ph)
unsigned char ph;
{
	int	i;

	for(i=0;i<NPTAB;i++)
		if(ph == ptab[i].p_graph)
			{
			return( push_entry(ptab[i].p_phone));
			}
	return( scan_error("Unknown ascky phoneme found"));
}

/*
 *  this routine scans the input line for arpabet phonemes ...
 */


BOOL to_arpabet()
{
	unsigned char ph1,ph2,count;

	count = 2;
	while(( line[curr_char] != '\0' ) || ( line[curr_char] != EOF ))
		{
		if(count == 2)
			ph1 = non_white();
		if(ph1 == ']')
                {
                  push_entry(0);  /* WIH Added 12/7/93 */
                  return( TRUE );
                }
		ph2 = non_white();
		count = lookup_arpabet(ph1,ph2);
		if(count == 0)
			return( scan_error("Unknown arpabet phoneme"));
		if(count == 1)
			ph1 = ph2;
		}
	return( scan_error("No arpabet terminator found"));
}

lookup_arpabet(p1,p2)
unsigned char p1,p2;
{
	int	i;

	if(p1 >= 'A' && p1 <= 'Z')
		p1 |= 0x20;
	if(p2 >= 'A' && p2 <= 'Z')
		p2 |= 0x20;
	for(i=0;i<arpa_size;i+=2)
		if(p1 == arpabet[i] && p2 == arpabet[i+1])
			{
			if( !push_entry(i/2))
			  return( 0 );
			return(2);
			}
	for(i=0;i<arpa_size;i+=2)
		if(p1 == arpabet[i] && arpabet[i+1] == ' ')
			{
			if( !push_entry(i/2) )
			  return( 0 );
			return(1);
			}
	return(0);
}

/*
 *  scan_error() is used to display an error message during the building
 *  of the user dictionary ...
 */

BOOL scan_error(es)
char *es;
{
	int	i;
	static  char szError[128];

	sprintf( szError, "Error at line %d : %s\n--> %s",line_number,es, line);
	MessageBox( hWnd, szError, "Dictionary compiler error", MB_OK | MB_ICONSTOP );
	for(i=0;i<curr_char-1;i++)
		printf(" ");
	printf("^");
	fclose(ftab);
	fclose(fdic);
	return( FALSE );
}

/*
 *  non_white() is used to return the next non white space character
 *  and bump the pointer ...
 */

unsigned char non_white()
{
	while(line[curr_char] == ' ' || line[curr_char] == '\t')
		curr_char++;
	return(line[curr_char++]);
}

/*
 *  This routine checks for legal graphemes in the user dict grapheme
 *  string.
 */

unsigned char get_alpha()
{
	if(legal_alpha(line[curr_char]))
			return(line[curr_char++]);
	if(line[curr_char] == ' ' || line[curr_char] == '\t')
		return(' ');
	scan_error("Unknown alphabetic character");
	return( (unsigned char) NULL );
}

unsigned char legal_graphs[] = "._-()&@*!\\/'";
BOOL legal_alpha(c)
unsigned char c;
{
/*        int     i;*/
	/*
	   GL 07/18/1997 BATS#372 and 373, need to support all the international charcter
	   and "." into the legal_graphs[], also use "unsigned char" for c
	   We need to change this code in the future to allow all the possible
	   characters.
	*/
/*        if((c >= 'a' && c <= 'z')       || (c >= 'A' && c <= 'Z') ||
	   (c >= 0xc0 && c <= 0xdd)	|| (c >= 0xe0 && c <= 0xfd) ||
	   (c >= '0' && c <= '9'))
			return( TRUE );
*/
/*        for(i=0;i<sizeof(legal_graphs);i++)*/
/*                if(c == legal_graphs[i])*/
/*                        return( TRUE );*/
/*        return( FALSE );*/

/* mfg allow any grapheme except  ' ','\n','\t'*/
        if(c == ' ' || c == '\n' || c == '\t')
                return(false);
        return(true);

}

BOOL push_entry(c)
unsigned char c;
{
   if ( text_bytes == uiMaxTextLength )
      return( scan_error("Dictionary too big"));
   *(pDictText+text_bytes) = c;
   text_bytes++;
   return( TRUE );
}
