/**********************************************************************
 
	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 "machine/include.h"
#include "vpf_util.h"
#include "gm2svg.h"
#include "vpf_node.h"
#include "vpf_edge.h"
#include "vpf_face.h"
#include "vpf_ring.h"
#include "data_quality_table.h"
#include "vpf_coverage_attribute_table.h"
#include "vpf_geographic_reference_table.h"
#include "vpf_feature_class_schema_table.h"
#include "vpf_table_buffer.h"
#include "gm_coast_line_lft.h"
#include "gm_road_lft.h"
#include "gm_railroad_lft.h"
#include "gm_political_boundary_aft.h"
#include "gm_tileref_aft.h"
#include "vpf_std.h"
#include "gm_svg_util.h"
#include "gm_convert_env.h"
#include "gm_convert_param.h"
#include "gm_record.h"
#include "gm_oceansea_aft.h"
#include "gm_water_course_lft.h"
#include "gm_inland_water_aft.h"
#include "gm_builtup_area_aft.h"
#include "gm_builtup_area_pft.h"


void gm_find_next_edge_of_face(
	LONG *next_tileid, 
	LONG *next_faceid, 
	VPF_EDGE_RECORD **next_edge, 
	VPF_TABLE **next_edge_table, 
	
	LONG current_tileid,
	LONG current_faceid,
	VPF_EDGE_RECORD *current_edge,
	VPF_TABLE *current_edge_table,
	GM_CONVERT_ENV *env,
	VPF_TABLE_BUFFER *table_buff,
	const char *coverage_path,
	BOOL join_faces,
	BOOL jump_to_near_tile)
{
	char path[MAX_PATH+1];
	GM_TILEREF_AFT_RECORD *tileref;
	LONG next_edgeid;

	if(jump_to_near_tile){ /* current edge is boundary */
		if( current_edge->left_face.cur_id == current_faceid){
			*next_tileid = current_edge->right_edge.tile_id;
			next_edgeid = current_edge->right_edge.ext_id;
			*next_faceid = current_edge->right_face.ext_id;
		}
		else{
			*next_tileid = current_edge->left_edge.tile_id;
			next_edgeid = current_edge->left_edge.ext_id;
			*next_faceid = current_edge->left_face.ext_id;
		}
		tileref = gm_env_get_tile(env, *next_tileid);
		sprintf(path, "%s%s/edg", coverage_path, tileref->tile_name);
		*next_edge_table = vpf_table_buff_get_table(table_buff, path, VTT_EDGE);
		*next_edge = vpf_table_get_edge(*next_edge_table, next_edgeid);

	}
	else{
		*next_tileid = current_tileid;
		*next_faceid = current_faceid;
		*next_edge_table = current_edge_table;
		if(current_edge->left_face.cur_id == current_faceid){
			*next_edge = vpf_table_get_edge(current_edge_table, current_edge->left_edge.cur_id);
		}
		else{
			*next_edge = vpf_table_get_edge(current_edge_table, current_edge->right_edge.cur_id);
		}
	}
}


