/* vi: set sw=4 ts=4: */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

#include "defs.h"
#include "convert.h"
#include "hzinput.h"
#include "pinyin.h"
#include "zhuyin.h"
#include "cin2tab.h"

static char *phrase_fname;

typedef struct _HzPhrase
{
   u_char hz[MAX_PYPHRASE_LEN*2+1];
   PYFREQTYPE freq;
   struct _HzPhrase *next;
}HzPhrase;

typedef struct _KeyPhrase
{
   u_short len;
   u_char key[2*MAX_PYPHRASE_LEN+1];
   u_short  count;   /* number of Phrase items, in file it should be u_char */
   HzPhrase *hzph;
   struct _KeyPhrase *next;
}KeyPhrase, *PKeyPhrase;

static PKeyPhrase phtab[MAX_PY_NUM];
static u_short phcount[MAX_PY_NUM];

static char strarr[4*MAX_PYPHRASE_LEN][2*MAX_PYPHRASE_LEN+1];

static u_short KeyAhead(u_char *key)
{
#if 1 /* 9 bit */
  u_short py;
  py = (short)key[1];
  py |= (key[0] & 0x01) << 8;
  return py;
#else
	u_short py;
	py = '\0';
	py |= key[0] << 2 & 0x3ff;
	py |= key[1] >> 6;
	return py;
#endif
}

static void PYKey2Key(u_char *key, u_short *pykey, u_char len)
{
#if 1

	int i;

     for(i=0; i<len; i++)  /* pinyin, 9 bits, 8 bits plus eight 1 bit in key[0] */
         key[i+1] = pykey[i] & 0xff;

     key[0] = '\0';
     for(i=0; i<len; i++)
     key[0] |= (pykey[i] & 0x0100) >> (8-i);

#else

        int i,j,p;
        int klen=(len*10)/8 +1;

        for (j=0; j<klen; j++) key[j] = '\0';

        j=p=0;
        for (i=0; i< len; i++){
                key[j++] |= (pykey[i] & 0x3ff) >> (2 + p);
                key[j] |= pykey[i] << (6 - p) ;
                p += 2;
                if ( (p %= 8) == 0 )  j++;
        }
#endif
}

static int String2Array(char *strbuf,int len, int maxcount, char strarr[][2*MAX_PYPHRASE_LEN+1])
{
	int i=0, cursor=0, count=0, buflen = strlen(strbuf);

	strarr[0][0] = '\0';
	while(i < buflen)
	{
    	while(strbuf[i] == ' ' || strbuf[i] == '\t' || strbuf[i] == '\'')
     		i++;   /* skip space and ' */

		cursor = i;
	 
	 	if (strbuf[i] == '\0' || strbuf[i] == '#' || strbuf[i] == '\r' || strbuf[i] == '\n') 
	 		break;
	 
     	while(i < buflen && strbuf[i] != ' ' && strbuf[i] != '\t' && strbuf[i] != '\''
			&& strbuf[i] != '#' && strbuf[i] != '\r' && strbuf[i] != '\n')
        	i++;  /* skip non-space pinyins */

     	if (i > cursor)
     	{
			if (i - cursor >= len)
				cursor = i - len + 1; /* for safety, take the last len bytes, add one 0 */

       		strncpy(strarr[count],strbuf+cursor,i-cursor);
       		strarr[count++][i-cursor] = '\0';
			if (count >= maxcount) return count;
     	}
  	}

  	return count;
}

static int total_phrase = 0;

