/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "light_xml_parser.h"
#include "xl2pdb_p.h"
#include "long_char.h"
#include "memory_routine.h"
#include "xl.h"
#include "xl.h"
#include "utils.h"
#include "xl2pdb_p.h"


extern PDB_POLYGON2D * pdb_p_list;
extern int pdb_p_list_nos;
extern GB_RECT minrect;
extern int point_nos;
extern CODE_METHOD int_cm;


#define PIT_HASH_MAX 500

typedef struct polygon_info_node{
	struct polygon_info_node *pLeft;
	struct polygon_info_node *pRight;
	char *szInfo;
	char *szCode;
} POLYGON_INFO_TABLE_NODE;

typedef struct polygon_information_table{
	POLYGON_INFO_TABLE_NODE *hash[PIT_HASH_MAX];
} POLYGON_INFOMATION_TABLE;

int pitGetCodeHashVal(const char *szCode);

//pTblから、szCodeを持ったものを検索して、informationに該当するデータへのポインタを返す
//返り値をfreeしてはいけない
char *pitGetPolygonInfoByCode(POLYGON_INFOMATION_TABLE *pTbl, const char *szCode);

#define GET_COLOR_IF(code1, code2, _r, _g, _b, _t)  \
                if(strcmp(code1,code2)==0){ \
                    GB_COLOR *pcl = (GB_COLOR*)d_alloc(sizeof(GB_COLOR), 1); \
					pcl->r = (REAL1)_r; \
					pcl->g = (REAL1)_g; \
					pcl->b = (REAL1)_b; \
					pcl->t = (REAL1)_t; \
					return pcl; \
                }

typedef struct svg_parse_info{
	FILE *fpOut;
	int nPolyNo;
	POLYGON_INFOMATION_TABLE *InfoTable;
}SVG_PARSE_INFO;

int pitGetCodeHashVal(const char *szCode)
{
	const char *p = szCode;
	int nReturn=0;
	for(;*p;++p){
		nReturn += (int)*p;
	}
	return nReturn%PIT_HASH_MAX;
}

char *pitGetPolygonInfoByCode(POLYGON_INFOMATION_TABLE *pTbl, const char *szCode)
{
	POLYGON_INFO_TABLE_NODE *pNode;
	int i;
	pNode = pTbl->hash[pitGetCodeHashVal(szCode)];
	while(pNode){
		i = strcmp(pNode->szCode, szCode);
		if(i==0)
			return pNode->szInfo;
		else if(i>0){
			pNode = pNode->pRight;
		}
		else{
			pNode = pNode->pLeft;
		}
	}
	return 0;
}

//テーブルにポリゴン情報を追加する。szCode,szInfomationは、mallocされたものでなくてはならない
void pitAttachPolygonInfo(POLYGON_INFOMATION_TABLE *tbl, char *szCode, char *szInfomation)
{
	int iComp;
	POLYGON_INFO_TABLE_NODE **ppRootNode = &(tbl->hash[pitGetCodeHashVal(szCode)]);
	POLYGON_INFO_TABLE_NODE *pNode = *ppRootNode;
	if(pNode){
		while(1){
			iComp = strcmp(pNode->szCode, szCode);
			if(iComp == 0){
				free(pNode->szCode);
				free(pNode->szInfo);
				break;
			}
			else if(iComp>0){
				if(pNode->pRight){
					pNode = pNode->pRight;
				}
				else{
					pNode->pRight = calloc(sizeof(POLYGON_INFO_TABLE_NODE),1);
					pNode = pNode->pRight;
					break;
				}
			}
			else {
				if(pNode->pLeft){
					pNode = pNode->pLeft;
				}
				else{
					pNode->pLeft = calloc(sizeof(POLYGON_INFO_TABLE_NODE),1);
					pNode = pNode->pLeft;
					break;
				}
			}
		}
	}
	if(!pNode){
		*ppRootNode = calloc(sizeof(POLYGON_INFO_TABLE_NODE),1);
		pNode = *ppRootNode;
	}
	pNode->szCode = szCode;
	pNode->szInfo = szInfomation;
}