void gm_write_face_svg(
		GM_SVG_FILE *fp, 
		VPF_MAP *written_faces,
		const char *coverage_path, 
		VPF_TABLE_BUFFER *table_buff, 
		LONG start_tile_id, 
		LONG start_face_id,
		GM_CONVERT_ENV *env,
		BOOL join_faces)
{
char path[MAX_PATH+1];
GM_TILEREF_AFT_RECORD *tile;
VPF_FACE_RECORD *face;
VPF_EDGE_RECORD *edge;
VPF_RING_RECORD *outer_ring;
VPF_TABLE *face_table;
VPF_TABLE *ring_table;
VPF_TABLE *edge_table;
char face_code_name[64];

	// get start tile
	tile = gm_env_get_tile(env, start_tile_id);
	
	// get start face
	sprintf(path, "%s%s/fac", coverage_path, tile->tile_name);
	face_table = vpf_table_buff_get_table(table_buff, path, VTT_FACE);
	face = vpf_table_get_face(face_table, start_face_id);
	
	// get code_name
	gm_svg_make_code_name(face_code_name, tile, start_face_id);
	
	// get outer-ling of first face
	sprintf(path, "%s%s/rng", coverage_path, tile->tile_name);
	ring_table = vpf_table_buff_get_table(table_buff, path, VTT_RING);
	outer_ring = vpf_table_get_ring(ring_table, face->ring_ptr);
	if(!outer_ring){
		printf("error: ring record not found that id=%d in %s\n", (int)face->ring_ptr, path);
		return;
	}

/*	// get inner rings (comment out)
	rings = vpf_list_new(NULL);
	sprintf(path, "%s%s/fac", coverage_path, tile->tile_name);
	face_table = vpf_table_buff_get_table(table_buff, path, VTT_FACE);
	vpf_table_get_rings_by_faceid(rings, face_table, face->id);
	vpf_list_delete(rings);
*/
	
	// get start-edge of outer-ring
	sprintf(path, "%s%s/edg", coverage_path, tile->tile_name);
	edge_table = vpf_table_buff_get_table(table_buff, path, VTT_EDGE);
	edge = vpf_table_get_edge(edge_table, outer_ring->start_edge);
	if(!edge){
		printf("error: edge record not found that id=%d in %s\n", (int)edge->id, path);
		return;
	}
	

	{
		VPF_LIST *edges;

		LONG next_tileid;
		LONG next_faceid;
		VPF_EDGE_RECORD *next_edge;
		VPF_TABLE *next_edge_table;
		LONG current_tileid;
		LONG current_faceid;
		VPF_EDGE_RECORD *current_edge;
		VPF_TABLE *current_edge_table;
		TRIPLET_ID written_face;
		VPF_EDGE_RECORD *first_edge_that_is_boundary;
		LONG start_edgeid=0;
		int max_edges;
		int i;
		max_edges=1000;
		i=0;
		first_edge_that_is_boundary = NULL;

		edges = vpf_list_new(NULL);
		
		if(!vpf_edge_is_boundary(edge)){
			vpf_list_push(edges, edge);
			start_edgeid = edge->id;
		}
		
		if(start_tile_id == 5 && start_face_id == 3){
//			int dbg_kaz=0;
		}

		current_tileid = start_tile_id;
		current_faceid = start_face_id;
		current_edge = edge;
		current_edge_table = edge_table;

		while(1){
			LONG findstart_tileid = current_tileid;
			VPF_EDGE_RECORD *findstart_edge = current_edge;
			BOOL jump_to_near_tile = FALSE;

find_again:
			gm_find_next_edge_of_face(
				&next_tileid,  &next_faceid, &next_edge, &next_edge_table, 
				current_tileid, current_faceid, current_edge, current_edge_table, 
				env, table_buff, coverage_path, join_faces, jump_to_near_tile);

			if(findstart_edge == next_edge && next_tileid == findstart_tileid){
				// next edge that is not boundary not found.
				// use boundary edge
			}
			else{
				current_tileid = next_tileid;
				current_faceid = next_faceid;
				current_edge = next_edge;
				current_edge_table = next_edge_table;
				if(join_faces && vpf_edge_is_boundary(current_edge)){
					if(start_edgeid!=0)
						jump_to_near_tile = TRUE;
					goto find_again;
				}
			}
			
			written_face.cur_id = current_faceid;
			written_face.tile_id = current_tileid;
			
			if(!vpf_map_find(written_faces, &written_face)){
				TRIPLET_ID *id;
				id = vpf_tripletid_new();
				id->cur_id = written_face.cur_id;
				id->tile_id = written_face.tile_id;
				vpf_map_push(written_faces, id, NULL);
			}
			if(join_faces && start_edgeid == 0 && vpf_edge_is_boundary(current_edge)){
				if(first_edge_that_is_boundary==NULL){
					first_edge_that_is_boundary = next_edge;
				}
				else if(first_edge_that_is_boundary==current_edge){
					// aviod infinite loop 
					break;
				}
			}
			else if(start_edgeid==0){
				start_edgeid = current_edge->id;
				start_tile_id = current_tileid;
			}
			else if(current_edge->id == start_edgeid && current_tileid == start_tile_id){
				break;
			}
			if(start_edgeid !=0){
				vpf_list_push(edges, current_edge);
				++i;
				if(i>=max_edges){
					break;
				}
			}
		}
		if(i==max_edges){
			static int n=0;
			if(n==0){
				fprintf(fp->fp, "<g style=\"stroke:#0000FF;stroke-width:0.001;fill:none\">"); 
				gm_write_face_edges_svg(fp, edges, face_code_name);
				fprintf(fp->fp, "</g>"); 
			}
			else if(n==1){
				fprintf(fp->fp, "<g style=\"stroke:#00FF00;stroke-width:0.001;fill:none\">"); 
				gm_write_face_edges_svg(fp, edges, face_code_name);
				fprintf(fp->fp, "</g>"); 
			}
			else if(n==2){
				fprintf(fp->fp, "<g style=\"stroke:#000000;stroke-width:0.001;fill:none\">"); 
				gm_write_face_edges_svg(fp, edges, face_code_name);
				fprintf(fp->fp, "</g>"); 
			}
			++n;
		}
		else{
			gm_write_face_edges_svg(fp, edges, face_code_name);
		}

		vpf_list_delete(edges);
	}
}

