/*
 * Copyright (c) 1991-1994  Sony Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL SONY CORPORATION BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Sony Corporation
 * shall not be used in advertising or otherwise to promote the sale, use
 * or other dealings in this Software without prior written authorization
 * from Sony Corporation.
 *
 */

/*
 * $SonyRCSfile: makelist.c,v $
 * $SonyRevision: 1.1 $
 * $SonyDate: 1994/06/03 08:00:39 $
 */

#include "sj4_dict_const.h"
#include "sj4_dict_struct.h"

#include "sj4mkdic.h"

#ifndef SS2
#define SS2 0x8e
#endif

static DouonRec* drectmp = NULL;

static void
clearklist(KanjiRec* krec) {
	KanjiRec* p;

	while(krec) {
		Free(krec->kptr);
		Free(krec->aptr);

		p    = krec;
		krec = krec->knext;

		Free(p);
	}
}

static void
clearhlist(HinsiRec* hrec) {
	HinsiRec* p;

	while(hrec) {
		clearklist(hrec->krec);

		p    = hrec;
		hrec = hrec->hnext;

		Free(p);
	}
}

void clear_list() {
	DouonRec* p;
	DouonRec* drec = douon_ptr;

	while(drec) {
		Free(drec->yptr);

		clearhlist(drec->hrec);

		p    = drec;
		drec = drec->dnext;

		Free(p);
	}

	douon_ptr = NULL;
}

static u_char*
makekanji(int* yomi, int* kanji, int* atr, int* len) {
	int	kana[MAXYOMILENGTH + 1];
	u_char	ktmp[MAXKANJILENGTH * 3 + MAXATRNUMBER * 2 + 1];
	int	i;
	int	pos = 0;
	int*	p;
	u_char* q;

	for(p = yomi, i = 0; *p;)
		kana[i++] = h2kcode(*p++);
	kana[i] = 0;

	while(*atr) {
		i = *atr;
		atr++;

		if(i & KANAKANCOST_BIT) {
			i &= ~KANAKANCOST_BIT;
			ktmp[pos++] = (KANAKANCOST | ((i >> 8) & 0xff));
			ktmp[pos++] = (i & 0xff);
		} else {
#ifndef NO_ATR
			ktmp[pos++] = (AIATTRIBUTE | ((i >> 8) & 0xff));
			ktmp[pos++] = (i & 0xff);
#endif
		}
	}

	if((i = top_strcmp(yomi, kanji))) {
		kanji += i;
		ktmp[pos++] = (ZENHIRAASSYUKU | (i - 1));
	} else if((i = top_strcmp(kana, kanji))) {
		kanji += i;
		ktmp[pos++] = (ZENKATAASSYUKU | (i - 1));
	}

	while(*kanji) {
		if((i = last_strcmp(yomi, kanji))) {
			kanji += i;
			ktmp[pos++] = (ZENHIRAASSYUKU | (i - 1));
		} else if((i = last_strcmp(kana, kanji))) {
			kanji += i;
			ktmp[pos++] = (ZENKATAASSYUKU | (i - 1));
		} else if(((*kanji >> 8) & 0xff) == SS2 || *kanji < 0x100) {
			ktmp[pos]     = LEADINGHANKAKU;
			ktmp[pos + 1] = (*kanji & 0xff);
			kanji++;
			pos += 2;
		} else {
			if(*kanji < 0x10000) {
				ktmp[pos]     = ((*kanji >> 8) & NORMALKANJIMASK);
				ktmp[pos + 1] = (*kanji & NORMALKANJIMASK);
				kanji++;
				pos += 2;
			} else if(*kanji < 0x1000000) {
				ktmp[pos]     = ((*kanji >> 8) & NORMALKANJIMASK);
				ktmp[pos + 1] = (*kanji & 0xff);
				kanji++;
				pos += 2;
			} else {
				fprintf(stderr, "Error: 4 byte code is found\n");
				exit(0);
			}
		}
	}
	*len = pos;

	q = (u_char*)Malloc(pos);
	if(!q) {
		fprintf(stderr, MEMINSUFFICIENT);
		exit(1);
	}
	memcpy(q, ktmp, pos);

	return q;
}

