/**********************************************************************
 
	Copyright (C) 2004 Tomohito Nakajima <nakajima@zeta.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include "addrdb.h"
#include "associate.h"
#include "memory_debug.h"
#include "lc_encode.h"
#include "csvparser.h"
#include "change_endian.h"

extern void
l_strcpy(L_CHAR * d,L_CHAR * s);

typedef struct {
	L_CHAR *from;
	int from_len;
	L_CHAR *to;
	int to_len;
}ADDRDB_REPLACE_TABLE;

/*
methods and data for normalize Japanese format address 
*/
typedef struct {
	ADDRDB_LOCALE common;
	ADDRDB_REPLACE_TABLE *address_number_replace_table;
	int address_number_replace_table_size;
	L_CHAR hifen;
	L_CHAR kanji_jyuu;
	L_CHAR zero;
	L_CHAR ichi;
	L_CHAR kanji_number[10];
}ADDRDB_KOKUDO_LOCALE;

static BOOL is_kanji_number_sjis(const char*code){
	int i;
	/*
	char *kanji_number[] = {
			"一",
			"ニ",
			"二",
			"三",
			"四",
			"五",
			"六",
			"七",
			"八",
			"九",
			"十"
		};
	*/
	
	unsigned char kanji_number[][3] = {
		{0x88,0xea,0x00},
		{0x83,0x6a,0x00},
		{0x93,0xf1,0x00},
		{0x8e,0x4f,0x00},
		{0x8e,0x6c,0x00},
		{0x8c,0xdc,0x00},
		{0x98,0x5a,0x00},
		{0x8e,0xb5,0x00},
		{0x94,0xaa,0x00},
		{0x8b,0xe3,0x00},
		{0x8f,0x5c,0x00},
	};
	
	for(i=0; i<11; ++i){
		if(strncmp(code, kanji_number[i], 2)==0){
			return TRUE;
		}
	}
	return FALSE;
}


XL_SEXP *addrdb_kokudo_make_record_data(double longitude, double latitule, int public_coordinate_system, double x, double y){
XL_PAIR *ret;
XL_PAIR *s;
static L_CHAR *degree_l=0;
static L_CHAR m[2];

	if(degree_l==0){
		degree_l = (L_CHAR *)d_alloc(sizeof(L_CHAR)*10+1);
		l_strcpy(degree_l,l_string(std_cm, "degree"));
		m[0] = *l_string(std_cm,"m");
		m[1] = '\0';
	}
	
	ret = (XL_PAIR*)get_sexp(0,XLT_PAIR);
	s = ret;
	s->car = get_integer(public_coordinate_system,0);
	
	s->cdr = get_sexp(0,XLT_PAIR);
	s=(XL_PAIR*)s->cdr;
	s->car = get_floating(x,m);

	s->cdr = get_sexp(0,XLT_PAIR);
	s=(XL_PAIR*)s->cdr;
	s->car = get_floating(y,m);
	
	s->cdr = get_sexp(0,XLT_PAIR);
	s=(XL_PAIR*)s->cdr;
	s->car = get_floating(latitule, degree_l);
	
	s->cdr = get_sexp(0,XLT_PAIR);
	s=(XL_PAIR*)s->cdr;
	s->car = get_floating(longitude, degree_l);

	return (XL_SEXP*)ret;
}


static BOOL is_kanji_number(ADDRDB_KOKUDO_LOCALE *loc, L_CHAR ch){
int i;
	for(i=0; i<10; ++i){
		if(loc->kanji_number[i] == ch){
			return TRUE;
		}
	}
	return FALSE;
}