static int written_face_comp(const TRIPLET_ID *lhs, const TRIPLET_ID *rhs){
	if(lhs->tile_id > rhs->tile_id)
		return 1;
	if(lhs->tile_id < rhs->tile_id)
		return -1;
	if(lhs->cur_id > rhs->cur_id)
		return 1;
	if(lhs->cur_id < rhs->cur_id)
		return -1;
	return 0;
}

static void written_face_destructor(TRIPLET_ID *lhs, void *dummy){
	vpf_tripletid_delete(lhs);
}

static BOOL is_already_written(VPF_MAP *written, LONG tileid, LONG id){
	TRIPLET_ID t;
	t.tile_id = tileid;
	t.cur_id = id;
	return (vpf_map_find(written, &t)!=NULL);
}

BOOL gm_convert(const GM_CONVERT_PARAM *param, GM_CONVERT_ENV *env){
	BOOL ret;
	char path[MAX_PATH+1];
	VPF_TABLE *feature_table=NULL;
	VPF_LIST_ITEM *it;
	VPF_TABLE_BUFFER *table_buff=NULL;
	VPF_TABLE *primitive_table;
	GM_TILEREF_AFT_RECORD *tile;
	GM_SVG_FILE *fp;
	LONG primitive_id;
	char code_name[64];
	VPF_MAP *written_faces=NULL;
	TRIPLET_ID *written_face;
	
	ret = FALSE;
	fp = NULL;
	
	table_buff = vpf_table_buff_new(20);
	feature_table = vpf_table_new(param->feature_table_type);
	sprintf(path, "%s%s", param->coverage_path, param->feature_table_name);

	if(!vpf_table_load(feature_table, path)){
		printf("error: loading feature table %s\n", path);
		goto exit;
	}
	
	fp = gm_svg_write_begin(param->output_filepath, param->feature_table_type, env->color_table);
	if(!fp){
		goto exit;
	}

	written_faces=vpf_map_new((vpf_map_data_destructor_type)written_face_destructor, (vpf_map_comp_type)written_face_comp);
	
	for(it = feature_table->records->head; it; it=it->next){
		tile = vpf_tilelef_get_tile(env->tileref_table, param->get_tile_id_func(it->data));
		primitive_id = param->get_primitive_id_func(it->data);

		gm_svg_make_code_name(code_name, tile, primitive_id);
		
		switch(param->primitive_table_type){
			case VTT_NODE:
				sprintf(path, "%s%s/end", param->coverage_path, tile->tile_name);
				break;
			case VTT_EDGE:
				sprintf(path, "%s%s/edg", param->coverage_path, tile->tile_name);
				break;
			case VTT_FACE:
				sprintf(path, "%s%s/fac", param->coverage_path, tile->tile_name);
				break;
			default: ;
		}
		
		primitive_table = vpf_table_buff_get_table(table_buff, path, param->primitive_table_type);
		
		switch(primitive_table->type){
			case VTT_NODE:
				{
					VPF_NODE_RECORD *node;
					node = vpf_table_get_node(primitive_table, primitive_id);
					gm_write_node_svg(fp, node, code_name);
				}
				break; 

			case VTT_EDGE:
				{
					VPF_EDGE_RECORD *edge;
					edge = vpf_table_get_edge(primitive_table, primitive_id);
					gm_write_edge_svg(fp, edge, code_name);
				}
				break;

			case VTT_FACE:
				{
					if(is_already_written(written_faces, tile->id, primitive_id)){
						continue;
					}
					
					written_face = vpf_tripletid_new();
					written_face->tile_id = tile->id;
					written_face->cur_id = primitive_id;
					vpf_map_push(written_faces, written_face, NULL);
					
					// if(X\J\A\F_1051
					if(primitive_id == 1051){
//						int db=0;
					}
					gm_write_face_svg(fp, 
						written_faces,
						param->coverage_path, 
						table_buff,
						tile->id, 
						primitive_id,
						env,
						param->join_faces);
				}
				break;
			default: ;
		}

		//write information 
		if(param->write_information_func){
			fprintf(fp->fp, "<information name=\"%s\" scheme=\"Global Map Version1.1\">\n", code_name);
			param->write_information_func(fp->fp, it->data);
			fprintf(fp->fp, "</information>\n");
		}
	}
	
	ret = TRUE;
exit:
	gm_svg_write_end(fp);
	vpf_map_delete(written_faces);
	vpf_table_delete(feature_table);
	vpf_table_buff_delete(table_buff);
	return ret;
}

