/*
 * 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: hindo.c,v $
 * $SonyRevision: 1.2 $
 * $SonyDate: 1994/08/17 01:50:26 $
 */

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

#include "sj4mkdic.h"

int check_hindo(u_char* ptr, int len) {
	int low, high, mid;
	int i;

	if(hindo_num <= 0) return 0;

	low  = 0;
	high = hindo_num - 1;
	while(low <= high) {
		mid = (low + high) / 2;
		i   = string_cmp(hindo[mid]->kptr, hindo[mid]->klen, ptr, len);
		if(i > 0)
			high = mid - 1;
		else if(i < 0)
			low = ++mid;
		else
			return (hindo[mid]->exist + hindo[mid]->hindo);
	}

	return 0;
}

static AssyukuRec*
makeassyuku(int num) {
	AssyukuRec* arec;

	arec = (AssyukuRec*)Malloc(sizeof(AssyukuRec));
	if(!arec) {
		fprintf(stderr, MEMNOTENOUGH);
		exit(1);
	}

	arec->len   = num;
	arec->nrec  = NULL;
	arec->anext = NULL;

	return arec;
}

static int
assyuku_len(HindoRec* hrec) {
	int org;
	int old;
	int new;
	OffsetRec* p;

	if(((*(hrec->kptr) & KANJIMODEMASK) == OFFSETASSYUKU) &&
	   (hrec->klen == 2)) {
		p   = real_ofsrec(hrec->kptr);
		org = check_hindo(p->kptr, p->klen);
		old = p->klen * org + 2 * (hrec->exist + hrec->hindo);
		new = p->klen + (hrec->exist + hrec->hindo + org);
	} else {
		old = hrec->klen * (hrec->exist + hrec->hindo);
		new = hrec->klen + (hrec->exist + hrec->hindo);
	}
	return (old - new);
}

static void
set_assyuku(HindoRec* hrec) {
	AssyukuRec* arec;
	AssyukuRec* aprev;
	int	    len;

	len = assyuku_len(hrec);

	if(len <= 0) return;

	arec  = assyuku;
	aprev = NULL;
	while(arec) {
		if(arec->len == len) {
			hrec->arec  = arec;
			hrec->anext = arec->nrec;
			arec->nrec  = hrec;
			return;
		} else if(arec->len < len)
			break;

		aprev = arec;
		arec  = arec->anext;
	}

	arec = makeassyuku(len);

	hrec->anext = NULL;
	hrec->arec  = arec;
	arec->nrec  = hrec;

	if(aprev) {
		arec->anext  = aprev->anext;
		aprev->anext = arec;
	} else {
		arec->anext = assyuku;
		assyuku	    = arec;
	}
}

static void
reset_assyuku(HindoRec* hrec) {
	AssyukuRec* arec;
	AssyukuRec* aptr;
	AssyukuRec* aprev;
	HindoRec*   hptr;
	HindoRec*   hprev;

	arec = hrec->arec;

	if(!arec) return;
	hrec->arec = NULL;

	hptr  = arec->nrec;
	hprev = NULL;
	while(hptr) {
		if(hptr == hrec) break;
		hprev = hptr;
		hptr  = hptr->anext;
	}

	if(!hptr) return;

	if(hprev) {
		hprev->anext = hrec->anext;
		hrec->anext  = NULL;
		return;
	}

	if(hrec->anext) {
		arec->nrec  = hrec->anext;
		hrec->anext = NULL;
		return;
	}

	if(!assyuku) return;
	aptr  = assyuku;
	aprev = NULL;
	while(aptr) {
		if(aptr == arec) break;
		aprev = aptr;
		aptr  = aptr->anext;
	}

	if(!aptr) return;

	if(aprev)
		aprev->anext = arec->anext;
	else
		assyuku = arec->anext;

	Free(arec);

	hrec->anext = NULL;
}

static HindoRec*
makehindo(u_char* ptr, int len, int alen) {
	HindoRec* hrec;
	u_char*	  p;

	hrec = (HindoRec*)Malloc(sizeof(HindoRec));
	if(!hrec) {
		fprintf(stderr, MEMNOTENOUGH);
		exit(1);
	}
	p = (u_char*)Malloc(len);
	if(!p) {
		fprintf(stderr, MEMNOTENOUGH);
		exit(1);
	}
	hrec->klen = len;
	hrec->kptr = p;
	if(len == alen && len > 2) {
		hrec->hindo  = 0;
		hrec->exist  = 1;
		hrec->offset = ++kanji_num;
	} else {
		hrec->hindo  = 1;
		hrec->exist  = 0;
		hrec->offset = 0;
	}
	while(len-- > 0) *p++ = *ptr++;
	hrec->anext = NULL;
	hrec->arec  = NULL;

	return hrec;
}

static void
set_hindo(u_char* ptr, int len, int alen) {
	HindoRec* hrec;
	int	  low, high, mid;
	int	  i;

	if(hindo_num <= 0) {
		hrec	  = makehindo(ptr, len, alen);
		hindo[0]  = hrec;
		hindo_num = 1;

		set_assyuku(hrec);

		return;
	}

	low  = 0;
	high = hindo_num - 1;
	while(low <= high) {
		mid = (low + high) / 2;
		i   = string_cmp(hindo[mid]->kptr, hindo[mid]->klen, ptr, len);
		if(i > 0)
			high = mid - 1;
		else if(i < 0)
			low = ++mid;
		else
			break;
	}

	if(i) {
		if(hindo_num >= MAXHINDONUMBER) {
			fprintf(stderr, TABLEOVERFLOW);
			exit(1);
		}

		hrec = makehindo(ptr, len, alen);

		for(i = hindo_num; i > mid; i--) hindo[i] = hindo[i - 1];
		hindo[mid] = hrec;
		hindo_num++;
	} else {
		hrec = hindo[mid];

		reset_assyuku(hrec);

		if(hrec->exist > 0)
			hrec->exist += 1;

		else if(len == alen && len > 2) {
			hrec->exist  = 1;
			hrec->offset = ++kanji_num;
		} else
			hrec->hindo += 1;
	}

	set_assyuku(hrec);
}