void DestroyPolygonInfoTableNode(POLYGON_INFO_TABLE_NODE *pNode){
	if(pNode){
		DestroyPolygonInfoTableNode(pNode->pLeft);
		DestroyPolygonInfoTableNode(pNode->pRight);
		free(pNode->szCode);
		free(pNode->szInfo);
		free(pNode);
	}
}

void pitDestroyPolygonInfoTable(POLYGON_INFOMATION_TABLE *tbl){
	int i;
	for(i=0; i<PIT_HASH_MAX; ++i){
		DestroyPolygonInfoTableNode(tbl->hash[i]);
	}
	free(tbl);
}

int GetPolyTypeNameFromCode(char *szCode){
        switch(*szCode){
                case 'A':
                case 'P':
                default:
                        return PDT_CLOSE;
                case 'L':
                        return PDT_OPEN;
        }
}

GB_COLOR *GetLineColorFromCode(char *szCodeFrom){
        char szCode[6];
        strncpy(szCode, szCodeFrom,5);
		szCode[5] = '\0';
        //si_tyo
        GET_COLOR_IF(szCode, "A1105", 1.0, 0.5, 0.5, 1.0)
        //ohaza,tyoutyoumei
        GET_COLOR_IF(szCode, "A1106", 1.0, 0.5, 0.5, 1.0)
        //gaiku
        GET_COLOR_IF(szCode, "A1108", 1.0, 0.5, 0.5, 0.5)
        //douro
        GET_COLOR_IF(szCode, "L2110", 0.5, 0.5, 0.5, 1.0)
        //tetudou
        GET_COLOR_IF(szCode, "L2300", 0.2, 0.2, 0.2, 1.0)
        //eki
        GET_COLOR_IF(szCode, "L2420", 0.0, 0.0, 0.0, 1.0)
        //tetudou_siki
        GET_COLOR_IF(szCode, "A6241", 0.3, 0.0, 0.0, 1.0)
        //tosi_kouen
        GET_COLOR_IF(szCode, "A6242", 0.0, 0.2, 0.0, 1.0)
        //gakkou
        GET_COLOR_IF(szCode, "A6243", 0.0, 0.0, 0.0, 1.0)
        //jinjya
        GET_COLOR_IF(szCode, "A6244", 0.0, 0.0, 0.0, 1.0)
        //boti
        GET_COLOR_IF(szCode, "A6215", 0.0, 0.0, 0.0, 1.0)
        //sonota no jyouti
        GET_COLOR_IF(szCode, "A6200", 0.0, 0.5, 0.0, 1.0)
        //kasen
        GET_COLOR_IF(szCode, "A5101", 0.0, 0.0, 1.0, 0.0)
        //mizuumi
        GET_COLOR_IF(szCode, "A5105", 0.0, 0.0, 1.0, 1.0)
        //koukyou_tatemono
        GET_COLOR_IF(szCode, "A3500", 0.0, 0.0, 0.0, 1.0)
        //sannkaku_ten
        GET_COLOR_IF(szCode, "P7301", 1.0, 0.0, 0.0, 1.0)
        //syadou_hodou_no_kyoukaisen
        GET_COLOR_IF(szCode, "L2112", 0.1, 0.1, 0.1, 1.0)
        //douro_no_kuiki_no_kyoukaisen
        GET_COLOR_IF(szCode, "L2101", 0.1, 0.1, 0.1, 1.0)
        //kasen_no_kuiki
        GET_COLOR_IF(szCode, "A5121", 0.0, 0.0, 1.0, 0.5)
        //kasen_cyuusin_sen
        GET_COLOR_IF(szCode, "A5122", 0.0, 0.0, 1.0, 1.0)
        //eki
        GET_COLOR_IF(szCode, "P2420", 0.2, 0.2, 0.2, 1.0)
		
		//sprintf(szColor, "%.1f  %.1f  %.1f  %.1f  ", 0.0, 0.0, 0.0, 0.0);
        return 0;
}