static int addrdb_init_jp(ADDRDB_LOCALE* locale, ADDRDB *db){
	L_CHAR from[5];
	L_CHAR to[5];
	int i;
	int table_size;
	
	/* setup dictionary */
	/* 
		Katakana -> Hiragana
		ア-ン -> あ-ん 
	*/
	const unsigned short hiragana_sjis = 0x829f; /* 'ぁ' */
	const unsigned short katakana_sjis = 0x8340; /* 'ァ' */
	const unsigned short hiragana_uni = 0x3041; /* 'ぁ' */
	const unsigned short katakana_uni = 0x30a1; /* 'ァ' */
	
	memset(from, 0, sizeof(from));
	memset(to, 0, sizeof(to));

	for(i=0; i<83; ++i){
		from[0] = katakana_uni+i;
		to[0] = hiragana_uni+i;
		addrdb_insert_replace_dict(db, from, to);
	}
	
	
	/* 
		Halfwidth-Katakana(JIS X 0201kana) -> hiragana
		ｱ-ﾝ -> あ-ん 
	*/
	{
		L_CHAR tbl[][2] = {
			{0xff66, 0x3092},
			{0xff67, 0x3041},
			{0xff68, 0x3043},
			{0xff69, 0x3045},
			{0xff6a, 0x3047},
			{0xff6b, 0x3049},
			{0xff6c, 0x3083},
			{0xff6d, 0x3085},
			{0xff6e, 0x3087},
			{0xff6f, 0x3063},
			{0xff70, 0x30fc},
			{0xff71, 0x3042},
			{0xff72, 0x3044},
			{0xff73, 0x3046},
			{0xff74, 0x3048},
			{0xff75, 0x304a},
			{0xff76, 0x304b},
			{0xff77, 0x304d},
			{0xff78, 0x304f},
			{0xff79, 0x3051},
			{0xff7a, 0x3053},
			{0xff7b, 0x3055},
			{0xff7c, 0x3057},
			{0xff7d, 0x3059},
			{0xff7e, 0x305b},
			{0xff7f, 0x305d},
			{0xff80, 0x305f},
			{0xff81, 0x3061},
			{0xff82, 0x3064},
			{0xff83, 0x3066},
			{0xff84, 0x3068},
			{0xff85, 0x306a},
			{0xff86, 0x306b},
			{0xff87, 0x306c},
			{0xff88, 0x306d},
			{0xff89, 0x306e},
			{0xff8a, 0x306f},
			{0xff8b, 0x3072},
			{0xff8c, 0x3075},
			{0xff8d, 0x3078},
			{0xff8e, 0x307b},
			{0xff8f, 0x307e},
			{0xff90, 0x307f},
			{0xff91, 0x3080},
			{0xff92, 0x3081},
			{0xff93, 0x3082},
			{0xff94, 0x3084},
			{0xff95, 0x3086},
			{0xff96, 0x3088},
			{0xff97, 0x3089},
			{0xff98, 0x308a},
			{0xff99, 0x308b},
			{0xff9a, 0x308c},
			{0xff9b, 0x308d},
			{0xff9c, 0x308f},
			{0xff9d, 0x3093},
			{0xff9e, 0x309b},
			{0xff9f, 0x309c},
			/*
			{ 0xFF66 , L'を' },
			{ 0xFF67 , L'ぁ' },
			{ 0xFF68 , L'ぃ' },
			{ 0xFF69 , L'ぅ' },
			{ 0xFF6A , L'ぇ' },
			{ 0xFF6B , L'ぉ' },
			{ 0xFF6C , L'ゃ' },
			{ 0xFF6D , L'ゅ' },
			{ 0xFF6E , L'ょ' },
			{ 0xFF6F , L'っ' },
			{ 0xFF70 , L'ー' },
			{ 0xFF71 , L'あ' },
			{ 0xFF72 , L'い' },
			{ 0xFF73 , L'う' },
			{ 0xFF74 , L'え' },
			{ 0xFF75 , L'お' },
			{ 0xFF76 , L'か' },
			{ 0xFF77 , L'き' },
			{ 0xFF78 , L'く' },
			{ 0xFF79 , L'け' },
			{ 0xFF7A , L'こ' },
			{ 0xFF7B , L'さ' },
			{ 0xFF7C , L'し' },
			{ 0xFF7D , L'す' },
			{ 0xFF7E , L'せ' },
			{ 0xFF7F , L'そ' },
			{ 0xFF80 , L'た' },
			{ 0xFF81 , L'ち' },
			{ 0xFF82 , L'つ' },
			{ 0xFF83 , L'て' },
			{ 0xFF84 , L'と' },
			{ 0xFF85 , L'な' },
			{ 0xFF86 , L'に' },
			{ 0xFF87 , L'ぬ' },
			{ 0xFF88 , L'ね' },
			{ 0xFF89 , L'の' },
			{ 0xFF8A , L'は' },
			{ 0xFF8B , L'ひ' },
			{ 0xFF8C , L'ふ' },
			{ 0xFF8D , L'へ' },
			{ 0xFF8E , L'ほ' },
			{ 0xFF8F , L'ま' },
			{ 0xFF90 , L'み' },
			{ 0xFF91 , L'む' },
			{ 0xFF92 , L'め' },
			{ 0xFF93 , L'も' },
			{ 0xFF94 , L'や' },
			{ 0xFF95 , L'ゆ' },
			{ 0xFF96 , L'よ' },
			{ 0xFF97 , L'ら' },
			{ 0xFF98 , L'り' },
			{ 0xFF99 , L'る' },
			{ 0xFF9A , L'れ' },
			{ 0xFF9B , L'ろ' },
			{ 0xFF9C , L'わ' },
			{ 0xFF9D , L'ん' },
			{ 0xFF9E , L'゛' },
			{ 0xFF9F , L'゜' },
			*/
		};
		
		table_size = (sizeof(tbl)/2)/sizeof(L_CHAR);
		for(i=0; i<table_size; ++i){
			from[0] = tbl[i][0];
			to[0] = tbl[i][1];
			addrdb_insert_replace_dict(db, from, to);
			
			/* for generate code */
			/* printf("{0x%x, 0x%x},\n", tbl[i][0], tbl[i][1]); */
		}
	}
	
	{
		L_CHAR tbl[][2][2] = {
			{ {0xff76, 0xff9e}, {0x304c, 0} },
			{ {0xff77, 0xff9e}, {0x304e, 0} },
			{ {0xff78, 0xff9e}, {0x3050, 0} },
			{ {0xff79, 0xff9e}, {0x3052, 0} },
			{ {0xff7a, 0xff9e}, {0x3054, 0} },
			{ {0xff7b, 0xff9e}, {0x3056, 0} },
			{ {0xff7c, 0xff9e}, {0x3058, 0} },
			{ {0xff7d, 0xff9e}, {0x305a, 0} },
			{ {0xff7e, 0xff9e}, {0x305c, 0} },
			{ {0xff7f, 0xff9e}, {0x305e, 0} },
			{ {0xff80, 0xff9e}, {0x3060, 0} },
			{ {0xff81, 0xff9e}, {0x3062, 0} },
			{ {0xff82, 0xff9e}, {0x3065, 0} },
			{ {0xff83, 0xff9e}, {0x3067, 0} },
			{ {0xff84, 0xff9e}, {0x3069, 0} },
			{ {0xff8a, 0xff9e}, {0x3070, 0} },
			{ {0xff8b, 0xff9e}, {0x3073, 0} },
			{ {0xff8c, 0xff9e}, {0x3076, 0} },
			{ {0xff8d, 0xff9e}, {0x3079, 0} },
			{ {0xff8e, 0xff9e}, {0x307c, 0} },
			{ {0xff8a, 0xff9f}, {0x3071, 0} },
			{ {0xff8b, 0xff9f}, {0x3074, 0} },
			{ {0xff8c, 0xff9f}, {0x3077, 0} },
			{ {0xff8d, 0xff9f}, {0x307a, 0} },
			{ {0xff8e, 0xff9f}, {0x307d, 0} },
			{ {0xff66, 0x3092}, {0xff67, 0} },
			{ {0xff68, 0x3043}, {0xff69, 0} },
			{ {0xff6a, 0x3047}, {0xff6b, 0} },
			{ {0xff6c, 0x3083}, {0xff6d, 0} },
			{ {0xff6e, 0x3087}, {0xff6f, 0} },
			{ {0xff70, 0x30fc}, {0xff71, 0} },
			{ {0xff72, 0x3044}, {0xff73, 0} },
			{ {0xff74, 0x3048}, {0xff75, 0} },
			{ {0xff76, 0x304b}, {0xff77, 0} },
			{ {0xff78, 0x304f}, {0xff79, 0} },
			{ {0xff7a, 0x3053}, {0xff7b, 0} },
			{ {0xff7c, 0x3057}, {0xff7d, 0} },
			{ {0xff7e, 0x305b}, {0xff7f, 0} },
			{ {0xff80, 0x305f}, {0xff81, 0} },
			{ {0xff82, 0x3064}, {0xff83, 0} },
			{ {0xff84, 0x3068}, {0xff85, 0} },
			{ {0xff86, 0x306b}, {0xff87, 0} },
			{ {0xff88, 0x306d}, {0xff89, 0} },
			{ {0xff8a, 0x306f}, {0xff8b, 0} },
			{ {0xff8c, 0x3075}, {0xff8d, 0} },
			{ {0xff8e, 0x307b}, {0xff8f, 0} },
			{ {0xff90, 0x307f}, {0xff91, 0} },
			{ {0xff92, 0x3081}, {0xff93, 0} },
			{ {0xff94, 0x3084}, {0xff95, 0} },
			{ {0xff96, 0x3088}, {0xff97, 0} },
			/*
			{ {0xFF76,0xFF9E} , {L'が',0} },
			{ {0xFF77,0xFF9E} , {L'ぎ',0} },
			{ {0xFF78,0xFF9E} , {L'ぐ',0} },
			{ {0xFF79,0xFF9E} , {L'げ',0} },
			{ {0xFF7A,0xFF9E} , {L'ご',0} },
			{ {0xFF7B,0xFF9E} , {L'ざ',0} },
			{ {0xFF7C,0xFF9E} , {L'じ',0} },
			{ {0xFF7D,0xFF9E} , {L'ず',0} },
			{ {0xFF7E,0xFF9E} , {L'ぜ',0} },
			{ {0xFF7F,0xFF9E} , {L'ぞ',0} },
			{ {0xFF80,0xFF9E} , {L'だ',0} },
			{ {0xFF81,0xFF9E} , {L'ぢ',0} },
			{ {0xFF82,0xFF9E} , {L'づ',0} },
			{ {0xFF83,0xFF9E} , {L'で',0} },
			{ {0xFF84,0xFF9E} , {L'ど',0} },
			{ {0xFF8A,0xFF9E} , {L'ば',0} },
			{ {0xFF8B,0xFF9E} , {L'び',0} },
			{ {0xFF8C,0xFF9E} , {L'ぶ',0} },
			{ {0xFF8D,0xFF9E} , {L'べ',0} },
			{ {0xFF8E,0xFF9E} , {L'ぼ',0} },
			{ {0xFF8A,0xFF9F} , {L'ぱ',0} },
			{ {0xFF8B,0xFF9F} , {L'ぴ',0} },
			{ {0xFF8C,0xFF9F} , {L'ぷ',0} },
			{ {0xFF8D,0xFF9F} , {L'ぺ',0} },
			{ {0xFF8E,0xFF9F} , {L'ぽ',0} },
			*/
		};
		
		table_size = (sizeof(tbl)/2)/sizeof(L_CHAR);
		for(i=0; i<table_size; ++i){
			from[0] = tbl[i][0][0];
			from[1] = tbl[i][0][1];
			to[0] = tbl[i][1][0];
			addrdb_insert_replace_dict(db, from, to);

			/* for generate code */
			/* printf("{ {0x%x, 0x%x}, {0x%x, 0} },\n", tbl[i][0][0], tbl[i][0][1], tbl[i][1][0]); */
		}
	}
	
	return 0;
}

