/**********************************************************************
 
	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 "map_matrix.h"
#include "zuyou_data.h"
#include "arc_data.h"
#include <string.h>
#include <math.h>
void free_map_matrix(MAP_MATRIX *pMatrix)
{
	int i;
	for(i=0;i<pMatrix->nColSize;++i)
	{
		int j;
		for(j=0;j<pMatrix->nRowSize;++j)
		{
			free_arc_data(pMatrix->maps[i][j].pArcData);
			free_poly_data(pMatrix->maps[i][j].pPolyData);
			free_attr_data(pMatrix->maps[i][j].pAttrData);
		}
		free(pMatrix->maps[i]);
	}
	free(pMatrix->maps);
	free(pMatrix);
}


int SetMapMatrixSize(MAP_MATRIX *pMatrix, int nColSize, int nRowSize, int nColOffset, int nRowOffset)
{
	MAP_NODE **ppOldMaps = pMatrix->maps;
	int nOldRowSize,nOldColSize;
	void *p;
	int i;
	
	if(ppOldMaps){
		nOldRowSize = pMatrix->nRowSize;
		nOldColSize = pMatrix->nColSize;
	}
	else{
		nOldRowSize = 0;
		nOldColSize = 0;
	}

	if(nOldRowSize == nRowSize &&
		nOldColSize == nColSize)
		return ID_ERR_NOERROR;
	
	p = malloc(sizeof(MAP_NODE*)*nColSize);
	if(!p)
		return ID_ERR_NOT_ENOUGH_MEMORY;
	memset(p, '\0', sizeof(MAP_NODE*)*nColSize);
	pMatrix->maps = (MAP_NODE **)p;
	pMatrix->nColSize = nColSize;
	for(i=0;i<nColSize;++i){
		p = malloc(sizeof(MAP_NODE)*nRowSize);
		if(!p)
			return ID_ERR_NOT_ENOUGH_MEMORY;
		memset(p, '\0', sizeof(MAP_NODE)*nRowSize);
		pMatrix->maps[i] = (MAP_NODE*)p;
	}
	pMatrix->nRowSize = nRowSize;
	
	for(i=0;i<nOldColSize;++i){
		if(i+nColOffset < nColSize){
			int j;
			for(j=0;j<nOldRowSize;++i){
				if(j+nRowOffset >= nRowSize)
					break;
				pMatrix->maps[i+nColOffset][j+nRowOffset] = ppOldMaps[i][j];
			}
		}
		if(ppOldMaps[i])
			free(ppOldMaps[i]);
	}
	if(ppOldMaps)
		free(ppOldMaps);
	
	return ID_ERR_NOERROR;
}



/* SetPathToBunruiSet(ID_ARC, "mizu", "mizu.arc", 0, pSet, szDataRootPath); */
void SetPathToBunruiSet(int DataType, const char *szBunruiName, const char *szFileName, 
				   int nIndex, BUNRUI_FILE_NAME_SET *set, const char *szDataRootPath)
{
	set->nDataTypes[nIndex] = DataType;
	sprintf( set->szFileNames[nIndex], "%s%s%c%s", szDataRootPath, szBunruiName, PATH_DEVIDER, szFileName);
}