static void
reset_hindo(u_char* p, int l) {
	HindoRec* hrec;
	int	  low, high, mid;
	int	  i;

	if(hindo_num <= 0) return;

	low  = 0;
	high = hindo_num - 1;
	while(low <= high) {
		mid = (low + high) / 2;
		i   = string_cmp(hindo[mid]->kptr, hindo[mid]->klen, p, l);
		if(i > 0)
			high = mid - 1;
		else if(i < 0)
			low = ++mid;
		else {
			hrec = hindo[mid];

			reset_assyuku(hrec);

			if(hrec->exist > 0)
				hrec->exist -= 1;
			else if(hrec->hindo > 0)
				hrec->hindo -= 1;

			if(hrec->exist + hrec->hindo == 0) {
				while(++mid < hindo_num) {
					hindo[mid - 1] = hindo[mid];
				}
				hindo_num--;
				Free(hrec->kptr);
				Free(hrec);
			} else {
				set_assyuku(hrec);
			}

			return;
			break;
		}
	}
}

void knjhnd_set(u_char* p, int l) {
	u_char* kp1;
	u_char* kp2;
	int	i;
	int	kl1;
	u_char	tmp[MAXKANJILENGTH * 2 + MAXATRNUMBER * 2 + 1];

	for(kp1 = p, kl1 = l; kl1 > 0;) {
		for(i = 0, kp2 = kp1; i < kl1;) {
			if(codesize(*kp2) == 1) {
				tmp[i++] = *kp2++;
			} else if(codesize(*kp2) == 2) {
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
			} else {
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
			}
			set_hindo(tmp, i, l);
		}

		kl1 -= codesize(*kp1);
		kp1 += codesize(*kp1);
	}
}

void knjhnd_reset(u_char* p, int l) {
	u_char* kp1;
	u_char* kp2;
	int	i;
	int	kl1;
	u_char	tmp[MAXKANJILENGTH * 2 + MAXATRNUMBER * 2 + 1];

	for(kp1 = p, kl1 = l; kl1 > 0;) {
		for(i = 0, kp2 = kp1; i < kl1;) {
			if(codesize(*kp2) == 1) {
				tmp[i++] = *kp2++;
			} else if(codesize(*kp2) == 2) {
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
			} else {
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
				tmp[i++] = *kp2++;
			}
			reset_hindo(tmp, i);
		}

		kl1 -= codesize(*kp1);
		kp1 += codesize(*kp1);
	}
}

static int
check_assyuku(HindoRec* p, HindoRec* q) {
	u_char ptmp[128], qtmp[128];
	int    plen;
	int    qlen;

	plen = make_knjstr(p->kptr, p->klen, ptmp);

	qlen = make_knjstr(q->kptr, q->klen, qtmp);

	if(bubun_str(ptmp, plen, qtmp, qlen)) {
		return -1;
	}

	if(overlap_str(ptmp, plen, qtmp, qlen))
		return -1;

	return 0;
}

int decide_knjask() {
	int	    i, j;
	AssyukuRec* p;
	HindoRec*   q;
	u_char*	    r;
	int	    len = 0;

	askknj_num = 0;

	for(p = assyuku; p; p = p->anext) {
		if(p->len <= 0) break;

		for(q = p->nrec; q; q = q->anext) {

			i = q->klen;
			r = q->kptr;
			if(i > 2) {
				while(i > 0) {
					if((*r & KANJIMODEMASK) == OFFSETASSYUKU) {
						break;
					}
					j = codesize(*r);
					i -= j;
					r += j;
				}
				if(i) continue;
			}
			if(askknj_num) {
				for(i = 0; i < askknj_num; i++) {
					if(check_assyuku(askknj[i], q))
						break;
				}
				if(i >= askknj_num) {
					askknj[askknj_num++] = q;
					len += p->len;
					if(askknj_num >= MAXKNJASKNUMBER)
						goto end;
				}
			} else {
				askknj[askknj_num++] = q;
				len += p->len;
			}
		}
	}
end:
{
	int	i, j, len;
	u_char* p;

	for(i = 0; i < askknj_num; i++) {
		len = askknj[i]->klen;
		p   = askknj[i]->kptr;
		if(len <= 2) continue;
		while(len > 0) {
			if((*p & KANJIMODEMASK) == OFFSETASSYUKU) {
				printf(CONTAINOFFSETCOMP, i);
			}
			j = codesize(*p);
			len -= j;
			p += j;
		}
	}
}
	return len;
}

void clear_hindo() {
	int i;

	for(i = 0; i < hindo_num; i++) {
		Free(hindo[i]->kptr);
		Free(hindo[i]);
	}
	hindo_num = 0;
	kanji_num = 0;
}

void clear_assyuku() {
	AssyukuRec* p;

	while(assyuku) {
		p	= assyuku;
		assyuku = assyuku->anext;
		Free(p);
	}
}