static u_char*
makeyomi(int* yomi) {
	u_char	tmp[MAXYOMILENGTH + 1];
	int	i;
	int	j;
	int*	y = yomi;
	u_char* p;

	for(i = 0; *y;) {
		j = cnvyomi(*y++);
		if(j == 0) {
			fprintf(stderr,
#ifdef ENGLISH
				"Invalid character used for pronunciation\n"
#else
				"\311\324\300\265\244\312\312\270\273\372\244\254\306\311\244\337\244\313\273\310\244\357\244\354\244\306\244\244\244\336\244\271\n"
#endif
			);
			output_int(stderr, yomi);
			fputc('\n', stderr);
			exit(1);
		}
		tmp[i++] = j;
	}
	tmp[i++] = 0;

	p = (u_char*)Malloc(i);
	if(!p) {
		fprintf(stderr, MEMINSUFFICIENT);
		exit(1);
	}
	memcpy(p, tmp, i);

	return p;
}

static KanjiRec*
make_krec(u_char* kcode, int klen) {
	KanjiRec* krec;

	krec = (KanjiRec*)Malloc(sizeof(KanjiRec));
	if(!krec) {
		fprintf(stderr, MEMINSUFFICIENT);
		exit(1);
	}

	krec->klen  = klen;
	krec->kptr  = kcode;
	krec->alen  = 0;
	krec->aptr  = NULL;
	krec->knext = NULL;

	return krec;
}

static HinsiRec*
make_hrec(int hinsi) {
	HinsiRec* hrec;

	hrec = (HinsiRec*)Malloc(sizeof(HinsiRec));
	if(!hrec) {
		fprintf(stderr, MEMINSUFFICIENT);
		exit(1);
	}

	hrec->hinsi = hinsi;
	hrec->krec  = NULL;
	hrec->hnext = NULL;

	return hrec;
}

static DouonRec*
make_drec(u_char* ycode) {
	DouonRec* drec;

	drec = (DouonRec*)Malloc(sizeof(DouonRec));
	if(!drec) {
		fprintf(stderr, MEMINSUFFICIENT);
		exit(1);
	}

	drec->yptr     = ycode;
	drec->hrec_num = 0;
	drec->hrec     = NULL;
	drec->dnext    = NULL;

	return drec;
}

static int
diff_ylen(DouonRec* drec) {
	DouonRec* dptr;
	DouonRec* dprev;
	u_char*	  p1;
	u_char*	  p2;
	int	  ylen = 0;

	dptr  = douon_ptr;
	dprev = NULL;
	while(dptr) {
		dprev = dptr;
		dptr  = dptr->dnext;
	}

	if(dprev) {
		p1 = dprev->yptr;
		p2 = drec->yptr;
		while(*p1 && (*p1 == *p2)) {
			p1++;
			p2++;
		}

		if(*p1 == *p2) {
			fprintf(stderr, HOMOPHONEBLOCK);
			output_yomi(stderr, dprev->yptr);
			fputc('\n', stderr);
			exit(1);
		}

		/* XXX: this does not always work... */
#if 0
		else if(*p1 > *p2) {
			fprintf(stderr, WRONGREADORDER);
			output_yomi(stderr, dprev->yptr);
			fputc('\n', stderr);
			output_yomi(stderr, drec->yptr);
			fputc('\n', stderr);
			exit(1);
		}
#endif
		while(*p2++)
			ylen++;
	}

	return ylen;
}

static int
douon_knj(DouonRec* drec) {
	int	  i;
	int	  len = 0;
	u_char*	  p;
	u_char*	  knjofscvt(u_char*, int, int*);
	HinsiRec* hrec;
	KanjiRec* krec;

	for(hrec = drec->hrec; hrec; hrec = hrec->hnext) {
		for(krec = hrec->krec; krec; krec = krec->knext) {
			p = knjofscvt(krec->kptr, krec->klen, &i);

			len += i + 1;

			knjhnd_set(p, i);

			krec->alen = i;
			krec->aptr = p;

			set_ofsrec(krec->kptr, krec->klen, 0);
		}
	}

	return len;
}