static int addrdb_normalize_addr_name_format_jp(ADDRDB_LOCALE *loc, ADDRDB *db, L_CHAR *normalized, int normalized_buff_size, const L_CHAR *original){
	return addrdb_substitute_str_for_normalize(db, normalized, normalized_buff_size, original);
}

static int addrdb_normalize_addr_number_format_jp(ADDRDB_LOCALE *loc, ADDRDB *db, L_CHAR *normalized, int normalized_buff_size, const L_CHAR *original){

int len;
ADDRDB_KOKUDO_LOCALE *locale;
ADDRDB_REPLACE_TABLE *tbl;
int table_size;
int dest;
int i;
int j;
L_CHAR *original_c;

	original_c = addrdb_convert_lcharcode(loc, original);
	if(original_c){
		original = original_c;
	}

	locale = (ADDRDB_KOKUDO_LOCALE *)loc;
	tbl = locale->address_number_replace_table;
	table_size = locale->address_number_replace_table_size;
	
	len = l_strlen((L_CHAR*)original);

	dest = 0;

	for(i=0; i<len; ++i){
		int converted = 0;
		for(j=0; j<table_size; ++j){
			if(memcmp(tbl[j].from, &original[i], tbl[j].from_len * sizeof(L_CHAR)) == 0){
				if( (int)((dest + tbl[j].to_len) * sizeof(L_CHAR)) >= normalized_buff_size){
					/* too small buffer */
					ADDRDB_ASSERT(0);
					return -1;
				}
				memcpy(&normalized[dest], tbl[j].to, tbl[j].to_len*sizeof(L_CHAR));
				dest += tbl[j].to_len;
				converted = 1;
				i+=tbl[j].from_len-1;
				break;
			}
		}
		
		if(!converted){

			if(original[i] == locale->kanji_jyuu){
				if(i>0){
					if(is_kanji_number(locale, original[i-1]) ){
						if(is_kanji_number(locale, original[i+1])){
							continue;
						}
						else{
							if(dest>=normalized_buff_size){
								ADDRDB_ASSERT(0);
								goto error_exit;
							}
							normalized[dest++] = locale->zero;
							continue;
						}
					}
					else{
						if(dest>=normalized_buff_size){
							ADDRDB_ASSERT(0);
							goto error_exit;
						}
						normalized[dest++] = locale->ichi;
						continue;
					}
				}
				else{
					if(dest>=normalized_buff_size){
						ADDRDB_ASSERT(0);
						goto error_exit;
					}
					normalized[dest++] = locale->ichi;
					continue;
				}
			}
			
			if(dest>=normalized_buff_size){
				ADDRDB_ASSERT(0);
				goto error_exit;
			}
			normalized[dest++] = original[i];
		}
	}
	
	if(dest>0 && normalized[dest-1] == locale->hifen){
		normalized[dest-1] = 0;
	}
	else{
		normalized[dest] = 0;
	}

	if(original_c){
		d_f_ree(original_c);
	}
	return 0;
error_exit:
	if(original_c){
		d_f_ree(original_c);
	}
	return -1;
}