int gm_coverage2svg(const char *output_path, const char *coverage_name, GM_CONVERT_ENV *env){
GM_CONVERT_PARAM param;

	sprintf(param.coverage_path, "%s%s/", env->library_directory, coverage_name);
	
	if(strcmp("bnd",coverage_name)==0){

		// political_boundary
		sprintf(param.output_filepath, "%spolitical_boundary.svg", output_path); 
		param.feature_table_type = GMTT_POLITICAL_BOUNDARY_AFT;
		strcpy(param.feature_table_name,"polbnda.aft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_political_boundary_aft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_political_boundary_aft_get_fac_id;
		param.write_information_func = (write_information_func_type)gm_political_boundary_aft_write_information_tag;
		param.primitive_table_type = VTT_FACE;
		param.join_faces = TRUE;
		gm_convert(&param, env);
		
		// coast line 
		sprintf(param.output_filepath, "%scoast_line.svg", output_path); 
		param.feature_table_type = GMTT_COAST_LINE_LFT;
		strcpy(param.feature_table_name,"coastl.lft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_coast_line_lft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_coast_line_lft_get_edge_id;
		param.write_information_func = (write_information_func_type)gm_coast_line_lft_write_information_tag;
		param.primitive_table_type = VTT_EDGE;
		gm_convert(&param, env);
		
		// oseansea
		sprintf(param.output_filepath, "%soceansea.svg", output_path); 
		param.feature_table_type = GMTT_OCEANSEA_AFT;
		strcpy(param.feature_table_name,"oceansea.aft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_oceansea_aft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_oceansea_aft_get_fac_id;
		param.write_information_func = (write_information_func_type)gm_oceansea_aft_write_information_tag;
		param.primitive_table_type = VTT_FACE;
		param.join_faces = FALSE;
		gm_convert(&param, env);

		
	}
	else if(strcmp("hydro",coverage_name)==0){
		// water course
		sprintf(param.output_filepath, "%swater_course.svg", output_path); 
		param.feature_table_type = GMTT_WATER_COURSE_LFT;
		strcpy(param.feature_table_name,"watrcrsl.lft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_water_course_lft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_water_course_lft_get_edge_id;
		param.write_information_func = (write_information_func_type)gm_water_course_lft_write_information_tag;
		param.primitive_table_type = VTT_EDGE;
		gm_convert(&param, env);

		// inland water
		sprintf(param.output_filepath, "%sinland_water.svg", output_path); 
		param.feature_table_type = GMTT_INLAND_WATER_AFT;
		strcpy(param.feature_table_name,"inwatera.aft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_inland_water_aft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_inland_water_aft_get_fac_id;
		param.write_information_func = (write_information_func_type)gm_inland_water_aft_write_information_tag;
		param.primitive_table_type = VTT_FACE;
		param.join_faces = TRUE;
		gm_convert(&param, env);

	}
	else if(strcmp("pop",coverage_name)==0){
		// builtup area (point)
		sprintf(param.output_filepath, "%sbuiltup_areap.svg", output_path); 
		param.feature_table_type = GMTT_BUILTUP_AREA_PFT;
		strcpy(param.feature_table_name,"builtupp.pft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_builtup_area_pft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_builtup_area_pft_get_end_id;
		param.write_information_func = (write_information_func_type)gm_builtup_area_pft_write_information_tag;
		param.primitive_table_type = VTT_NODE;
		gm_convert(&param, env);

		// builtup area (area)
		sprintf(param.output_filepath, "%sbuiltup_areaa.svg", output_path); 
		param.feature_table_type = GMTT_BUILTUP_AREA_AFT;
		strcpy(param.feature_table_name,"builtupa.aft");
		param.get_tile_id_func = (get_tile_id_func_type)gm_builtup_area_aft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_builtup_area_aft_get_fac_id;
		param.write_information_func = (write_information_func_type)gm_builtup_area_aft_write_information_tag;
		param.primitive_table_type = VTT_FACE;
		param.join_faces = TRUE;
		gm_convert(&param, env);

	}
	else if(strcmp("raster",coverage_name)==0){
		
	}
	else if(strcmp("tileref",coverage_name)==0){
		
	}
	else if(strcmp("trans",coverage_name)==0){
		// road
		sprintf(param.output_filepath, "%sroad.svg", output_path); 
		param.feature_table_type = GMTT_ROAD_LFT;
		strcpy(param.feature_table_name,"roadl.lft");
		
		param.get_tile_id_func = (get_tile_id_func_type)gm_road_lft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_road_lft_get_edge_id;
		param.write_information_func = (write_information_func_type)gm_road_lft_write_information_tag;
		param.primitive_table_type = VTT_EDGE;
		gm_convert(&param, env);
		
		//railroad
		sprintf(param.output_filepath, "%srailroad.svg", output_path); 
		param.feature_table_type = GMTT_RAILROAD_LFT;
		strcpy(param.feature_table_name,"railrdl.lft");
		
		param.get_tile_id_func = (get_tile_id_func_type)gm_railroad_lft_get_tileid;
		param.get_primitive_id_func = (get_primitive_id_func_type)gm_railroad_lft_get_edge_id;
		param.write_information_func = (write_information_func_type)gm_railroad_lft_write_information_tag;
		param.primitive_table_type = VTT_EDGE;
		gm_convert(&param, env);
	}

	return TRUE;
}

/*
static COVERAGE_ATTRIBUTE_RECORD *get_tileref_coverage_record(VPF_TABLE *tbl){
VPF_LIST_ITEM *it;
COVERAGE_ATTRIBUTE_RECORD *car;

	for(it = tbl->records->head; it; it=it->next){
		car = (COVERAGE_ATTRIBUTE_RECORD*)(it->data);
		vpf_trim(car->coverage_name);
		if(strcmp(car->coverage_name, "tileref")==0){
			return car;
		}
	}
	return NULL;
}
*/

int gm2svg(const char *output_path, const char *vpf_library_directory, const char *configure_file){
	GM_CONVERT_ENV *env;
	COVERAGE_ATTRIBUTE_RECORD *car;
VPF_LIST_ITEM *it;

	env = gm_convert_env_new(vpf_library_directory, configure_file);
	if(!env){
		printf("error: gm_convert_env initialize failed.\n");
		return FALSE;
	}

	// foreach coverages
	for(it = env->coverage_attribute_table->records->head; it; it=it->next){
		car = (COVERAGE_ATTRIBUTE_RECORD*)(it->data);
		if(strcmp(car->coverage_name, "tileref")==0){
			// skip tile ref coverage
			continue;
		}
		vpf_trim(car->coverage_name);
		gm_coverage2svg(output_path, car->coverage_name, env);
	}

	gm_convert_env_delete(env);

	return TRUE;
}