GB_COLOR *GetPaddingColorFromCode(char *szCodeSrc){
        char szCode[6];
        strncpy(szCode, szCodeSrc, 5);
		szCode[5] = 0;
		
        //si_tyo
        GET_COLOR_IF(szCode, "A1105", 0.0, 0.0, 0.0, 0.0)
        //ohaza,tyoutyoumei
        GET_COLOR_IF(szCode, "A1106", 0.0, 0.0, 0.0, 0.0)
        //gaiku
        GET_COLOR_IF(szCode, "A1108", 0.0, 0.0, 0.0, 0.0)
        //douro
        GET_COLOR_IF(szCode, "L2110", 0.0, 0.0, 0.0, 0.0)
        //tetudou
        GET_COLOR_IF(szCode, "L2300", 0.0, 0.0, 0.0, 0.0)
        //eki
        GET_COLOR_IF(szCode, "L2420", 0.6, 0.3, 0.3, 1.0)
        //tetudou_siki
        GET_COLOR_IF(szCode, "A6241", 0.3, 0.0, 0.0, 1.0)
        //tosi_kouen
        GET_COLOR_IF(szCode, "A6242", 0.0, 0.4, 0.1, 1.0)
        //gakkou
        GET_COLOR_IF(szCode, "A6243", 0.5, 0.5, 0.5, 1.0)
        //jinjya
        GET_COLOR_IF(szCode, "A6244", 0.3, 0.1, 0.1, 1.0)
        //boti
        GET_COLOR_IF(szCode, "A6215", 0.3, 0.1, 0.1, 1.0)
        //sonota no jyouti
        GET_COLOR_IF(szCode, "A6200", 0.3, 0.1, 0.1, 1.0)
        //kasen
        GET_COLOR_IF(szCode, "A5101", 0.3, 0.3, 1.0, 1.0)
        //mizuumi
        GET_COLOR_IF(szCode, "A5105", 0.3, 0.3, 1.0, 1.0)
        //koukyou_tatemono
        GET_COLOR_IF(szCode, "A3500", 0.8, 0.8, 0.8, 1.0)
        //sannkaku_ten
        GET_COLOR_IF(szCode, "P7301", 1.0, 0.0, 0.0, 1.0)
        //syadou_hodou_no_kyoukaisen
        GET_COLOR_IF(szCode, "L2112", 0.1, 0.1, 0.1, 0.0)
        //douro_no_kuiki_no_kyoukaisen
        GET_COLOR_IF(szCode, "L2101", 0.0, 0.0, 0.0, 0.0)
        //kasen_no_kuiki
        GET_COLOR_IF(szCode, "A5121", 0.3, 0.3, 1.0, 0.5)
        //kasen_cyuusin_sen
        GET_COLOR_IF(szCode, "A5122", 0.0, 0.0, 1.0, 0.0)
        //eki
        GET_COLOR_IF(szCode, "P2420", 0.2, 0.2, 0.2, 1.0)

		//sprintf(szColor,"%.1f  %.1f  %.1f  %.1f  ", 0.0, 0.0, 0.0, 0.0);
		return 0;
}