static void print_code_table(wchar_t *data[][2], int size){
	size_t i,j;
	for(i=0; i<(size_t)size; ++i){
		printf("{");

		printf("{");
		for(j=0;j<wcslen(data[i][0]); ++j){
			printf("0x%x,", 0xffff&data[i][0][j]);
		}
		printf("0x00");
		printf("},");

		printf("{");
		for(j=0;j<wcslen(data[i][1]); ++j){
			printf("0x%x,", 0xffff&data[i][1][j]);
		}
		printf("0x00");
		printf("}");
		
		printf("},\n");
	}
}


ADDRDB_LOCALE *addrdb_kokudo_get_locale(){
	static ADDRDB_KOKUDO_LOCALE ret;
	static int init=0;
	
	if(init==0){
		/* initialize KOKUDO_LOCALE datas */
		
		int i;
		static LCZ_SET utf8_set[] = {
			{0,LCZM_4B_TYPE},
			{LCC_ERROR,0}
		};

		/* setup address number part dictionary */
		/*
		wchar_t *rep_tbl[][2] = {
			{L"丁目", L"-"},
			{L"番地", L"-"},
			{L"番", L"-"},
			{L"号", L""},
			{L" ", L""},
			{L"　", L""},
			{L"の", L"-"},
			{L"ノ", L"-"},
			{L"ー", L"-"},
			{L"‐", L"-"},
			{L"−", L"-"},
			{L"条", L"-"},
			{L"地割", L"-"},
			{L"一",L"1"},
			{L"二",L"2"},
			{L"三",L"3"},
			{L"四",L"4"},
			{L"五",L"5"},
			{L"六",L"6"},
			{L"七",L"7"},
			{L"八",L"8"},
			{L"九",L"9"},
			{L"１",L"1"},
			{L"２",L"2"},
			{L"３",L"3"},
			{L"４",L"4"},
			{L"５",L"5"},
			{L"６",L"6"},
			{L"７",L"7"},
			{L"８",L"8"},
			{L"９",L"9"},
			{L"０",L"0"},
		};
		*/
		static L_CHAR rep_tbl[][2][5] = {
			{{0x4e01,0x76ee,0x00},{0x2d,0x00}},
			{{0x756a,0x5730,0x00},{0x2d,0x00}},
			{{0x756a,0x00},{0x2d,0x00}},
			{{0x53f7,0x00},{0x00}},
			{{0x20,0x00},{0x00}},
			{{0x3000,0x00},{0x00}},
			{{0x306e,0x00},{0x2d,0x00}},
			{{0x30ce,0x00},{0x2d,0x00}},
			{{0x30fc,0x00},{0x2d,0x00}},
			{{0x2010,0x00},{0x2d,0x00}},
			{{0xff0d,0x00},{0x2d,0x00}},
			{{0x6761,0x00},{0x2d,0x00}},
			{{0x5730,0x5272,0x00},{0x2d,0x00}},
			{{0x4e00,0x00},{0x31,0x00}},
			{{0x4e8c,0x00},{0x32,0x00}},
			{{0x4e09,0x00},{0x33,0x00}},
			{{0x56db,0x00},{0x34,0x00}},
			{{0x4e94,0x00},{0x35,0x00}},
			{{0x516d,0x00},{0x36,0x00}},
			{{0x4e03,0x00},{0x37,0x00}},
			{{0x516b,0x00},{0x38,0x00}},
			{{0x4e5d,0x00},{0x39,0x00}},
			{{0xff11,0x00},{0x31,0x00}},
			{{0xff12,0x00},{0x32,0x00}},
			{{0xff13,0x00},{0x33,0x00}},
			{{0xff14,0x00},{0x34,0x00}},
			{{0xff15,0x00},{0x35,0x00}},
			{{0xff16,0x00},{0x36,0x00}},
			{{0xff17,0x00},{0x37,0x00}},
			{{0xff18,0x00},{0x38,0x00}},
			{{0xff19,0x00},{0x39,0x00}},
			{{0xff10,0x00},{0x30,0x00}},
		};

		ADDRDB_REPLACE_TABLE* tbl;
		int table_size;
		
		table_size = 32;
		/*print_code_table(rep_tbl, table_size);  */

		ret.common.init = addrdb_init_jp;
		ret.common.normalize_addr_name_format = addrdb_normalize_addr_name_format_jp;
		ret.common.normalize_addr_number_format = addrdb_normalize_addr_number_format_jp;
		
		ret.common.sexp_encoding_of_stored_file = "utf-8";
		ret.common.search_key_codeset = utf8_set;
		/*
		d_alloc(sizeof(utf8_set));
		memcpy(ret.common.search_key_codeset, utf8_set, sizeof(utf8_set));
		*/

		ret.address_number_replace_table_size = table_size;
		tbl = ret.address_number_replace_table = (ADDRDB_REPLACE_TABLE*)d_alloc(table_size*sizeof(ADDRDB_REPLACE_TABLE));
		
		for(i=0; i<table_size; ++i){
			tbl[i].from = &rep_tbl[i][0][0];
			tbl[i].from_len = l_strlen(&rep_tbl[i][0][0]);
			tbl[i].to = &rep_tbl[i][1][0];
			tbl[i].to_len = l_strlen(&rep_tbl[i][1][0]);
		}
		
		ret.hifen = 0x2d; /* - */
		ret.kanji_jyuu = 0x5341; /* 十 */
		ret.zero = 0x30; /* 0 */
		ret.ichi = 0x31; /* 1 */
		
		ret.kanji_number[0] = 0x4e00;/* L'一' */
		ret.kanji_number[1] = 0x4e8c;/* L'二' */
		ret.kanji_number[2] = 0x4e09;/* L'三' */
		ret.kanji_number[3] = 0x56db;/* L'四' */
		ret.kanji_number[4] = 0x4e94;/* L'五' */
		ret.kanji_number[5] = 0x516d;/* L'六' */
		ret.kanji_number[6] = 0x4e03;/* L'七' */
		ret.kanji_number[7] = 0x516b;/* L'八' */
		ret.kanji_number[8] = 0x4e5d;/* L'九' */
		ret.kanji_number[9] = 0x5341;/* L'十' */
		
		init = 1;
	}
	return (ADDRDB_LOCALE*)&ret;
}

