/*-
 * Copyright (c) 2000
 * Tatsuya Kudoh(CDR/TK),ROYALPANDA.    All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include<stdlib.h>

#include"regg_p.h"

void draw_str( unsigned char far *str );


static unsigned char Yomi_buffer[REGG_YOMI_LEN+2];

static unsigned char far *Dictionary;


/*
** char* -> 16bit code
*/

static unsigned kcode( unsigned char far *str)
{
	if( str[0] & 0x80 )
		return (unsigned)str[0] << 8 | str[1];
	else
		return *str;
}


#if 1

static kana_assoc_t *get_kana_assoc( unsigned code )
{
	int type;
	int bottom,top,center;
	unsigned cur;

	/* check code range */

	if( code >= ReggKanaAssoc[0].code[0]
		    && code <= ReggKanaAssoc[REGG_NUM_KANA_ASSOC-1].code[0] )
		type = 0;	/* hiragana */
	else if( code >= ReggKanaAssoc[0].code[1]
		    && code <= ReggKanaAssoc[REGG_NUM_KANA_ASSOC-1].code[1] )
		type = 1;	/* katakana */
	else
		return NULL;

	/* binart search */

	bottom = 0;
	top = REGG_NUM_KANA_ASSOC - 1;
	do{
		center = (top+bottom) / 2;
		cur = ReggKanaAssoc[center].code[type];
		if( code < cur )
			top = center - 1;
		else if( code > cur )
			bottom = center + 1;
		else
			return ReggKanaAssoc + center;
	}while( top >= bottom );

	return NULL;
}

#else

static kana_assoc_t *get_kana_assoc( unsigned code )
{
	int i;

	for( i = 0 ; i < REGG_NUM_KANA_ASSOC ; i++ ){
		if( ReggKanaAssoc[i].code[0] == code )
			return ReggKanaAssoc + i;
		if( ReggKanaAssoc[i].code[1] == code )
			return ReggKanaAssoc + i;
	}
	return NULL;
}

#endif


static int parse_yomi( unsigned char far *yomi,int len,unsigned char far *okuri)
{
	kana_assoc_t *ap;
	int size;
	unsigned code;
	int hash;

	size = 0;
	hash = 0;
	while( len > 0 ){
		code = kcode(yomi);
		if( code & 0x8000 ){
			if( size+2 > REGG_YOMI_LEN )
				return -1;
			ap = get_kana_assoc(code);
			if( ap )
				code = ap->code[0];
			else if( code >= 'A' && code <= 'Z' )
				code -= 'A' - 'a';
			Yomi_buffer[size++] = code >> 8;
			Yomi_buffer[size++] = code & 0xff;
			yomi += 2;
			len -= 2;
			hash += (code >> 8) + (code & 0xff);
		}else{
			if( size+1 > REGG_YOMI_LEN )
				return -1;
			Yomi_buffer[size++] = code;
			yomi++;
			len--;
			hash += code;
		}
	}
	if( okuri ){
		code = kcode(okuri);
		if( !(code & 0x8000) )
			return -1;
		ap = get_kana_assoc(code);
		if( ap == NULL )
			return -1;
		if( ap->cons == 0 )
			return -1;
		Yomi_buffer[size++] = ap->cons;
		hash += ap->cons;
	}
	Yomi_buffer[size] = 0;

	return hash % REGG_HASHSIZE;
}


static int str_cmp( unsigned char far *p, unsigned char far *q )
{
	while( *p && *p == *q ){
		p++;
		q++;
	}
	return *p - *q;
}


static unsigned char far *search_candidate( int hash )
{
	long addr;
	unsigned char far *p;
	unsigned char far *np;
	int offset;

	addr = ((long far*)Dictionary)[hash];
	
	if( addr == 0 )
		return NULL;

	np = Dictionary + addr;
	do{
		offset = *(unsigned far *)np;
		p = np + 2;
		np += offset;
		if( str_cmp(Yomi_buffer,p) == 0 ){
			while( *p )
				p++;
			p += 2;
			return p;
		}
	}while( offset );

	return NULL;
}


int REGG_set_dictionary( unsigned char far *dic )
{
	Dictionary = dic;
}


unsigned char far *REGG_start( unsigned char far *yomi,int len,unsigned char far *okuri )
{
	int hash;

	if( Dictionary == NULL )
		return NULL;

	hash = parse_yomi(yomi,len,okuri);
	if( hash < 0 )
		return NULL;
	return search_candidate(hash);
}


unsigned char far *REGG_next( unsigned char far *context )
{
	if( context == NULL )
		return NULL;

	while( *context )
		context++;
	context++;
	if( *context == 0 )
		context--;
	return context;
}


unsigned char far *REGG_prev( unsigned char far *context )
{
	if( context == NULL )
		return NULL;

	context -= 2;
	while( *context )
		context--;
	context++;
	if( *context == 0 )
		context++;
	return context;
}