POLYGON_INFOMATION_TABLE *pitCreatePolygonInfoTable(FILE *fpXml){
	POLYGON_INFOMATION_TABLE *tblReturn = calloc(1,sizeof(POLYGON_INFOMATION_TABLE));

	char szTempInfomation[1024];
	char *szName;
	char *szInfomation;
	char *pTemp;
	char c;
	char cQuote;
	
	while(1){
		szName = 0;
		szInfomation = 0;
		
		pTemp = szTempInfomation;
		//find tag
		for(c = fgetc(fpXml); (c!='<'); c=fgetc(fpXml)){
			if(c == EOF) 
				goto CreatePolygonInfoTableEnd;
		}
		//read tagname
		for(c=fgetc(fpXml); (c!=' ')&&(c!='/')&&c!='>'; c=fgetc(fpXml), ++pTemp){
			if(c == EOF) 
				goto CreatePolygonInfoTableEnd;
			*pTemp = c;
		}
		*pTemp = '\0';
		
		if(!strcmp(szTempInfomation, "group")){
			char *p = "puorg<";
			ungetc(c,fpXml);
			for(;*p;++p){
				ungetc(*p,fpXml);
			}
			break;
		}
		//skip if tag is not "information"
		if(strcmp(szTempInfomation,"information"))
			continue;
		//if tagname is "information", read attribute "name" and text between tag
		while(1){
			//skip blank
			for(c=fgetc(fpXml); (c==' ')||(c=='\t'); c=fgetc(fpXml)){
				if(c == EOF)
					goto CreatePolygonInfoTableEnd;
			}
			if(c == '>' || c == '/')
				break;
			//read attrname
			pTemp = szTempInfomation;
			*pTemp = c;
			++pTemp;
			for(c=fgetc(fpXml); c!='='; c=fgetc(fpXml), ++pTemp){
				if(c == EOF)
					goto CreatePolygonInfoTableEnd;
				*pTemp = c;
			}
			*pTemp = '\0';
			//if attr is "name", then read namevalue
			if(strcmp(szTempInfomation,"name"))
				continue;
			cQuote = fgetc(fpXml);
			pTemp = szTempInfomation;
			for(c=fgetc(fpXml); c != cQuote; c=fgetc(fpXml), ++pTemp){
				if(c == EOF)
					goto CreatePolygonInfoTableEnd;
				*pTemp = c;
			}
			*pTemp = '\0';
			szName = malloc(strlen(szTempInfomation)+1);
			strcpy(szName,szTempInfomation);
			//skip to end of starttag
			for(c=fgetc(fpXml); c != '>' && c!='/';c=fgetc(fpXml)){
				if(c == EOF)
					goto CreatePolygonInfoTableEnd;
			}
			//read information
			pTemp = szTempInfomation;
			if(c == '/'){
				//with no information ...
				c=fgetc(fpXml);
			}else{
				char *pEndOfInfo;
				c=fgetc(fpXml);
				do{
					for(; c != '<'; c=fgetc(fpXml), ++pTemp){
						if(c == EOF)
							goto CreatePolygonInfoTableEnd;
						*pTemp = c;
					}
					pEndOfInfo = pTemp;
					for(; c=='/' || (c!='>' && c!=' '); c=fgetc(fpXml), ++pTemp){
						if(c == EOF)
							goto CreatePolygonInfoTableEnd;
						*pTemp = c;
					}
					*pTemp = '\0';
					if(!strcmp(pEndOfInfo,"</information")){
						*pEndOfInfo = '\0';
						break;
					}
				}while(1);
			}
			*pTemp = '\0';
			szInfomation = malloc(strlen(szTempInfomation)+1);
			strcpy(szInfomation, szTempInfomation);
			break;
		}
		if(szName && szInfomation){
			pitAttachPolygonInfo(tblReturn, szName, szInfomation);
			//DEBUG_PRINT("pitAttachPolygonInfo : %s %s\n",szName,szInfomation);
		}
	}

CreatePolygonInfoTableEnd:
	return tblReturn;
}

void fReadXMLAttrData(FILE *fp, char *szBuff){
	char cDelim;
	char *pBuff;
	cDelim = fgetc(fp);
	if(szBuff){
		for(pBuff=szBuff;;pBuff++){
			if((*pBuff = fgetc(fp))== EOF || *pBuff == cDelim)
				break;
		}
		*pBuff = '\0';
	}
	else{
		char c;
		while(((c = fgetc(fp)) != EOF) && (c != cDelim));
	}
}


GB_POINT *GetPointsFromD(char *d, int *pnPointCount)
{
	int nMalloced = 20;
	int nPointCount = 0;
	char *token;
	GB_POINT *ret = (GB_POINT*)d_alloc(sizeof(GB_POINT)*nMalloced, 1);
	REAL1 x,y;
	token = strtok(d," ");
	//set points
	while(token){
		//read x
		if(!(token = strtok(NULL," ")))
			break;
		x = (REAL1)atof(token);
		//read y
		if(!(token = strtok(NULL," ")))
			break;
		y = (REAL1)atof(token);
		if(nPointCount==0 || ret[nPointCount-1].x!=x || ret[nPointCount-1].y!=y){
			ret[nPointCount].x = x;
			ret[nPointCount].y = y;
			++nPointCount;
			if(nMalloced <= nPointCount){
				nMalloced *= 2;
				ret = (GB_POINT*)d_re_alloc(ret, sizeof(GB_POINT)*nMalloced);
			}
		}
		//skip 'L' or 'M'
		if(!(token = strtok(NULL," ")))
			break;
	}
	*pnPointCount = nPointCount;
	return ret;
}