#include <math.h>
static BOOL float_eq(double lhs, double rhs){
	if(fabs(lhs-rhs) < 0.001){
		return TRUE;
	}
	return FALSE;
}


static double get_longtude(XL_SEXP *s){
	return ((XL_FLOAT*)car(cdr(cdr(cdr(cdr(s))))))->data;
}

static double get_latitude(XL_SEXP *s){
	return ((XL_FLOAT*)car(cdr(cdr(cdr(s)))))->data;
}


typedef struct kokudo_marge_info
{
	struct kokudo_marge_info* parent;
	char name[128];
	int count;
	double longitude_sum;
	double latitude_sum;
	double x_sum;
	double y_sum;
	int coordinate;
	BOOL is_number;
}KOKUDO_MARGE_INFO;

typedef struct 
{
	KOKUDO_MARGE_INFO todofuken;
	KOKUDO_MARGE_INFO shikucyoson;
	KOKUDO_MARGE_INFO ohaza;
	KOKUDO_MARGE_INFO cyome;
	KOKUDO_MARGE_INFO chiban;
}KOKUDO_ADDR_BATCH_IMPORT_INFO;

void kokudo_addr_import_csv(ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO* info, const char *csvfile);
XL_SEXP *kokudo_addr_make_record_from_marge_data(const KOKUDO_MARGE_INFO *info);
int kokudo_addr_insert_name_alias(ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO *info);


int kokudo_addr_flush_marge(ADDRDB *db, KOKUDO_MARGE_INFO *info){
char name_buff[1024];
char number_buff[512];
KOKUDO_MARGE_INFO *inv[5];
XL_SEXP *data;
int ret;
KOKUDO_MARGE_INFO *inf;
int i=0;

	if(info->count == 0)
		return 0;
	
	data = kokudo_addr_make_record_from_marge_data(info);
	inf = info;
	while(inf){
		ADDRDB_ASSERT(i<5);
		inv[i++] = inf;
		inf = inf->parent;
	}
	--i;
	
	name_buff[0] = 0;
	for(;i>=0;--i){
		if(inv[i]->is_number){
			break;
		}
		if(strlen(name_buff)+strlen(inv[i]->name)+1>sizeof(name_buff)){
			er_panic("error too long address name.");
		}
		strcpy(name_buff+strlen(name_buff), inv[i]->name); 
	}

	number_buff[0] = 0;
	for(;i>=0;--i){
		if(strlen(number_buff)+strlen(inv[i]->name)+1>sizeof(number_buff)){
			er_panic("error too long address name.");
		}
		strcpy(number_buff+strlen(number_buff), inv[i]->name); 
	}
	
	info->name[0] = '\0';
	info->count = 0;
	info->longitude_sum = 0;
	info->latitude_sum = 0;
	info->x_sum = 0;
	info->y_sum = 0;
	info->coordinate = 0;

	ret = addrdb_insert2(db, 
		l_string(&sjis_cm, name_buff), 
		l_string(&sjis_cm, number_buff), 
		data);
	
	return ret;
}


/*********
	FILENAME_ARRAY
**********/
typedef struct {
	char *filename_buff;
	int filename_buff_size;
	char **array;
	int file_count;
	char *directory;
	char *err_msg;
	BOOL (*filter)(const char* name);
}FilenameArray;