/*
void MakePathFromDataFileType(int DataFileType, const char *szDataRootPath, 
							  char **szFileNames, int *pnDataTypes)
*/
void MakePathsFromDataFileType(int DataFileType, const char *szDataRootPath, 
							  BUNRUI_FILE_NAME_SET *pSet)
{
/*	ADDPATH(ID, PATH, BUNRUI, FILENAME) */
	switch(DataFileType){
		case ID_SI_TYO:
			SetPathToBunruiSet(ID_ARC, "gyousei", "gyousei.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "gyousei", "si_tyo.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "gyousei", "si_tyo.atr", 2, pSet, szDataRootPath);
		break;
		case ID_TYOME:
			SetPathToBunruiSet(ID_ARC, "gyousei", "gyousei.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "gyousei", "tyome.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "gyousei", "tyome.atr", 2, pSet, szDataRootPath);
		break;
		case ID_GAIKU:
			SetPathToBunruiSet(ID_ARC, "gaiku", "gaiku.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "gaiku", "gaiku.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "gaiku", "gaiku.atr", 2, pSet, szDataRootPath);
		break;
		case ID_KASEN:
			SetPathToBunruiSet(ID_ARC, "kasen", "kasen.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "kasen", "kasen.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "kasen", "kasen.atr", 2, pSet, szDataRootPath);
		break;
		case ID_KASEN2:
			SetPathToBunruiSet(ID_ARC, "kasen", "kasen.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "kasen", "kasen.atr", 1, pSet, szDataRootPath);
		break;
		case ID_ZYOUTI:
			SetPathToBunruiSet(ID_ARC, "others", "others.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "others", "zyouti.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "others", "zyouti.atr", 2, pSet, szDataRootPath);
		break;
		case ID_MIZU:
			SetPathToBunruiSet(ID_ARC, "mizu", "mizu.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "mizu", "mizu.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "mizu", "mizu.atr", 2, pSet, szDataRootPath);
		break;
		case ID_TATEMONO:
			SetPathToBunruiSet(ID_ARC, "tatemono", "tatemono.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_POLYGON, "tatemono", "tatemono.pgn", 1, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "tatemono", "tatemono.atr", 2, pSet, szDataRootPath);
		break;
		case ID_TETUDOU:
			SetPathToBunruiSet(ID_ARC, "others", "others.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "others", "tetudou.atr", 1, pSet, szDataRootPath);
		break;
		case ID_ROAD:
			SetPathToBunruiSet(ID_ARC, "road", "roadntwk.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "road", "road.atr", 1, pSet, szDataRootPath);
		break;
		case ID_KOKUDOU:
			SetPathToBunruiSet(ID_ARC, "kokudou", "kokudou.arc", 0, pSet, szDataRootPath);
			SetPathToBunruiSet(ID_ATTER, "kokudou", "kokudou.atr", 1, pSet, szDataRootPath);
		break;
	}
}


int ReadMapsFromMapName(MAP_MATRIX *pMatrix, 
					  int nOriginCol,
					  int nOriginRow,
					  const char *szDataRootPath, 
					  const char *szMapName,
					  int DataFileType)
{
	MAP_NODE *pMap;
	BUNRUI_FILE_NAME_SET fs;
	char CharMapPath[MAX_PATH];
	int i;
	memset(&fs, 0, sizeof(fs));
	if(szMapName == NULL)
		return ID_ERR_NOERROR;
	if(strlen(szMapName) == 0)
		return ID_ERR_NOERROR;
	
	pMap = &(pMatrix->maps[nOriginCol][nOriginRow]);
	pMap->nCol = nOriginCol;
	pMap->nRow = nOriginRow;
	strcpy(pMap->szMapName,szMapName);
	
	sprintf(CharMapPath, "%s%s%c", szDataRootPath, szMapName, PATH_DEVIDER);
	MakePathsFromDataFileType(DataFileType, CharMapPath, &fs);
	for(i=0;i<3;++i){
		void *p;
		if(ReadKibanFile(fs.szFileNames[i], fs.nDataTypes[i], (void **)&p, RKF_ABSOLUTE) == ID_ERR_NOERROR){
			switch(fs.nDataTypes[i]){
				case ID_ARC:
					pMap->pArcData = (ARC_DATA*)p;
					break;
				case ID_POLYGON:
					pMap->pPolyData = (POLY_DATA*)p;
					break;
				case ID_ATTER:
					pMap->pAttrData = (ATTR_DATA*)p;
					break;
			}
		}
	}
	if(pMap->pArcData==NULL){
		free_poly_data(pMap->pPolyData);
		free_attr_data(pMap->pAttrData);
		pMap->pPolyData=NULL;
		pMap->pAttrData=NULL;
	}
	return ID_ERR_NOERROR;
}

MAP_MATRIX *MMCreate(const char *szDataRootPath, int DataFileType, KIBAN2SVG_PROCESSING_INFO_DATA *pInfo)
{
	MAP_MATRIX *pMatrix;

	pMatrix = (MAP_MATRIX *)malloc(sizeof(MAP_MATRIX));
	memset(pMatrix, 0, sizeof(MAP_MATRIX));

	SetMapMatrixSize(pMatrix, 
		(int)((pInfo->rAllMap.right - pInfo->rAllMap.left)/pInfo->nOneMapWidth),
		(int)((pInfo->rAllMap.bottom - pInfo->rAllMap.top)/pInfo->nOneMapHeight),
		0,0);
	
	switch(DataFileType){
		/* polygon type */
		case ID_SI_TYO:
		case ID_TYOME:
		case ID_GAIKU:
		case ID_KASEN:
		case ID_ZYOUTI:
		case ID_MIZU:
		case ID_TATEMONO:
			{
				char szMapName[8];
				int i;
				char *ptr;
				pMatrix->DataType = ID_ARC | ID_POLYGON | ID_ATTER;
				szMapName[7] = '\0';
				ptr = pInfo->szMapNames;
				for(i=0;i<pInfo->nMapNameCount;++i){
					strncpy(szMapName, ptr, 7);
					ReadMapsFromMapName(pMatrix, pInfo->Cols[i], pInfo->Rows[i], szDataRootPath, szMapName, DataFileType);
					ptr+=8;
				}
			}
			break;
		/* arc types */
		case ID_ROAD:
		case ID_KOKUDOU:
		case ID_KASEN2:
		case ID_TETUDOU:
			{
				char szMapName[8];
				char *ptr;
				int i;
				pMatrix->DataType = ID_ARC | ID_ATTER;
				szMapName[7] = '\0';
				ptr = pInfo->szMapNames;
				for(i=0;i<pInfo->nMapNameCount;++i){
					strncpy(szMapName, ptr, 7);
					ReadMapsFromMapName(pMatrix, pInfo->Cols[i], pInfo->Rows[i], szDataRootPath, szMapName, DataFileType);
					ptr+=8;
				}
			}
	}
	return pMatrix;
}


void FindBorderMap(MAP_MATRIX *pMatrix, int nCenterCol, int nCenterRow, GB_POINT *pt, int *pnFoundCol, int *pnFoundRow){
	GB_RECT *rCurrent;
	*pnFoundRow = nCenterRow;
	*pnFoundCol = nCenterCol;
	rCurrent = &(pMatrix->maps[nCenterCol][nCenterRow].pArcData->header.r);
	if(pt->y == rCurrent->top){
		*pnFoundRow = nCenterRow-1;
	}
	if(pt->x == rCurrent->left){
		*pnFoundCol = nCenterCol-1;
	}
	if(pt->y == rCurrent->bottom){
		*pnFoundRow = nCenterRow+1;
	}
	if(pt->x == rCurrent->right){
		*pnFoundCol = nCenterCol+1;
	}
}

int FindAndJoinPolyByArc(MAP_MATRIX *pMatrix, POLY_ID_RECORD *pDstPolyRecord, ATTR_RECORD *pDstAttrRecord, int *pnMallocedPoint,
						  GB_POINT *ptFindBegin, GB_POINT *ptFindEnd, int nCol, int nRow)
{
	POLY_DATA *pPoly;
	ARC_DATA *pArc;
	int nNewCol;
	int nNewRow;
	int bFound;
	ARC_ID_RECORD *pRecord;
	int nArcID; 
	int i;
	int j;
	
	FindBorderMap(pMatrix, nCol, nRow, ptFindBegin, &nNewCol, &nNewRow);
	if(nNewCol<0 || nNewRow<0 || pMatrix->nColSize<=nNewCol || pMatrix->nRowSize<=nNewRow)
		return ID_ERR_MAP_NOTFOUND;
	
	pPoly = pMatrix->maps[nNewCol][nNewRow].pPolyData;
	pArc = pMatrix->maps[nNewCol][nNewRow].pArcData;
	if((!pPoly) || (!pArc))
		return ID_ERR_MAP_NOTFOUND;

	bFound = 0;
	for(i=0; i<pArc->header.record_count; ++i){
		int pEnd;
		pRecord = &(pArc->record[i]);
		if(pRecord->kind != 5 || pRecord->point_count <= 0)
			continue;
		
		pEnd = pRecord->point_count-1;
		if((pRecord->points[0].x == ptFindBegin->x && pRecord->points[0].y == ptFindBegin->y ) && 
			(pRecord->points[1].x == ptFindEnd->x && pRecord->points[1].y == ptFindEnd->y))
		{
			bFound = -1; /* mutch begin point */
		}
		else if ((pRecord->points[pEnd].x == ptFindBegin->x && pRecord->points[pEnd].y == ptFindBegin->y) &&
				  (pRecord->points[pEnd-1].x == ptFindEnd->x && pRecord->points[pEnd-1].y == ptFindEnd->y))
		{
			bFound = 1; /* mutch end point */
		}
		if(bFound){
			nArcID = pRecord->id;
			break;
		}
	}
	/* skip if arc not found. */
	if(!bFound)
		return ID_ERR_MAP_NOTFOUND;
	
	for(j=0;j<pPoly->header.record_count;++j){
		POLY_ID_RECORD *pPolyRecord = &(pPoly->record[j]);
		int k;
		for(k=0;k<pPolyRecord->count;++k){
			if(pPolyRecord->arcs[k].arc_id == nArcID){
				int l,m;
				int bInvert = 0;
				
				/* polygon has found, but already joined. (skip) */
				if(pPolyRecord->flags & PF_NOTFINDFORJOIN)
					return ID_ERR_NOERROR;
		
				if((pPolyRecord->arcs[k].direction) && (bFound == -1) ||
					!((pPolyRecord->arcs[k].direction)) && (bFound == 1)
					){
					bInvert=1;
				}
				
				pPolyRecord->flags |= PF_NOTFINDFORJOIN;
				
				l = k;
				for(m=0;m<pPolyRecord->count;++m){
					ARC_ID_RECORD *pArcID;
					int bAddRecord=1;

					if(bInvert){
						--l;
						if(l==-1)
							l=pPolyRecord->count-1;
					}
					else{
						++l;
						if(l==pPolyRecord->count)
							l=0;
					}
					
					pArcID = GetArcIDRecord(pArc, pPolyRecord->arcs[l].arc_id);
					if(pArcID==NULL){
						return ID_ERR_NOERROR;
					}
					if(pArcID->kind == 5){
						GB_POINT *ptBegin;
						GB_POINT *ptEnd;
						int MaxOfN;
						
						bAddRecord = 0;
						
						if(m==pPolyRecord->count-1)
							MaxOfN = pArcID->point_count-1;
						else
							MaxOfN = pArcID->point_count;
						
						if(pPolyRecord->arcs[l].direction && !bInvert || 
							!(pPolyRecord->arcs[l].direction) && bInvert){
							int n=0;
							ptBegin = &(pArcID->points[n]);
							ptEnd = &(pArcID->points[n+1]);
							if(FindAndJoinPolyByArc(pMatrix, pDstPolyRecord, pDstAttrRecord, pnMallocedPoint, ptBegin, ptEnd, nNewCol, nNewRow)==
								ID_ERR_MAP_NOTFOUND){
							}
						}
						else{
							int n=MaxOfN-1;
							ptBegin = &(pArcID->points[n]);
							ptEnd = &(pArcID->points[n-1]);
							if(FindAndJoinPolyByArc(pMatrix, pDstPolyRecord, pDstAttrRecord, pnMallocedPoint, ptBegin, ptEnd, nNewCol, nNewRow)==
								ID_ERR_MAP_NOTFOUND){
							}
						}
					}
					if(bAddRecord){
						if(*pnMallocedPoint < pDstPolyRecord->count + pArcID->point_count)
						{
							GB_POINT *p;
							int pts;
							
							p = (GB_POINT*)realloc(pDstPolyRecord->points, sizeof(GB_POINT)*((*pnMallocedPoint)*2+pArcID->point_count));
							if(!p)
								return ID_ERR_NOT_ENOUGH_MEMORY;
							pts = (int)(*pnMallocedPoint)*2+pArcID->point_count;
							*pnMallocedPoint = pts;
							pDstPolyRecord->points= p;
						}
						if(!(pPolyRecord->arcs[l].direction) && bInvert || 
							(pPolyRecord->arcs[l].direction) && !bInvert){
							memcpy(&(pDstPolyRecord->points[pDstPolyRecord->count]),
								pArcID->points, sizeof(GB_POINT) * pArcID->point_count);
						}
						else{
							GB_POINT *pt = (GB_POINT*)&(pDstPolyRecord->points[pDstPolyRecord->count]);
							int m;
							for(m=pArcID->point_count-1;m>=0;--m){
								pt->x = pArcID->points[m].x;
								pt->y = pArcID->points[m].y;
								pt++;
							}
						}

						pDstPolyRecord->count += pArcID->point_count;
					}
				}
			}
		}
	}
	return ID_ERR_NOERROR;
}


int JoinPolyInMatrix(MAP_MATRIX *pMatrix, POLY_DATA **ppPolyJoined, ATTR_DATA **ppAttrJoined){
	unsigned int nTotalPolyRecord=0;
	unsigned int nTotalAttrRecord=0;
	POLY_DATA *pPolyJoined;
	ATTR_DATA *pAttrJoined;
	GB_RECT *pDstRect;
	int nNewRecordID;
	POLY_ID_RECORD *pDstPolyRecord;
	ATTR_RECORD *pDstAttrRecord;
	int i,j,k;
	ARC_DATA *pArcSrc;
	POLY_DATA *pPolySrc;
	ATTR_DATA *pAttrSrc;
	
	*ppPolyJoined = malloc_poly_data();
	pPolyJoined = *ppPolyJoined;
	*ppAttrJoined = malloc_attr_data();
	pAttrJoined = *ppAttrJoined;
	
	pDstRect = &(pPolyJoined->header.r);
	for(i=0;i<pMatrix->nColSize;++i){
		int j;
		for(j=0;j<pMatrix->nRowSize;++j){
			ATTR_DATA *pAttrSrc = (ATTR_DATA*)(pMatrix->maps[i][j].pAttrData);
			POLY_DATA *pPolySrc = (POLY_DATA*)(pMatrix->maps[i][j].pPolyData);
			ARC_DATA *pArcSrc = (ARC_DATA*)(pMatrix->maps[i][j].pArcData);
			if(!pPolySrc)
				continue;
			if(pDstRect->left == 0 && pDstRect->right == 0){
				memcpy(pDstRect, &(pPolySrc->header.r), sizeof(GB_RECT));
			}
			else{
				MaximizeRect(pDstRect, &(pPolySrc->header.r));
			}
			
			nTotalPolyRecord += pPolySrc->header.record_count;
			if(pAttrSrc)
				nTotalAttrRecord += pAttrSrc->header.record_count;
		}
	}
	
	pPolyJoined->record = (POLY_ID_RECORD *)malloc(sizeof(POLY_ID_RECORD)*nTotalPolyRecord);
	memset(pPolyJoined->record , '\0', sizeof(POLY_ID_RECORD)*nTotalPolyRecord);
	pAttrJoined->record = (ATTR_RECORD *)malloc(sizeof(ATTR_RECORD)*nTotalAttrRecord);
	memset(pAttrJoined->record , '\0', sizeof(ATTR_RECORD)*nTotalAttrRecord);
	
	if(nTotalPolyRecord==0 || nTotalAttrRecord==0){
		return ID_ERR_NOERROR;
	}

	nNewRecordID = 0;
	pDstPolyRecord = pPolyJoined->record;
	pDstAttrRecord = pAttrJoined->record;
	pDstPolyRecord->arcs_type = 1;
	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			pArcSrc = (ARC_DATA*)(pMatrix->maps[i][j].pArcData);
			pPolySrc = (POLY_DATA*)(pMatrix->maps[i][j].pPolyData);
			pAttrSrc = (ATTR_DATA*)(pMatrix->maps[i][j].pAttrData);
			if(!pArcSrc || !pPolySrc)
				continue;
			
			for(k=0; k<pPolySrc->header.record_count; ++k){
				int bFoundAttr = 0;
				int bIgnore = 0;
				int l;
				unsigned int nInsertPos = 0;
				int nMalloced = 0;
				POLY_ID_RECORD *pSrcPolyRecord;

				pSrcPolyRecord = &(pPolySrc->record[k]);

				if(pSrcPolyRecord->flags & PF_NOTFINDFORJOIN)
					continue;

				pSrcPolyRecord->flags |= PF_NOTFINDFORJOIN;

				pDstPolyRecord->id = nNewRecordID;
				strcpy(pDstPolyRecord->code,pSrcPolyRecord->code);
				if(pAttrSrc){
					for(l=0;l<pAttrSrc->header.record_count; ++l){
						if(pSrcPolyRecord->id == pAttrSrc->record[l].id){
							int m;
							memcpy(pDstAttrRecord, &(pAttrSrc->record[l]), sizeof(ATTR_RECORD));
							for(m=0;m<pDstAttrRecord->attr_count;++m){
								pDstAttrRecord->pszAttrs[m] = (char *)malloc(strlen(pAttrSrc->record[l].pszAttrs[m])+1);
								strcpy(pDstAttrRecord->pszAttrs[m], pAttrSrc->record[l].pszAttrs[m]);
							}
							pDstAttrRecord->id = nNewRecordID;
							bFoundAttr = 1;
							break;
						}
					}
				}
				if(bIgnore)
					continue;
				
				nInsertPos = 0;
				nMalloced = 0;
				
				for(l=0; l < pSrcPolyRecord->count; ++l){
					ARC_ID_RECORD *pFoundArcRecord = NULL;
					int m;
					for(m=0; m<pArcSrc->header.record_count; ++m){
						ARC_ID_RECORD *pSrcArcRecord = &(pArcSrc->record[m]);
						if(pSrcPolyRecord->arcs[l].arc_id == pSrcArcRecord->id){
							pFoundArcRecord = pSrcArcRecord;
							break;
						}
					}
					
					if(pFoundArcRecord){
						if(nMalloced==0){
							pDstPolyRecord->points = (GB_POINT*)malloc(sizeof(GB_POINT) *10);
							nMalloced = 10;
						}
						
						if(pFoundArcRecord->kind == 5){
							GB_POINT *ptBegin,*ptEnd;
							int n;
							
							if(pSrcPolyRecord->arcs[l].direction){
								ptBegin = &(pFoundArcRecord->points[0]);
								ptEnd = &(pFoundArcRecord->points[1]);
							}
							else{
								ptBegin = &(pFoundArcRecord->points[pFoundArcRecord->point_count-1]);
								ptEnd = &(pFoundArcRecord->points[pFoundArcRecord->point_count-2]);
							}
							for(n=0;n<1;++n){
								if(bFoundAttr)
								{
									if(FindAndJoinPolyByArc(pMatrix, pDstPolyRecord, pDstAttrRecord, &nMalloced, 
										ptBegin, ptEnd, i, j) == ID_ERR_MAP_NOTFOUND){
									}
								}
								else{
									if(FindAndJoinPolyByArc(pMatrix, pDstPolyRecord, NULL, &nMalloced, 
										ptBegin, ptEnd, i, j) == ID_ERR_MAP_NOTFOUND){
									}
								}
								if(pSrcPolyRecord->arcs[l].direction){
									++ptBegin;
									++ptEnd;
								}
								else{
									--ptBegin;
									--ptEnd;
								}
							}
						}
						else{
							if(pDstPolyRecord->count+pFoundArcRecord->point_count >= nMalloced){
								pDstPolyRecord->points = (GB_POINT*)realloc(pDstPolyRecord->points, 
										sizeof(GB_POINT) * (nMalloced*2 + pFoundArcRecord->point_count));
								nMalloced = (nMalloced*2 + pFoundArcRecord->point_count);
							}
							if(pSrcPolyRecord->arcs[l].direction){
								memcpy(&(pDstPolyRecord->points[pDstPolyRecord->count]), pFoundArcRecord->points, sizeof(GB_POINT) * pFoundArcRecord->point_count);
							}
							else{
								GB_POINT *pt = (GB_POINT*)&(pDstPolyRecord->points[pDstPolyRecord->count]);
								int m;
								for(m=pFoundArcRecord->point_count-1; m>=0; --m){
									pt->x = pFoundArcRecord->points[m].x;
									pt->y = pFoundArcRecord->points[m].y;
									++pt;
								}
							}
							pDstPolyRecord->count += pFoundArcRecord->point_count;
						}
					}
				}
				
				++pDstPolyRecord;
				pPolyJoined->header.record_count++;
				
				if(bFoundAttr){
					++pDstAttrRecord;
					pAttrJoined->header.record_count++;
				}
				++nNewRecordID;
			}
		}
	}
	return ID_ERR_NOERROR;
}


double GetApplyLevelAsRoad(GB_POINT *ptO, GB_POINT *pt1, GB_POINT *pt2){
	double pt1len,pt2len,x,y;
	pt1len = sqrt(pow((pt1->x - ptO->x),2) + pow((pt1->y - ptO->y),2)); 
	pt2len = sqrt(pow((pt2->x - ptO->x),2) + pow((pt2->y - ptO->y),2)); 
	x = (ptO->x - pt1->x) / pt1len - (ptO->x - pt2->x) / pt2len;
	y = (ptO->y - pt1->y) / pt1len - (ptO->y - pt2->y) / pt2len;
	return x*x+y*y;
}

JOIN_ARC_CANDIDATE *GetMostApplyArcToJoin(GB_POINT *ptFrom, GB_POINT *ptFrom2, V_LIST *pCandidateList){
	JOIN_ARC_CANDIDATE *pReturn=NULL;
	JOIN_ARC_CANDIDATE *p;
	double dMaxLevel=-1;
	V_LIST *pOld1=0;
	pCandidateList=vl_Back(pCandidateList);
	for(;pCandidateList; pCandidateList = pCandidateList->pPrev){
		p = (JOIN_ARC_CANDIDATE *)(pCandidateList->Data);
		if(p->HitPos == JAC_HIT_BEGIN){
			if(dMaxLevel<GetApplyLevelAsRoad(ptFrom, &(p->pRecord->points[1]), ptFrom2)){
				pReturn = p;
			}
		}
		else{
			if(dMaxLevel<GetApplyLevelAsRoad(ptFrom, &(p->pRecord->points[p->pRecord->point_count-2]), ptFrom2)){
				pReturn = p;
			}
		}
	}
	return pReturn;
}

void FindJoinArcs(ARC_ID_RECORD_PTR_LIST *pJoinArcList,  
				  GB_POINT *ptFrom, GB_POINT *ptFrom2, char *szCode, MAP_MATRIX *pMapMatrix, int nCol, int nRow, int Direction, int nRefferCol, int nRefferRow){
	GB_POINT *ptBegin,*ptEnd;
	JOIN_ARC_CANDIDATE *pJoin;
	ARC_ID_RECORD_PTR_LIST *pDest;
	V_LIST *pCandidateList = NULL;
	int i;
	ARC_DATA *pArcSrc = pMapMatrix->maps[nCol][nRow].pArcData;
	int nNextCol=nCol,nNextRow=nRow;
	int nFoundCount=0;

	if(!pArcSrc)
		return;
	while(1){
		pCandidateList = NULL;
		for(i=0;i<pArcSrc->header.record_count;++i){
			if(pArcSrc->record[i].flags & AF_NOTFINDFORJOIN)
				continue;
			if(strcmp(pArcSrc->record[i].code, szCode))
				continue;
			ptBegin = &(pArcSrc->record[i].points[0]);
			ptEnd = &(pArcSrc->record[i].points[pArcSrc->record[i].point_count-1]);

			if(!memcmp(ptFrom, ptBegin, sizeof(GB_POINT))){
				JOIN_ARC_CANDIDATE *p= (JOIN_ARC_CANDIDATE *)calloc(sizeof(JOIN_ARC_CANDIDATE),1);
				p->HitPos = JAC_HIT_BEGIN;
				p->pRecord = &(pArcSrc->record[i]);
				pCandidateList = vl_PushBack(pCandidateList, (void *)p);
			}
			else if(!memcmp(ptFrom, ptEnd, sizeof(GB_POINT))){
				JOIN_ARC_CANDIDATE *p= (JOIN_ARC_CANDIDATE *)calloc(sizeof(JOIN_ARC_CANDIDATE),1);
				p->HitPos = JAC_HIT_END;
				p->pRecord = &(pArcSrc->record[i]);
				pCandidateList = vl_PushBack(pCandidateList, (void *)p);
			}
		}
		if(!pCandidateList)
			break;
		++nFoundCount;
		
		if(Direction == FJA_TO_PREV){
			pDest = (ARC_ID_RECORD_PTR_LIST *)calloc(sizeof(ARC_ID_RECORD_PTR_LIST),1);
			pJoinArcList->pPrev = pDest;
			pDest->pNext = pJoinArcList;
		}
		else{
			pDest = (ARC_ID_RECORD_PTR_LIST *)calloc(sizeof(ARC_ID_RECORD_PTR_LIST),1);
			pJoinArcList->pNext = pDest;
			pDest->pPrev = pJoinArcList;
		}
		pJoinArcList = pDest;
		
		pDest->pMap = &(pMapMatrix->maps[nCol][nRow]);
		
		pJoin = GetMostApplyArcToJoin(ptFrom, ptFrom2, pCandidateList);
		pJoin->pRecord->flags |= AF_NOTFINDFORJOIN;
		pDest->pRecord = pJoin->pRecord;
		
		if(pJoin->HitPos == JAC_HIT_BEGIN){
			ptFrom = &(pJoin->pRecord->points[pJoin->pRecord->point_count-1]);
			ptFrom2 = &(pJoin->pRecord->points[pJoin->pRecord->point_count-2]);
			if(Direction == FJA_TO_PREV)
				pDest->pRecord->flags |= AF_INVERTFORJOIN;
		}
		else{
			ptFrom = &(pJoin->pRecord->points[0]);
			ptFrom2 = &(pJoin->pRecord->points[1]);
			if(Direction == FJA_TO_NEXT)
				pDest->pRecord->flags |= AF_INVERTFORJOIN;
		}
		vl_Free(pCandidateList, VL_FREEDATA);
		pCandidateList = NULL;
	}
	
	FindBorderMap(pMapMatrix, nCol, nRow, ptFrom, &nNextCol, &nNextRow);

	if( (nNextCol>=pMapMatrix->nColSize) || (nNextRow>=pMapMatrix->nRowSize) ) {
		return;
	}

	if((nNextCol != nCol || nNextRow != nRow ) && 
		nNextCol != -1 && 
		nNextRow != -1 && 
		(
			(nFoundCount || nNextCol != nRefferCol) || 
			(nFoundCount || nNextRow != nRefferRow) 
		) 
		){
		FindJoinArcs(pJoinArcList, ptFrom, ptFrom2, szCode, pMapMatrix, nNextCol, nNextRow, Direction, nCol, nRow);
	}
}

void FindJoinArcsInMap(MAP_MATRIX *pMatrix, int nCol, int nRow, V_LIST **ppJoinArcsList)
{
	MAP_NODE *pMap = &(pMatrix->maps[nCol][nRow]);
	ARC_DATA *pArcSrc = pMap->pArcData;
	ARC_ID_RECORD *pSrcArcRecord;
	GB_POINT *ptBegin, *ptEnd, *ptBegin2, *ptEnd2;

	int i;
	
	if(!pArcSrc)
		return;
	pSrcArcRecord = pArcSrc->record;
	for(i=0;i<pArcSrc->header.record_count;++i,++pSrcArcRecord){
		ARC_ID_RECORD_PTR_LIST *pJoinArcs;
		
		if(pSrcArcRecord->flags & AF_NOTFINDFORJOIN)
			continue;
		pSrcArcRecord->flags = AF_NOTFINDFORJOIN;
		
		ptBegin = &(pSrcArcRecord->points[0]);
		ptBegin2 = &(pSrcArcRecord->points[1]);
		ptEnd = &(pSrcArcRecord->points[pSrcArcRecord->point_count-1]);
		ptEnd2 = &(pSrcArcRecord->points[pSrcArcRecord->point_count-2]);
		
		pJoinArcs = (ARC_ID_RECORD_PTR_LIST *)calloc(sizeof(ARC_ID_RECORD_PTR_LIST),1);
		pJoinArcs->pRecord = pSrcArcRecord;
		pJoinArcs->pMap = pMap;
		
		FindJoinArcs(pJoinArcs, ptBegin, ptBegin2, pSrcArcRecord->code, pMatrix, nCol, nRow, FJA_TO_PREV, nCol, nRow);
		FindJoinArcs(pJoinArcs, ptEnd, ptEnd2, pSrcArcRecord->code, pMatrix, nCol, nRow, FJA_TO_NEXT, nCol, nRow);
		*ppJoinArcsList = vl_PushBack(*ppJoinArcsList, (void*)pJoinArcs);
	}
	return;
}

int JoinArcsInMap(ARC_DATA *pArcJoined, ATTR_DATA *pAttrJoined, MAP_MATRIX *pMatrix, int nCol, int nRow, int *nNewID, VC_HASH_TABLE *pIDConv)
{
	char szKeyForIDExTree[32];
	ARC_ID_RECORD *pSrcArcRecord,*pDstArcRecord;
	MAP_NODE *pMap = &(pMatrix->maps[nCol][nRow]);
	ARC_DATA *pArcSrc = pMap->pArcData;
	ATTR_DATA *pAttrSrc = pMap->pAttrData;
	
	GB_POINT *ptBegin,*ptEnd,*ptBegin2,*ptEnd2;
	int i;
	
	if(!pArcSrc)
		return 0;
	pSrcArcRecord = pArcSrc->record;
	for(i=0;i<pArcSrc->header.record_count;++i,++pSrcArcRecord){
		ARC_ID_RECORD_PTR_LIST *pJoinArcList;
		ARC_ID_RECORD_PTR_LIST *pFirstArcList;
		GB_POINT *ptDest;
		GB_POINT *ptLast;
		
		if(pSrcArcRecord->flags & AF_NOTFINDFORJOIN)
			continue;
		pSrcArcRecord->flags = AF_NOTFINDFORJOIN;
		pDstArcRecord = &(pArcJoined->record[pArcJoined->header.record_count++]);

		pDstArcRecord->id = *nNewID;
		
		ptBegin = &(pSrcArcRecord->points[0]);
		ptBegin2 = &(pSrcArcRecord->points[1]);
		ptEnd = &(pSrcArcRecord->points[pSrcArcRecord->point_count-1]);
		ptEnd2 = &(pSrcArcRecord->points[pSrcArcRecord->point_count-2]);

		pJoinArcList = (ARC_ID_RECORD_PTR_LIST *)calloc(sizeof(ARC_ID_RECORD_PTR_LIST),1);
		pJoinArcList->pRecord = pSrcArcRecord;
		pJoinArcList->pMap = pMap;

		FindJoinArcs(pJoinArcList, ptBegin, ptBegin2, pSrcArcRecord->code, pMatrix, nCol, nRow, FJA_TO_PREV, nCol, nRow);
		FindJoinArcs(pJoinArcList, ptEnd, ptEnd2, pSrcArcRecord->code, pMatrix, nCol, nRow, FJA_TO_NEXT, nCol, nRow);
		
		for(;pJoinArcList->pPrev; pJoinArcList = pJoinArcList->pPrev);
		pFirstArcList = pJoinArcList;
		for(; pJoinArcList; pJoinArcList = pJoinArcList->pNext){
			pDstArcRecord->point_count += pJoinArcList->pRecord->point_count;
		}
		pDstArcRecord->points = (GB_POINT*)malloc(pDstArcRecord->point_count*sizeof(GB_POINT));
		
		strcpy(pDstArcRecord->code,pSrcArcRecord->code);
		ptLast = 0;
		ptDest = pDstArcRecord->points;
		for(pJoinArcList=pFirstArcList ; pJoinArcList; pJoinArcList = pJoinArcList->pNext){
			int j;
			GB_POINT *ptSrc;
			ATTR_RECORD *pAttrRecord;
			
			sprintf(szKeyForIDExTree, "%s_%d", pJoinArcList->pMap->szMapName, pJoinArcList->pRecord->id);
			vch_Add(pIDConv, szKeyForIDExTree, (void *)(*nNewID), VCA_COPYKEY);
			
			if(pJoinArcList->pRecord->flags & AF_INVERTFORJOIN){
				ptSrc = &(pJoinArcList->pRecord->points[pJoinArcList->pRecord->point_count-1]);
				for(j=0; j < pJoinArcList->pRecord->point_count; ++j,++ptDest,--ptSrc){
					memcpy(ptDest,ptSrc,sizeof(GB_POINT));
				}
			}
			else{
				ptSrc = pJoinArcList->pRecord->points;
				for(j=0; j < pJoinArcList->pRecord->point_count; ++j,++ptDest,++ptSrc){
					memcpy(ptDest,ptSrc,sizeof(GB_POINT));
				}
			}
			
			pAttrRecord = FindAttrRecordFromAttrDataByID(
				pJoinArcList->pMap->pAttrData,
				pJoinArcList->pRecord->id);

			if(pAttrRecord){
				pAttrRecord->id = *nNewID;
			}
			
		}
		
		for(pJoinArcList=pFirstArcList; pJoinArcList; ){
			ARC_ID_RECORD_PTR_LIST *p = pJoinArcList->pNext;
			free(pJoinArcList);
			pJoinArcList=p;
		}
		
		(*nNewID)++;
	}
	return 0;
}
			

int JoinArcsInMatrix(MAP_MATRIX *pMatrix, ARC_DATA **ppArcJoined, ATTR_DATA **ppAttrJoined){
	int nTotalArcRecord=0;
	int nTotalAttrRecord=0;
	ARC_DATA *pArcJoined;
	ATTR_DATA *pAttrJoined;
	GB_RECT *pDstRect;
	int i,j;
	int nNewID=0;
	int nNewRecordID;
	ARC_ID_RECORD *pDstArcRecord;
	ATTR_RECORD *pDstAttrRecord;

	VC_HASH_TABLE IDConv;
	memset(&IDConv,0,sizeof(VC_HASH_TABLE));
	
	*ppArcJoined = malloc_arc_data();
	pArcJoined = *ppArcJoined;
	*ppAttrJoined = malloc_attr_data();
	pAttrJoined = *ppAttrJoined;
	
	pDstRect = &(pArcJoined->header.r);
	for(i=0;i<pMatrix->nColSize;++i){
		int j;
		for(j=0;j<pMatrix->nRowSize;++j){
			ATTR_DATA *pAttrSrc = (ATTR_DATA*)(pMatrix->maps[i][j].pAttrData);
			ARC_DATA *pArcSrc = (ARC_DATA*)(pMatrix->maps[i][j].pArcData);
			if(!pArcSrc)
				continue;
			if(pDstRect->left == 0 && pDstRect->right == 0){
				memcpy(pDstRect, &(pArcSrc->header.r), sizeof(GB_RECT));
			}
			else{
				MaximizeRect(pDstRect, &(pArcSrc->header.r));
			}
			nTotalArcRecord += pArcSrc->header.record_count;
			nTotalAttrRecord += pAttrSrc->header.record_count;
		}
	}

	pArcJoined->record = (ARC_ID_RECORD *)calloc(sizeof(ARC_ID_RECORD)*nTotalArcRecord,1);
	pAttrJoined->record = (ATTR_RECORD *)calloc(sizeof(ATTR_RECORD)*nTotalAttrRecord,1);
	
	nNewRecordID = 0;
	pDstArcRecord = pArcJoined->record;
	pDstAttrRecord = pAttrJoined->record;
	for(j=0;j<pMatrix->nColSize;++j){
		int k;
		for(k=0;k<pMatrix->nRowSize;++k){
			JoinArcsInMap(pArcJoined, pAttrJoined, pMatrix, j, k, &nNewID, &IDConv);
		}
	}
	vch_Free(&IDConv, VCF_FREEKEY);
	return 0;
}

void FindJoinArcsInMatrix(MAP_MATRIX *pMatrix, V_LIST **ppJoinArcsList){
	int i,j;
	for(i=0; i<pMatrix->nColSize; ++i){
		for(j=0; j<pMatrix->nRowSize; ++j){
			FindJoinArcsInMap(pMatrix, i, j, ppJoinArcsList);
		}
	}
}

void UniqeArcIDInMatrix(MAP_MATRIX *pMatrix){
	char szKeyForIDConv[32];
	int i,j,k;
	int nNewID=1;
	VC_HASH_TABLE IDConv;

	memset(&IDConv,0,sizeof(IDConv));

	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			MAP_NODE *pMap = &(pMatrix->maps[i][j]);
			ARC_DATA *pArc = pMap->pArcData;
			ARC_ID_RECORD *pRecord;
			if(!pArc)
				continue;
			pRecord = pArc->record;
			for(k=0;k<pArc->header.record_count;++k,++pRecord,++nNewID){
				sprintf(szKeyForIDConv, "%s_%d", pMap->szMapName, pRecord->id);
				vch_Add(&IDConv, szKeyForIDConv, (void *)nNewID, VCA_COPYKEY);
				pRecord->id = nNewID;
			}
		}
	}
	
	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			MAP_NODE *pMap = &(pMatrix->maps[i][j]);
			ATTR_DATA *pAttr = pMap->pAttrData;
			ATTR_RECORD *pRecord;
			if(!pAttr)
				continue;
			pRecord = pAttr->record;
			for(k=0;k<pAttr->header.record_count;++k,++pRecord,++nNewID){
				char **ppIDAttr=NULL;
				int nNewID,nOldID;
				nOldID = pRecord->id;
				sprintf(szKeyForIDConv, "%s_%d", pMap->szMapName, nOldID);
				nNewID = (int)vch_Find(&IDConv, szKeyForIDConv);
				pRecord->id = nNewID;
				if(pRecord->attr_count <= 0)
					continue;
				
				if(!strcmp(pRecord->code,"A5121") && (pRecord->attr_count > 1) ){
					ppIDAttr = &(pRecord->pszAttrs[1]);
				}
				
				else if(!strcmp(pRecord->code,"L2112") || !strcmp(pRecord->code,"L2101")){
					ppIDAttr = &(pRecord->pszAttrs[0]);
				}
				if(ppIDAttr){
					char *szNewID;
					sprintf(szKeyForIDConv, "%s_%s", pMap->szMapName, *ppIDAttr);
					nNewID = (int)vch_Find(&IDConv, szKeyForIDConv);
					if(!nNewID) nNewID = -1;
					szNewID = (char*)malloc(16);
					sprintf(szNewID, "%d", nNewID);
					free(*ppIDAttr);
					*ppIDAttr = szNewID;
					ppIDAttr = NULL;
				}
			}
		}
	}
	vch_Free(&IDConv, VCF_FREEKEY);
}


void RemoveArcFromMatrixById(MAP_MATRIX *pMatrix, char *szFilter)
{
	int i,j;
	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			ARC_DATA *pArc = pMatrix->maps[i][j].pArcData;
			if(pArc){
				int k;
				for(k=0;k<pArc->header.record_count;++k){
					if(strncmp(szFilter, pArc->record[k].code, strlen(szFilter))){
						pArc->record[k].flags |= AF_REMOVED;
					}
				}
			}
		}
	}
}

void ArcData2SVGInMatrix(FILE *fp, MAP_MATRIX *pMatrix, char *szColor){
	int i,j;
	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			ARC_DATA *pArc = pMatrix->maps[i][j].pArcData;
			if(pArc)
				ArcData2SVG(fp, pArc, szColor);
		}
	}
}

void AttrData2XMLInMatrix(FILE *fp, MAP_MATRIX *pMatrix){
	int i,j;
	for(i=0;i<pMatrix->nColSize;++i){
		for(j=0;j<pMatrix->nRowSize;++j){
			ATTR_DATA *pAttr = pMatrix->maps[i][j].pAttrData;
			if(pAttr)
				Attr2XML(fp, pAttr);
		}
	}
}