static int SavePhraseToMem(u_char *str,u_char *key, u_short len, PYFREQTYPE freq)
{
	PKeyPhrase kph,tmpkph;
  	HzPhrase  *hzph;
  	int first;
  	short ahead;
	static int hzlen[10];

  	if (len<1) return 0;

  	if (len > MAX_PYPHRASE_LEN) 
	{
      	fprintf(stderr, "Phrase %s(len %d) exceeded max phrase len(%d)\n", 
				str, len, MAX_PYPHRASE_LEN);
		return 0;
  	}

  	ahead = (short)KeyAhead(key);

  	kph = phtab[ahead];
  	if (kph != NULL)   /* first phrase of this pinyin */
  	{
    	first=1; 
    	do
    	{
      		if (first) first = 0; 
      		else kph = kph->next;

      		if (kph->len == len && !memcmp(kph->key,key, PYKEYLEN(len) ))
      		{
        		for(hzph = kph->hzph; hzph != NULL; hzph = hzph->next)
           		if ( len != 1 &&  !memcmp(hzph->hz,str,2*len))  /* same phrase */
           		{
#ifdef DEBUG
               		fprintf(stderr,"Duplicate phrase %s detected, ignored!\n",
                  		hzph->hz);
#endif
               		return 0; 
           		}

        		hzph = kph->hzph;
        		while(hzph->next != NULL)
          			hzph = hzph->next;  /* reach the end of the link list */

        		if ((hzph->next = (HzPhrase *)malloc(sizeof(HzPhrase)))==NULL)
        		{
          	 		fprintf(stderr,"SavePhraseToMem: no enough memory\n");
           			exit(1);
        		}
        		kph->count++;
        		hzph = hzph->next;
        		hzph->freq = freq;
        		hzph->next = NULL;
        		memcpy(hzph->hz,str,len*2);	/* len < MAX_PYPHRASE_LEN */
        		hzph->hz[len*2] = '\0';

				total_phrase++;

        		return 1;   /* insert a new Hanzi Phrase at the end of the link list */
      		}
    	}while(kph->next != NULL);
 	}

 	if ((tmpkph = (KeyPhrase *)malloc(sizeof(KeyPhrase))) == NULL)
 	{
     	fprintf(stderr,"SavePhraseToMem: not enough memory\n");
     	exit(1);
 	}
 	
	if (phtab[ahead] == NULL) 
    	phtab[ahead] = tmpkph;
 	else 
    	kph->next = tmpkph;
 	tmpkph->len = len;
 	tmpkph->count = 1;
 	memcpy(tmpkph->key,key,  PYKEYLEN(len) );	/* len < MAX_PYPHRASE_LEN */
 	tmpkph->next = NULL;

 	if ((tmpkph->hzph = (HzPhrase *)malloc(sizeof(HzPhrase))) == NULL)
 	{
    	fprintf(stderr,"no enough memory\n");
    	exit(1);
 	}
 	tmpkph->hzph->freq = freq;
 	tmpkph->hzph->next = NULL;
 	memcpy(tmpkph->hzph->hz,str,len*2);	/* len < MAX_PYPHRASE_LEN */
 	tmpkph->hzph->hz[len*2] = '\0';
 	phcount[ahead]++;

 	hzlen[len]++;

	total_phrase++;

 	return 1;
}

static int SavePhraseToFile(FILE *out, int type)
{
	KeyPhrase *kph,*kphtmp;
	HzPhrase *hzph,*hzphtmp;
	u_short j;
    u_char key[MAX_PYKEY_LEN];  
  	u_short len,/* count,*/ size;

	int max_count = 0;
	int file_size = 0;

 	for (j=1; j<MAX_PY_NUM; j++)
  	{
      	kph = phtab[j];  
      	file_size += 2; /*u_short */

      	fwrite(&(phcount[j]),sizeof(phcount[j]),1,out); /* u_short, size=2 */

      	while (kph != NULL)
      	{
        	hzph = kph->hzph;
        	kphtmp = kph;
        	kph = kph->next;

        	len = kphtmp->len;
			if (len > MAX_PYPHRASE_LEN) 
			{
	    		fprintf(stderr, "buffer overrun\n");
	    		abort();
			}
        	
			memcpy(key,kphtmp->key, PYKEYLEN(len) );
        	fwrite(&len,sizeof(len),1, out); /* + 2 */
      
        	size = kphtmp->count;

        	if (kphtmp->count >  65530 )   /* 2 bytes? */
        	{
           		fprintf(stdout,"Phrase Count = %d > 65535, error!!!\n",kphtmp->count);
           		exit(1);
        	}  
        	fwrite(&size,sizeof(size),1,out);  /* +2 */
        	fwrite(key,sizeof(u_char),PYKEYLEN(len),out);

        	if (kphtmp->count > max_count) max_count = kphtmp->count;

        	file_size += SizeOfPhrase(len,kphtmp->count);

        	while (hzph != NULL)
        	{
          		hzphtmp = hzph;
          		hzph = hzph->next;

          		fwrite(hzphtmp->hz,sizeof(char),len*2,out);
          		fwrite(&(hzphtmp->freq),sizeof(PYFREQTYPE),1,out);
          		free(hzphtmp);
        	}
        	free(kphtmp);
      	}
    }

	if (type == 1)
		file_size += sizeof(hz_input_table);

    fwrite(&file_size,sizeof(file_size),1,out);
    fprintf(stderr, "%s:\tFileSize=%d\tTotalPhraseNum=%d\n",
		phrase_fname, file_size+4, total_phrase);  /* last integer */

	total_phrase = 0; /* clear it up after saving */

    return 1;
}

#if 0
static void PrintPYMap(FILE *fp)
{
	int i, j, count;

	count = 0;
	for(i = 0; i < 26; i++)
	{
		for(j = 0; PYMap[i][j].key; j++)
			fprintf(fp, "%6d ahead=0x%04X %s\n", ++count, ((i+1) << 8) | (j+1), PYMap[i][j].py);
	}
}
#endif