static void
make_d_list(DouonRec* drec) {
	DouonRec *dptr, *dprev;
	HinsiRec* hptr;
	KanjiRec* kptr;
	int	  ylen;
	int	  klen;
	int	  hnum;
	int	  len;
	int	  i;

start:
	if(douon_ptr == NULL) {
		douon_num = 0;
		yomi_len  = 0;
		hinsi_num = 0;
		kanji_len = 0;
		assyuku	  = NULL;
	}

	klen = douon_knj(drec);

	ylen = diff_ylen(drec);

	hnum = drec->hrec_num;

#if 1
	i = 0x100 + /* knjask can go up to 255 */
#else
	i = 1 +
	    MAXKNJASKNUMBER +
#endif
	    douon_num * DOUONBLKSIZENUMBER + DOUONBLKSIZENUMBER +
	    yomi_len + ylen +
	    hinsi_num + hnum +
	    hinsi_num + hnum +
	    kanji_len + klen;

	len = (i <= MAINSEGMENTLENGTH) ? 0 : decide_knjask();

	if((i - len) <= MAINSEGMENTLENGTH) {
		if(douon_ptr) {
			dptr  = douon_ptr;
			dprev = NULL;
			while(dptr) {
				dprev = dptr;
				dptr  = dptr->dnext;
			}

			dprev->dnext = drec;
		} else {
			douon_ptr = drec;
		}
		drec->dnext = NULL;

		drec->dlen = ylen;

		douon_num += 1;

		yomi_len += ylen;

		hinsi_num += hnum;

		kanji_len += klen;

		return;
	} else if(douon_ptr == NULL) {
		fprintf(stderr, TOOLARGEBLOCK);
		exit(1);
	}

	for(hptr = drec->hrec; hptr; hptr = hptr->hnext) {
		for(kptr = hptr->krec; kptr; kptr = kptr->knext) {

			knjhnd_reset(kptr->aptr, kptr->alen);

			Free(kptr->aptr);
			kptr->aptr = NULL;
		}
	}

	len = decide_knjask();

	makeseg();

	clear_list();

	clear_hindo();

	clear_ofsrec();

	askknj_num = 0;

	clear_assyuku();

	goto start;
}

void flush_douon() {
	if(drectmp) {
		make_d_list(drectmp);

		drectmp = NULL;
	}

	if(douon_ptr) {
		decide_knjask();

		makeseg();

		clear_list();

		clear_hindo();

		clear_ofsrec();

		askknj_num = 0;
	}
}

void makelist(int* yomi, int* kanji, int hinsi, int* atr) {
	u_char*	  ycode;
	u_char*	  kcode;
	int	  klen;
	HinsiRec* hrec;
	KanjiRec *krec, *kprev = NULL;

	ycode = makeyomi(yomi);

	kcode = makekanji(yomi, kanji, atr, &klen);

	if(drectmp && strcmp((char*)ycode, (char*)drectmp->yptr)) {
		make_d_list(drectmp);

		drectmp = NULL;
	}

	if(!drectmp) {

		drectmp = make_drec(ycode);

		hrec		  = make_hrec(hinsi);
		drectmp->hrec	  = hrec;
		drectmp->hrec_num = 1;

		krec	   = make_krec(kcode, klen);
		hrec->krec = krec;

		return;
	}

	for(hrec = drectmp->hrec; hrec->hnext; hrec = hrec->hnext)
		if(hrec->hinsi == hinsi)
			break;

	if(hrec->hinsi == hinsi) {
		for(krec = hrec->krec; krec; krec = krec->knext) {
			if((krec->klen == klen) &&
			   !memcmp(krec->kptr, kcode, klen)) {
				fprintf(stderr, DUPECOMBINEDWORD);
				fprintf(stderr, DUPECOMBINEDWORDREAD);
				output_int(stderr, yomi);
				fprintf(stderr, "\n");
				fprintf(stderr, DUPECOMBINEDWORDKANJI);
				output_int(stderr, kanji);
				fprintf(stderr, "\n");

				Free(kcode);

				return;
			}
			kprev = krec;
		}

		if(kprev)
			kprev->knext = make_krec(kcode, klen);

		return;
	}

	hrec->hnext = make_hrec(hinsi);
	hrec	    = hrec->hnext;
	drectmp->hrec_num += 1;

	krec	   = make_krec(kcode, klen);
	hrec->krec = krec;
}