void RegistPolygonToPdbAvt(
	char *szName, 
	int nType,
	GB_COLOR *pclLineColor, 
	GB_COLOR *pclPaddingColor,
	char *szInfo,
	GB_POINT *Points,
	int nPointCount)
{
	PDB_POLYGON2D *new_p2d;
	PDB_POLYGON2D *p2;
	int nPointNo;
	
	if ( search_pdb_avt(l_string(std_cm, szName), l_string(std_cm, "")) ){
		fprintf(stderr, "duplicated polygon polyname:%s", szName);
		return;
	}
	
	new_p2d = (PDB_POLYGON2D *)d_alloc(sizeof(PDB_POLYGON2D), 1); 
	if(!new_p2d){
		fprintf(stderr, "err memory alloc : at PegistPolygonToPdbAvt");
		return;
	}
	
	//initialize new polygon
	memset(new_p2d, 0, sizeof(PDB_POLYGON2D));
	new_p2d->minrect.br.x = -1;
	new_p2d->minrect.br.y = -1;
	new_p2d->subname = nl_copy_str(std_cm,"0");
	
	//set name
	new_p2d->name = nl_copy_str(std_cm, szName);
	
	//set type
	new_p2d->type = nType;
	
	//set line color
	new_p2d->padding = *pclPaddingColor;
	
	//set padding color
	new_p2d->line = *pclLineColor;
	
	//set information
	if(szInfo){
		//get SEXP from 'szInfo'(char * buffer)
		STREAM * st;
		L_CHAR * lc;
		lc = l_string(std_cm, szInfo);
		st = s_open_string_read(lc, &int_cm, l_strlen(lc)*sizeof(L_CHAR),1);
		new_p2d->info = car(init_parse(st, l_string(std_cm,"Parse"), 0));
		//DEBUG_PRINT(szInfo);
		//print_sexp(s_stdout, new_p2d->info, PF_MULTI_ROOT|PF_LISP);
	}
	
	//set points
	for(nPointNo=0; nPointNo<nPointCount; ++nPointNo){
		//create pdb point
		PDB_PD_POINT *new_pdb_point = (PDB_PD_POINT *)d_alloc(sizeof(PDB_PD_POINT), 1);
		memset(new_pdb_point,0,sizeof(PDB_PD_POINT));
		new_pdb_point->reso = -1;
		new_pdb_point->no = nPointNo;
		new_pdb_point->lod_max = 0;
		new_pdb_point->lod_min = 0;
		new_pdb_point->p = Points[nPointNo];
		//append new_point to polygon
		new_pdb_point->next = new_p2d->point;
		new_p2d->point = new_pdb_point;
		
		//calc minimum rect
		insert_rect(&(new_p2d->minrect), Points[nPointNo]);
		insert_rect(&minrect, Points[nPointNo]);
		
		//increment grobal 'point_nos'
		point_nos++;
	}
	
	//append polygon to pdblist
	new_p2d->next = pdb_p_list;
	pdb_p_list = new_p2d;
	pdb_p_list_nos ++;
	//insert polygon to avt_tree
	insert_pdb_avt(new_p2d);
	
	p2 = search_sn_avt(new_p2d->name);
	if ( p2 == 0 ) {
		new_p2d->samename = 0;
		insert_sn_avt(new_p2d);
	}
	else {
		new_p2d->samename = p2->samename;
		p2->samename = new_p2d;
	}
}