int ConvertGBPhraseFormat(int mode, char *filename)
{
	FILE *stream;
	FILE *fout = stdout;
	int i;
	char str[512];
	u_short len;
  	int count,freq;
	u_char *p;

	YinFormat = PINYIN_FORMAT; /* default is pinyin */
	stream = fopen(filename, "r");
	if (!stream || !fout) return -1;

	fprintf(stderr, "Now converting GBPhrase file %s mode %d...\n", filename, mode);

 	while ( !feof( stream )) 
  	{
    	if ( fgets(str,sizeof(str),stream) != NULL) 
     	{
			p = FilterFormatOptions(str, SYSPH_MAP_STR);
			if (p == PTR_CONTINUE) 
			{
				fprintf(fout, "%s", str);
				continue;
			}
			else if (p == PTR_BREAK) 
			{
				fprintf(fout, "%s", str);
				break; /* end */
			}
			
         	count = String2Array(p, sizeof(strarr[0]), sizeof(strarr)/sizeof(strarr[0]), strarr);
         	len = strlen(strarr[0])/2;

         	if ((len != count-1 && len != count-2) || len > MAX_PYPHRASE_LEN ||
				 count == 0 || len < 1)
         	{
      	     	fprintf(stderr,"Phrase error len=%d maxlen=%d yin=%d: %s",
						 len, MAX_PYPHRASE_LEN, count-1, p);
 	     		continue;
         	}
         
         	if (len == count-2)
         	{
            	freq = atoi(mode == 1 ? strarr[1] : strarr[count-1]);
            	if (freq > 250) freq = 250;
            	count--;
         	}
         	else freq = 0;

			if (mode == 0)
			{
				fprintf(fout, "%s\t%d", strarr[0], freq);
     			for (i=1; i<count; i++)
					fprintf(fout, "%c%s", i==1? '\t' : ' ', strarr[i]);
				fprintf(fout, "\n");
			}
			else
			{
				fprintf(fout, "%s", strarr[0]);
				for(i=2; i<=count; i++)
					fprintf(fout, "%c%s", i==2? '\t' : ' ', strarr[i]);
				fprintf(fout, "\t%d\n", freq);
			}

    	}
  	}
   
	fclose(stream);
  	return (0);
}

static int HandleZhuyinFormat(u_char *p, u_char *c, int len, int freq, FILE *fout)
{
	int yincount;
	u_short  yin[30][MAX_ZHUYIN_NUMBER];
	int i; 
  	u_char key[MAX_PYPHRASE_LEN+1];
  	u_short pykey[MAX_PYPHRASE_LEN];
 
	memset( yin, 0, sizeof(yin));
	yincount = convert_to_zhuyins(ZHUYIN_FORMAT, p, yin, len);
	if (yincount < len)
	{
#ifdef DEBUG
		fprintf(stderr, "Phrase %s(len=%d) has only %d yins: %s!\n", c, len, yincount, p);
#endif
		return -1;
	}
	
	if (len == 1) /* single char, output all the yins without tones */
	{
		for(i = 0; yin[0][i]; i++)
		{
			u_short index = ZYToPYMap[yin[0][i] & 0x07FF];
			
			if (fout)
				fprintf(fout, "%s                   %s %8d\n", 
						c, GET_PYMAP_PY(index), freq);

			pykey[0] =  INDEX_TO_PYMAP(index).key;
	
			PYKey2Key(key, pykey, len);
			SavePhraseToMem(c,key,len,freq); /* save char for each yin */
		}
	}
	else  /* multi-char, phrase only output the first yin? */
	{
		if (fout) 
		{
			fprintf(fout, "%s ", c);
			for(i = 20-2*len; i > 0; i--)
				fprintf(fout, "%c", ' ');
		}

		for(i = 0; i < len; i++)
		{
			u_short index = ZYToPYMap[yin[i][0] & 0x07FF];

			if (index == 0) 
			{
				fprintf(stderr, "Unknow yin 0x%04X Phrase %s(len=%d)\n", index, c,len);
				fprintf(fout, " ????? %d\n", freq);
				return -1;
			}
			
			pykey[i] = INDEX_TO_PYMAP(index).key;
			if (fout)
				fprintf(fout, "%s ", GET_PYMAP_PY(index));
		}
		if (fout)
			fprintf(fout, " %d\n", freq);

		PYKey2Key(key, pykey, len);
		SavePhraseToMem(c,key,len,freq);
	}
	return 0;
}

static int LoadOnePinyinAndConvert(char *py, char *str)
{
	int j, ahead, flag = 0, len;
	
   	ahead = (int)py[0]-'a';
	flag = 0;
	if (ahead<0 || ahead>25)
   	{
   		fprintf(stderr,"Phrase error invalid pinyin: %s, py %s", str, py);
		return -1;
	}

	len = strlen(py);
	if (strchr("012345", py[len-1]))
		py[len-1] = '\0';  /* override the tone 0~5 */

	for(j=0; PYMap[ahead][j].key; j++)
   	{
   		if (!strcmp(PYMap[ahead][j].py,py))
       	{
   			return PYMap[ahead][j].key;
   		}
	}
	    		
	if (!flag) 
	{
#ifdef DEBUG
		fprintf(stderr,"Phrase error no pinyin mapping found: %s", str);
#endif
	}

	return -1;
}