FilenameArray *filename_array_open(const char*directory, BOOL (*filter)(const char* name)){
struct dirent *de;
DIR *dir;
char *out;
int i;
	FilenameArray *fa = d_alloc(sizeof(FilenameArray));
	memset(fa, 0, sizeof(*fa));
	fa->directory = d_alloc(strlen(directory)+1);
	strcpy(fa->directory, directory);
	if(!(dir = opendir(directory))){
		return 0;
	}
	while ( de = readdir(dir) ) {
		if(filter && (!filter(de->d_name))){
			continue;
		}
		fa->file_count++;
		fa->filename_buff_size += strlen(de->d_name) + 1;
	}
	closedir(dir);
	
	fa->filename_buff = (char*)d_alloc(fa->filename_buff_size);
	fa->array = (char**)d_alloc(fa->file_count*sizeof(char*));
	
	dir = opendir(directory);
	out = fa->filename_buff;
	for(i=0; i<fa->file_count;){
		if(!(de = readdir(dir))){
			ADDRDB_ASSERT(0);
			/* stop listup files */
			/* file is removed ? */
			break;
		}
		if(filter && (!filter(de->d_name))){
			continue;
		}
		if(out+strlen(de->d_name)+1 > fa->filename_buff + fa->filename_buff_size){
			ADDRDB_ASSERT(0);
			/* stop listup files */
			/* file count is modified ? */
			break;
		}
		strcpy(out, de->d_name);
		fa->array[i] = out;
		out += strlen(de->d_name)+1;
		++i;
	}
	closedir(dir);
	
	return fa;
}

const char*filename_array_get_filename(FilenameArray *fa, int index){
	if(index>=fa->file_count){
		fa->err_msg = "out of index";
		return 0;
	}
	return fa->array[index];
}

BOOL filename_array_get_fullpath(FilenameArray *fa, int index, char *path_buff, int path_buff_size){
	if((size_t)path_buff_size <= strlen(fa->array[index]) + strlen(fa->directory) + 1){
		fa->err_msg = "filename length is over pathname buffer";
		return FALSE;
	}
	sprintf(path_buff, "%s/%s", fa->directory, fa->array[index]);
	return TRUE;
}

static int pstrcmp(const void *lhs, const void *rhs)
{
	return strcmp(*(char *const *) lhs, *(char *const *) rhs);
}

void filename_array_sort(FilenameArray *fa){
	qsort(fa->array, fa->file_count, sizeof(char*), pstrcmp);
}

void filename_array_close(FilenameArray *fa){
	if(fa->array)
		d_f_ree(fa->array);
	if(fa->filename_buff)
		d_f_ree(fa->filename_buff);
	if(fa->directory)
		d_f_ree(fa->directory);
	if(fa)
		d_f_ree(fa);
}

/*********
	END OF FILENAME_ARRAY
**********/


BOOL csv_name_filter(const char*name){
int len;
	len = strlen(name);
	if(len<4)
		return FALSE;
	if(strcmp(&name[len-4], ".csv")!=0 && strcmp(&name[len-4], ".CSV")!=0){
		return FALSE;
	}
	return TRUE;
}

void remove_tail_delim(char *buff, int buff_size, const char *source){
L_CHAR *ls;
L_CHAR c;

	strncpy(buff, source, buff_size);
	ls = l_string(std_cm, (char*)source);
	if(l_strlen(ls) > 1){
		c = ls[l_strlen(ls)-1];
		if(c == '\\' || c == '/'){
			buff[strlen(buff)-1] = '\0';
		}
	}
}

int addrdb_kokudo_import_csv_batch(ADDRDB *db, const char *input_dir_)
{

char filename[512];
KOKUDO_ADDR_BATCH_IMPORT_INFO info;
FilenameArray *fa;
int i;
char input_dir[512];
	
	remove_tail_delim(input_dir, sizeof(input_dir), input_dir_);
	
	memset(&info, 0, sizeof(info));
	info.todofuken.is_number = FALSE;
	info.shikucyoson.parent = &info.todofuken;
	info.shikucyoson.is_number = FALSE;
	info.ohaza.parent = &info.shikucyoson;
	info.ohaza.is_number = FALSE;
	info.cyome.parent = &info.ohaza;
	info.cyome.is_number = TRUE;
	info.chiban.parent = &info.cyome;
	info.chiban.is_number = TRUE;
	
	fa = filename_array_open(input_dir, csv_name_filter);
	if(fa==0){
		printf("error open directory \"%s\"\n", input_dir);
		ADDRDB_ASSERT(0);
		return -1;
	}
	filename_array_sort(fa);
	for(i=0; i<fa->file_count; ++i){
		if(!filename_array_get_fullpath(fa, i, filename, sizeof(filename))){
			printf("error get fullpath : %s", fa->err_msg);
		}
		gc_push(0,0,"kokudo_addr_import_csv");
		kokudo_addr_import_csv(db, &info, filename);
		gc_pop(0,0);
	}
	filename_array_close(fa);

	kokudo_addr_insert_name_alias(db, &info);
	kokudo_addr_flush_marge(db, &info.chiban);
	kokudo_addr_flush_marge(db, &info.cyome);
	kokudo_addr_flush_marge(db, &info.ohaza);
	kokudo_addr_flush_marge(db, &info.shikucyoson);
	kokudo_addr_flush_marge(db, &info.todofuken);
	
	return 0;
}

XL_SEXP *kokudo_addr_make_record_from_marge_data(const KOKUDO_MARGE_INFO *info){
	return addrdb_kokudo_make_record_data(
		info->longitude_sum/info->count, 
		info->latitude_sum/info->count, 
		info->coordinate, 
		info->x_sum/info->count, 
		info->y_sum/info->count);
}

BOOL kokudo_addr_can_marge(const KOKUDO_MARGE_INFO *info, const char *name, int coordinate){
	if(strlen(info->name) == 0)
		return TRUE;
	if(strcmp(info->name, name)==0 && (info->coordinate == coordinate) )
		return TRUE;
	
	return FALSE;
}