void *SVGBeginElementFunc(XML_ELEMENT *ele, void *Param){
	SVG_PARSE_INFO *pInfo = (SVG_PARSE_INFO *)Param;
	XML_ATTR *pAttr;
	
	if(!strcmp(ele->szName, "path")){
		char szInfoBuffer[4096];
		char *data;
		char *szName;
		int nPolyType;
		char *szInfo=0;
		
		GB_POINT *Points;
		GB_COLOR *pclLine;
		GB_COLOR *pclPadding;
		int nPointCount;
		
		//get attributes
		for(pAttr=ele->attr; pAttr; pAttr = pAttr->pNext){
			if(!strcmp(pAttr->szName, "d")){
				data = pAttr->szValue;
			}
			else if(!strcmp(pAttr->szName, "id")){
				szName = pAttr->szValue;
				nPolyType = GetPolyTypeNameFromCode(szName);
				pclLine = GetLineColorFromCode(szName);
				pclPadding = GetPaddingColorFromCode(szName);
			}
		}

		//get polygon information
		if(pInfo->InfoTable){
			szInfo = pitGetPolygonInfoByCode(pInfo->InfoTable,szName);
			if(szInfo){
				sprintf(szInfoBuffer, 
					"<information name=\"%s\" scheme=\"kokudo-digital-map-2500\">%s</information>",
					szName, szInfo);
				szInfo = szInfoBuffer;
			}
		}
		
		//get points from data string
		Points = GetPointsFromD(data, &nPointCount);
		
		//regist polygon to pdb_avt
		RegistPolygonToPdbAvt(
			szName,
			nPolyType, 
			pclLine,
			pclPadding,
			szInfo,
			Points,
			nPointCount);
		
		d_f_ree(Points);
		pInfo->nPolyNo++;
	}
	return Param;
}

int load_svg(int argc,char **argv)
{
        FILE *fpSvg,*fpXml;
		POLYGON_INFOMATION_TABLE *tblInfo=0;
        XML_EVENT x_eve;
		SVG_PARSE_INFO spi;

		DEBUG_PRINT("load svg begin\n");
		
#ifdef _DEBUG
		char *FileNames[2] = {"kokudou.svg","kokudou.xml"};
#else
		int i;
		char *FileNames[2];
		for(i=1;i<3;++i){
			FileNames[i-1] = argv[i];
		}
#endif
		memset(&spi,0,sizeof(SVG_PARSE_INFO));
		memset(&x_eve,0,sizeof(XML_EVENT));
        fpSvg = fopen(FileNames[0],"r");
        if(!fpSvg){
                fprintf(stderr,"err open file %s\n", FileNames[0]);
                return 0;
        }
		DEBUG_PRINT("open svg file :%s\n", FileNames[0]);
		
		if(strcmp(FileNames[1],"/")){
			fpXml = fopen(FileNames[1],"r");
			if(!fpXml){
					fprintf(stderr,"err open file %s\n", FileNames[1]);
					fclose(fpSvg);
					return 0;
			}
			DEBUG_PRINT("open xml file :%s\n", FileNames[1]);
			DEBUG_PRINT("create info table\n");
			tblInfo = pitCreatePolygonInfoTable(fpXml);
			fclose(fpXml);
		}
		
		spi.nPolyNo = 0;
		spi.InfoTable = tblInfo;
		x_eve.beginElement = SVGBeginElementFunc;
		DEBUG_PRINT("parse svg\n");
		XMLParse(fpSvg, &x_eve, &spi);
		
		//output groups
		/*
		if(!feof(fpXml)){
			int nBuffSize = 65536*4;
			char *szGroupBuff = (char *)malloc(nBuffSize);
			for(; fgets(szGroupBuff, nBuffSize, fpXml); ){
				if(!strncmp(szGroupBuff,"</gbs>",strlen("</gbs>")))
					break;
				fputs(szGroupBuff, fpOut);
			}
			free(szGroupBuff);
		}
		*/
		DEBUG_PRINT("destroy info table\n");
		if(tblInfo)
			pitDestroyPolygonInfoTable(tblInfo);
		fclose(fpSvg);
		DEBUG_PRINT("load svg end\n");
		return 1;
}