static int LoadPhraseFromFile(FILE *stream)
{
	int i, ret;
	int len, count, freq;
 	u_char key[MAX_PYPHRASE_LEN+1];
  	u_short pykey[MAX_PYPHRASE_LEN];
	u_char *p, *c;

	YinFormat = PINYIN_FORMAT; /* default is pinyin */

 	while ( !feof( stream )) 
  	{
    	if ( fgets(LineBuf,LINE_BUFFER_LEN,stream) != NULL) 
     	{
			p = FilterFormatCharFreq(LineBuf, SYSPH_MAP_STR, &c, &len, &freq, 
				YinFormat == ZHUYIN_FORMAT ? 1: 0 );
			
			if (p == PTR_CONTINUE) continue;
			else if (p == PTR_BREAK) break; /* end */

			if (YinFormat == ZHUYIN_FORMAT)
			{
				HandleZhuyinFormat(p, c, len, freq, NULL); /*  stdout); */
				continue;	
			}
			 
         	count = String2Array(p, sizeof(strarr[0]), sizeof(strarr)/sizeof(strarr[0]), strarr);
         	if ( len > MAX_PYPHRASE_LEN || len < 1 || count < len || count % len != 0) 
         	{
      	     	fprintf(stderr,"Phrase error len=%d maxlen=%d yin=%d: %s",
						 len, MAX_PYPHRASE_LEN, count, p);
 	     		continue;
         	}
    
			count -= count % len;
     		for (i=0; i < count; i++)
         	{
				ret = LoadOnePinyinAndConvert(strarr[i], p);
				if (ret < 0) /* fail! */
				{
#ifdef DEBUG
#endif
					break;
				}
				pykey[i % len] = (u_short)ret;

				if (((i+1) % len) == 0) /* a complete yin */
				{
       				PYKey2Key(key, pykey, len);
        			SavePhraseToMem(c,key,len,freq);
				}
			}
    	}
  	}
   
  	return (0);
}

int ConvertPinyinCIN(FILE *fr, hz_input_table *table, char *filename )
{
	int i=0;
	FILE *fw;

	OldCoding = NewCoding = table->encoding;
	
	if ((fw=fopen(filename,"w"))==NULL) 
		print_error("Failed to create output file!");

	phrase_fname = filename;

	fwrite(table,sizeof(*table),1, fw);

#if 0
	cmd_arg(fr,LineBuf, &cmd, &arg);
	if (LoadFileSection( cmd, arg, PINYIN_MAP_STR, fr, LoadPinyinTable))
		print_error("%" PINYIN_MAP_STR " [ <FILE_NAME> | begin ] expected!");		
		
	fwrite(&PYMap, sizeof(PYMap),1, fw); /* write the pinyin tab	 */

	cmd_arg(fr,LineBuf, &cmd, &arg);
	if (LoadFileSection( cmd, arg, PYZY_MAP_STR, fr, LoadZhuyinToPinyinMap))
		print_error("%" PYZY_MAP_STR " [ <FILE_NAME> | begin ] expected!");

	fwrite(&ZYToPYMap, sizeof(ZYToPYMap), 1, fw);
#endif

	if (LoadFileSection( cmd, arg, SYSPH_MAP_STR, fr, LoadPhraseFromFile))
          print_error("%" SYSPH_MAP_STR " [ <FILE_NAME> | begin ] expected!");

	do
	{
		cmd_arg(fr,LineBuf, &cmd, &arg);
	}
	while (LoadFileSection( cmd, arg, SYSPH_MAP_STR, fr, LoadPhraseFromFile) == 0);

	SavePhraseToFile(fw, 1); /* 1 for system */
	fclose(fw);

	if (!cmd[0] || !arg[0] || strcasecmp(cmd, USRPH_MAP_STR))
		return 0;

	strcat(filename, ".usr");
	if ( (fw = fopen(filename,"w"))==NULL)
        print_error("Failed to create pinyin user phrase output file %s", filename);

	for(i = 0; i < MAX_PY_NUM; i++)
	{
    	phtab[i] = NULL;
    	phcount[i] = 0;
  	}

	while(LoadFileSection(cmd, arg, USRPH_MAP_STR, fr, LoadPhraseFromFile) == 0)
	{

		cmd_arg(fr,LineBuf, &cmd, &arg);
	}

	SavePhraseToFile(fw, 0); /* 0 for user */
	fclose(fw);

	fclose(fr);

	return 0;
}