typedef struct {
	const char *todofuken;
	const char *shikucyoson;
	const char *ohaza;
	const char *cyome; 
	const char *chiban;
	int coordinate_system;
	double x;
	double y;
	double longitude;
	double latitude;
}KOKUDO_RECORD;

void kokudo_addr_marge_info(KOKUDO_MARGE_INFO *info, const char*name, const KOKUDO_RECORD *rec){
	info->longitude_sum += rec->longitude;
	info->latitude_sum += rec->latitude;
	info->x_sum += rec->x;
	info->y_sum += rec->y;
	info->count ++;
	info->coordinate = rec->coordinate_system;
	strcpy(info->name, name);
}

int kokudo_addr_insert_name_alias(ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO *info)
{
	char part_name[1024];
	char addr_name[1024];
	int ret;	
	sprintf(addr_name, "%s%s%s", info->todofuken.name, info->shikucyoson.name, info->ohaza.name);
	ret = addrdb_insert_name_alias(db, l_string(&sjis_cm, addr_name), l_string(&sjis_cm, addr_name));
	if(ret==-1){
		ADDRDB_ASSERT(0);
		return ret;
	}
	sprintf(part_name, "%s%s", info->shikucyoson.name, info->ohaza.name);
	ret = addrdb_insert_name_alias(db, l_string(&sjis_cm, part_name), l_string(&sjis_cm, addr_name));
	if(ret==-1){
		ADDRDB_ASSERT(0);
		return ret;
	}
	sprintf(part_name, "%s", info->ohaza.name);
	ret = addrdb_insert_name_alias(db, l_string(&sjis_cm, part_name), l_string(&sjis_cm, addr_name));
	if(ret==-1){
		ADDRDB_ASSERT(0);
		return ret;
	}
	return 0;
}

int kokudo_addr_marge_record(ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO *info, KOKUDO_RECORD *rec)
{
	if(!kokudo_addr_can_marge(&info->todofuken, rec->todofuken, rec->coordinate_system)){
		kokudo_addr_flush_marge(db, &info->chiban);
		kokudo_addr_flush_marge(db, &info->cyome);
		kokudo_addr_flush_marge(db, &info->ohaza);
		kokudo_addr_flush_marge(db, &info->shikucyoson);
		kokudo_addr_flush_marge(db, &info->todofuken);
		kokudo_addr_insert_name_alias(db, info);
	}
	kokudo_addr_marge_info(&info->todofuken, rec->todofuken, rec);
	
	if(!kokudo_addr_can_marge(&info->shikucyoson, rec->shikucyoson, rec->coordinate_system)){
		kokudo_addr_insert_name_alias(db, info);
		kokudo_addr_flush_marge(db, &info->chiban);
		kokudo_addr_flush_marge(db, &info->cyome);
		kokudo_addr_flush_marge(db, &info->ohaza);
		kokudo_addr_flush_marge(db, &info->shikucyoson);
	}
	kokudo_addr_marge_info(&info->shikucyoson, rec->shikucyoson, rec);
	
	if(!kokudo_addr_can_marge(&info->ohaza, rec->ohaza, rec->coordinate_system)){
		kokudo_addr_insert_name_alias(db, info);
		kokudo_addr_flush_marge(db, &info->chiban);
		kokudo_addr_flush_marge(db, &info->cyome);
		kokudo_addr_flush_marge(db, &info->ohaza);
	}
	kokudo_addr_marge_info(&info->ohaza, rec->ohaza, rec);
	
	if(!kokudo_addr_can_marge(&info->cyome, rec->cyome, rec->coordinate_system)){
		kokudo_addr_flush_marge(db, &info->chiban);
		kokudo_addr_flush_marge(db, &info->cyome);
	}
	kokudo_addr_marge_info(&info->cyome, rec->cyome, rec);
	
	if(!kokudo_addr_can_marge(&info->chiban, rec->chiban, rec->coordinate_system)){
		kokudo_addr_flush_marge(db, &info->chiban);
	}
	kokudo_addr_marge_info(&info->chiban, rec->chiban, rec);
	
	return 0;
	
}

/* 
	import kokudo csv 1 record
	treat kokudo data as Shift_JIS here
*/
static int kokudo_addr_import_from_csv_record(ADDRDB_LOCALE *loc, ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO *info, CSV_RECORD *record){
/*"都道府県名","市区町村名","大字・町丁目","街区符号・地番","座標系番号","Ｘ座標","Ｙ座標","緯度","経度","住居表示フラグ","代表フラグ"*/

CSV_CELL *cell;
char *ohaza;
char cyome[256];
ADDRDB_KOKUDO_LOCALE *locale;
static char unsigned cyome_static[] = {0x92,0x9a,0x96,0xda,0}; /* 丁目 */

KOKUDO_RECORD rec;

	locale = (ADDRDB_KOKUDO_LOCALE *)loc;

	if(record->head==0){
		return -1;
	}
	
	/* 都道府県名 */
	cell=record->head;
	rec.todofuken = cell->value;

	/* 市区町村名 */
	cell = cell->next;
	rec.shikucyoson = cell->value;
	
	/* 大字・町丁目 */
	cell = cell->next;
	ohaza = cell->value;
	
	/* 街区符号・地番 */
	cell = cell->next;
	rec.chiban = cell->value;
	
	/* 公共座標系番号 */
	cell = cell->next;
	rec.coordinate_system = atoi(cell->value);
	
	/* Ｘ座標 */
	cell = cell->next;
	rec.x = atof(cell->value);

	/* Ｙ座標 */
	cell = cell->next;
	rec.y = atof(cell->value);
	
	/* 緯度 */
	cell = cell->next;
	rec.latitude = atof(cell->value);
	
	/* 経度 */
	cell = cell->next;
	rec.longitude = atof(cell->value);
	
	/* 丁目と大字を分解 */
	if(strlen(ohaza)>5 && strcmp(ohaza + (strlen(ohaza)-4), cyome_static) == 0){
		int i;
		for(i=strlen(ohaza)-6; i>0; i-=2){
			if(!is_kanji_number_sjis(&ohaza[i])){
				break;
			}
		}
		i+=2;
		if(strlen(&ohaza[i])>=sizeof(cyome)){
			ADDRDB_ASSERT(0);
			/* too long cyome */
			return -1;
		}
		strcpy(cyome, &ohaza[i]);
		ohaza[i]='\0';
	}
	else{
		strcpy(cyome, "");
	}
	rec.ohaza = ohaza;
	rec.cyome = cyome;

	kokudo_addr_marge_record(db, info, &rec);
	
	return 0;
}

void kokudo_addr_import_csv(ADDRDB *db, KOKUDO_ADDR_BATCH_IMPORT_INFO* info, const char *csvfile){

STREAM *stream;
CSV_PARSER *parser;
CSV_RECORD *record;
	
	stream = s_open_file((char*)csvfile, O_RDONLY, S_IREAD);
	ADDRDB_ASSERT(stream!=0);
	
	parser = csv_parser_new(stream);
	
	/* skip first line as title */
	record = csv_parser_get_record(parser);
	
	while((record = csv_parser_get_record(parser))){
		gc_push(0,0,"import csv");
		kokudo_addr_import_from_csv_record(db->locale, db, info, record);
		gc_pop(0,0);
	}
	
	csv_parser_delete(parser);
	
	s_close(stream);
}


void addrdb_test()
{
	ADDRDB *db;
	XL_SEXP *s;
/*
	DeleteFile("addrdb_test/test.db"); 
*/
	gc_push(0,0,"addrdb");
	
	db = addrdb_open("addrdb_test/test.db", addrdb_kokudo_get_locale(), ADOPT_INSERT_SKIP_ON_KEY_COLLISION);
	
	addrdb_set_option(db, ADOPT_INSERT_OVERWRITE_ON_KEY_COLLISION);
	addrdb_set_option(db, ADOPT_INSERT_SKIP_ON_KEY_COLLISION);
	
	addrdb_kokudo_import_csv_batch(db,"addrdb_test/kokudo_csv"); 
	
	/*"東京都","千代田区","麹町三丁目",4,9,-34949.3,-8655.3,35.684947,139.737708,0,0*/
	s = addrdb_search(db, l_string(std_cm, "麹町三丁目4番地"));
	ADDRDB_ASSERT(s && float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(s && float_eq(get_latitude(s), 35.684947));
	
	s = addrdb_search(db, l_string(std_cm, "麹町三の４"));
	ADDRDB_ASSERT(s && float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(s && float_eq(get_latitude(s), 35.684947));

	s = addrdb_search(db, l_string(std_cm, "千代田区麹町3の４"));
	ADDRDB_ASSERT(s && float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(s && float_eq(get_latitude(s), 35.684947));
	
	s = addrdb_search(db, l_string(std_cm, "東京都千代田区麹町3ノ四"));
	ADDRDB_ASSERT(float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(float_eq(get_latitude(s), 35.684947));

	addrdb_insert_name_alias(db, l_string(std_cm, "こうじまち"), l_string(std_cm, "東京都千代田区麹町"));
	
	s = addrdb_search(db, l_string(std_cm, "こうじまち3ノ四"));
	ADDRDB_ASSERT(float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(float_eq(get_latitude(s), 35.684947));
	
	s = addrdb_search(db, l_string(std_cm, "コウじマち3ノ四"));
	ADDRDB_ASSERT(float_eq(get_longtude(s), 139.737708));
	ADDRDB_ASSERT(float_eq(get_latitude(s), 35.684947));

	printf("コウじマち3ノ四＝");
	print_sexp(s_stdout, s, PF_XML);
	
	{
		s = addrdb_kokudo_make_record_data(135.1, 35.1111, 9, 10, 100);
		addrdb_insert(db, l_string(std_cm, "東京都鷹番３の１の３"),  s);
		s = addrdb_search(db, l_string(std_cm, "東京都鷹番３の１の３"));
		ADDRDB_ASSERT(float_eq(get_longtude(s), 135.1));
		ADDRDB_ASSERT(float_eq(get_latitude(s), 35.1111));
		print_sexp(s_stdout, s, PF_XML);
	}

	s = addrdb_search(db, l_string(std_cm, "千代田区一番町二十五"));
	ADDRDB_ASSERT(float_eq(get_longtude(s), 139.742413));
	ADDRDB_ASSERT(float_eq(get_latitude(s), 35.685906));


	s = addrdb_search(db, l_string(std_cm, "コウジマチ"));
	ADDRDB_ASSERT(s);
	printf("コウジマチ＝");
	print_sexp(s_stdout, s, PF_XML);
	puts("");

	s = addrdb_search(db, l_string(std_cm, "神田須田町二丁目6"));
	ADDRDB_ASSERT(s);
	printf("神田須田町二丁目6=");
	print_sexp(s_stdout, s, PF_XML);
	puts("");

	s = addrdb_search(db, l_string(std_cm, "丸の内一丁目十一"));
	ADDRDB_ASSERT(s);
	printf("丸の内一丁目十一＝");
	print_sexp(s_stdout, s, PF_XML);
	puts("");
	
	gc_pop(0,0);
	
	addrdb_convert_csv_batch(db, "addrdb_test/source_csv", "addrdb_test/dest_csv", "Shift_JIS", 0);
	
	addrdb_close(db);
}