/**********************************************************************
 
	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 <stdlib.h>
#include <math.h>

#include "xlerror.h"
#include "xl.h"
#include "svg2gb.h"
#include "xl2pdb_p.h"
#include "bezier2points.h"

#define MAX_BEZIER_PIONT_BUFF_COUNT 1024

double data_input_resolution;

int max_count;

void svg_add_point(SVG2GB_CONTEXT *ctx, SVG_POINT point,int flags)
{


	if(ctx->point_buff==NULL){
		ctx->point_buff_size = 256;
		ctx->point_buff = (SVG_POINT_LIST*)
			malloc(sizeof(SVG_POINT_LIST)*
			ctx->point_buff_size);
	}
	else if(ctx->point_count == ctx->point_buff_size ){
		ctx->point_buff_size *= 2;
		ctx->point_buff = (SVG_POINT_LIST*)
			realloc(ctx->point_buff, 
			sizeof(SVG_POINT_LIST)*(ctx->point_buff_size));
	}
	if ( ctx->point_count )
		ctx->point_buff[ctx->point_count-1].flags = flags;
	ctx->point_buff[ctx->point_count].p = point;
	ctx->point_buff[ctx->point_count++].flags = 0;


}

SVG_FLOAT svg_read_float(SVG2GB_CONTEXT * ctx,char **pp){
char * q1;
int digit;
	SVG_FLOAT f = (SVG_FLOAT)atof(*pp);
	*pp += 1;
	*pp += strspn(*pp, "0123456789.");
	if(**pp==' '||**pp==',')
		++(*pp);
	q1 = *pp + 1;
	q1 += strspn(q1,"123456789");
	switch ( *q1 ) {
	case '.':
		q1 ++;
		digit = -strspn(q1,"1234567890");
		break;
	case '0':
		digit = strspn(q1,"0");
		break;
	default:
		digit = 0;
	}
	if ( digit < ctx->digit )
		ctx->digit = digit;
	return f;
}

SVG_POINT svg_read_point(SVG2GB_CONTEXT * ctx,char **pp){
	SVG_POINT point;
	point.x = svg_read_float(ctx,pp);
	point.y = svg_read_float(ctx,pp);
	return point;
}

/* apply transform to point. (rotate and skew not supprted) */
void svg_transform_point(SVG2GB_CONTEXT *ctx, SVG_POINT_LIST *point){
SVG_LAYER *layer;
SVG_TRANSFORM_INFO *tr;
double reso;
int i;

	if ( ctx->digit > 0 ) {
		reso = 1;
		for ( i = 0 ; i < ctx->digit ; i ++ )
			reso *= 10;
	}
	else if ( ctx->digit < 0 ) {
		reso = 1;
		for ( i = 0 ; i > ctx->digit ; i -- )
			reso /= 10;
	}
	else {
		reso = 1;
	}
	for(layer=ctx->current_layer; layer; layer=layer->parent){
		for(tr=layer->transform; tr; tr=tr->next){
			point->p.x += tr->x_translate;
			point->p.y += tr->y_translate;
			point->p.x *= tr->x_scale;
			point->p.y *= tr->y_scale;

			reso *= (tr->x_scale +
				tr->y_scale)/2;
		}
	}
	if ( data_input_resolution == 0 )
		data_input_resolution = reso;
	else if ( data_input_resolution > reso )
		data_input_resolution = reso;
}

/* execute transform to point_buffer */
void svg_exec_transform(SVG2GB_CONTEXT *ctx){
	int i=0;
	for(;i<ctx->point_count;++i){
		svg_transform_point(ctx, &(ctx->point_buff[i]));
	}
}

/* call on_polygon-callback and reset point_count */
void svg_flush_points(SVG2GB_CONTEXT *ctx, int closeFlag){
	if(ctx->point_count > 0){
		svg_exec_transform(ctx);
		svg2gb_on_convert_polygon(ctx, closeFlag);
		ctx->point_count = 0;
	}
}

int svg_get_bezier_point_count(SVG_POINT *from, SVG_POINT *to){
	int cnt;
	cnt = (int)(hypot(from->x-to->x, from->y-to->y)/4);
	if(cnt < 10){
		return 10;
	}
	else if(cnt<MAX_BEZIER_PIONT_BUFF_COUNT){
		return cnt;
	}
	else{
		return MAX_BEZIER_PIONT_BUFF_COUNT;
	}
}

void svg_parse_point_data(L_CHAR *d)
{
#define F_2D_SPLINE 0x01
#define F_3D_SPLINE 0x02

SVG2GB_CONTEXT *ctx;
char *p;
char *buff;
char command;
int last_point_flag=0;

SVG_POINT current_point;
SVG_POINT tmp_point;
SVG_POINT point_buff[MAX_BEZIER_PIONT_BUFF_COUNT];
SVG_POINT src_point_buff[4];
SVG_POINT moveto_point;
int i;
int point_count;

int start_point_flag,z_ditect;

	ctx = svg2gb_get_context();
	buff = n_string(std_cm, d);	
	p = buff;


	start_point_flag = 1;
	z_ditect = 0;

	for( ; *p ; start_point_flag = 0 ){

		command = *p++;
		while(*p && (*p==' ' || *p=='\r' || *p=='\n') )
			++p;
		switch(command){
		case 'M':

			current_point = svg_read_point(ctx,&p);
			moveto_point = current_point;
			svg_add_point(ctx, current_point,PP_LMOVE);
			last_point_flag = 0;
			break;
		case 'm':

			tmp_point = svg_read_point(ctx,&p);
			current_point.x += tmp_point.x;
			current_point.y += tmp_point.y;
			moveto_point = current_point;

			svg_add_point(ctx,current_point,PP_LMOVE);
			last_point_flag = 0;
			break;
		case 'L':
			current_point = svg_read_point(ctx,&p);
			if ( start_point_flag )
				moveto_point = current_point;
			svg_add_point(ctx, current_point,0);
			last_point_flag = 0;
			break;
		case 'l':
			tmp_point = svg_read_point(ctx,&p);
			current_point.x += tmp_point.x;
			current_point.y += tmp_point.y;
			svg_add_point(ctx,current_point,0);
			last_point_flag = 0;
			break;
		case 'V':
			current_point.y = svg_read_float(ctx,&p);
			svg_add_point(ctx,current_point,0);
			last_point_flag = 0;
			break;
		case 'v':
			current_point.y += svg_read_float(ctx,&p);
			svg_add_point(ctx,current_point,0);
			last_point_flag = 0;
			break;
		case 'H':
			current_point.x = svg_read_float(ctx,&p);
			svg_add_point(ctx, current_point,0);
			last_point_flag = 0;
			break;
		case 'h':
			current_point.x += svg_read_float(ctx,&p);
			svg_add_point(ctx, current_point,0);
			last_point_flag = 0;
			break;
		case 'C':
			{
				src_point_buff[0] = current_point;
				src_point_buff[1] = 
					svg_read_point(ctx,&p);
				src_point_buff[2] = 
					svg_read_point(ctx,&p);
				src_point_buff[3] = 
					svg_read_point(ctx,&p);

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[3]);
				bezier2points(point_buff, point_count, src_point_buff, 3);
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx,
						point_buff[i],
						0);
				}

				current_point = src_point_buff[3];
				
				last_point_flag = F_3D_SPLINE;
			}
			break;
		case 'c':
			{
				src_point_buff[0] = current_point;
				src_point_buff[1] = 
					svg_read_point(ctx,&p);
				src_point_buff[2] = 
					svg_read_point(ctx,&p);
				src_point_buff[3] = 
					svg_read_point(ctx,&p);
				
				for(i=1; i<4; ++i){
					src_point_buff[i].x += current_point.x;
					src_point_buff[i].y += current_point.y;
				}

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[3]);
				bezier2points(point_buff, point_count, src_point_buff, 3);
				
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, 
						point_buff[i],
						0);
				}

				current_point = src_point_buff[3];
				
				last_point_flag = F_3D_SPLINE;
			}
			break;
		case 'S':
			{
				src_point_buff[0] = current_point;
				if(last_point_flag & F_3D_SPLINE){
					src_point_buff[1].x = src_point_buff[3].x + src_point_buff[3].x-src_point_buff[2].x;
					src_point_buff[1].y = src_point_buff[3].y + src_point_buff[3].y-src_point_buff[2].y;
				}
				else{
					src_point_buff[1] = current_point;
				}
				src_point_buff[2] = 
					svg_read_point(ctx,&p);
				src_point_buff[3] = 
					svg_read_point(ctx,&p);

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[3]);
				bezier2points(point_buff, point_count, src_point_buff, 3);
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, 
						point_buff[i],0);
				}

				current_point = src_point_buff[3];
				last_point_flag = F_3D_SPLINE;
			}
			break;
		case 's':
			{
				src_point_buff[0] = current_point;
				if(last_point_flag & F_3D_SPLINE){
					src_point_buff[1].x = src_point_buff[3].x + src_point_buff[3].x-src_point_buff[2].x;
					src_point_buff[1].y = src_point_buff[3].y + src_point_buff[3].y-src_point_buff[2].y;
				}
				else{
					src_point_buff[1] = current_point;
				}
				tmp_point = svg_read_point(ctx,&p);
				src_point_buff[2].x = current_point.x + tmp_point.x;
				src_point_buff[2].y = current_point.y + tmp_point.y;
				tmp_point = svg_read_point(ctx,&p);
				src_point_buff[3].x = current_point.x + tmp_point.x;
				src_point_buff[3].y = current_point.y + tmp_point.y;

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[3]);
				bezier2points(point_buff, point_count, src_point_buff, 3);
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, 
						point_buff[i],0);
				}

				current_point = src_point_buff[3];
				last_point_flag = F_3D_SPLINE;
			}
			break;
		case 'Q':
			{
				src_point_buff[0] = current_point;
				src_point_buff[1] = 
					svg_read_point(ctx,&p);
				src_point_buff[2] = 
					svg_read_point(ctx,&p);

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[2]);
				bezier2points(point_buff, point_count, src_point_buff, 2);
				
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, 
						point_buff[i],0);
				}

				current_point = src_point_buff[2];
				
				last_point_flag = F_2D_SPLINE;
			}
			break;
		case 'q':
			{
				src_point_buff[0] = current_point;
				src_point_buff[1] = 
					svg_read_point(ctx,&p);
				src_point_buff[2] = 
					svg_read_point(ctx,&p);
				
				for(i=1; i<3; ++i){
					src_point_buff[i].x += current_point.x;
					src_point_buff[i].y += current_point.y;
				}

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[2]);
				bezier2points(point_buff, point_count, src_point_buff, 2);
				
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, 
						point_buff[i],0);
				}

				current_point = src_point_buff[2];
				
				last_point_flag = F_2D_SPLINE;
			}
			break;
		case 'T':
			{
				src_point_buff[0] = current_point;
				if(last_point_flag & F_2D_SPLINE){
					src_point_buff[1].x = src_point_buff[2].x + src_point_buff[2].x-src_point_buff[1].x;
					src_point_buff[1].y = src_point_buff[2].y + src_point_buff[2].y-src_point_buff[1].y;
				}
				else{
					src_point_buff[1] = current_point;
				}
				src_point_buff[2] = 
					svg_read_point(ctx,&p);

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[2]);
				bezier2points(point_buff, point_count, src_point_buff, 2);
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, point_buff[i],
						0);
				}

				current_point = src_point_buff[2];
				last_point_flag = F_2D_SPLINE;
			}
			break;
		case 't':
			{
				src_point_buff[0] = current_point;
				if(last_point_flag & F_2D_SPLINE){
					src_point_buff[1].x = src_point_buff[2].x + src_point_buff[2].x-src_point_buff[1].x;
					src_point_buff[1].y = src_point_buff[2].y + src_point_buff[2].y-src_point_buff[1].y;
				}
				else{
					src_point_buff[1] = current_point;
				}
				
				tmp_point = svg_read_point(ctx,&p);
				src_point_buff[2].x = current_point.x + tmp_point.x;
				src_point_buff[2].y = current_point.y + tmp_point.y;

				point_count = svg_get_bezier_point_count(&src_point_buff[0], &src_point_buff[2]);
				bezier2points(point_buff, point_count, src_point_buff, 2);
				for(i=0; i<point_count; ++i){
					svg_add_point(ctx, point_buff[i],
						0);
				}

				current_point = src_point_buff[2];
				last_point_flag = F_2D_SPLINE;

			}
			break;

		case 'z':
			{
				/* debug
				{
					FILE *fp;
					static int cnt=0;
					char fn[256];
					++cnt;
					sprintf(fn, "%08d.txt", cnt);
					fp = fopen(fn, "wt");
					fprintf(fp, "<path d=\"");
					
					fprintf(fp, "M%.3f,%.3f", ctx->point_buff[0].x, ctx->point_buff[0].y);
					for(i=1; i<ctx->point_count; ++i){
						fprintf(fp, "L%.3f,%.3f", ctx->point_buff[i].x, ctx->point_buff[i].y);
					}
					fprintf(fp, "\" />");
					fclose(fp);
				}
				*/

				if ( ctx->point_buff
					[ctx->point_count-1].p.x
					!= moveto_point.x ||
					ctx->point_buff
					[ctx->point_count-1].p.y
					!= moveto_point.y )
					svg_add_point(ctx,moveto_point,0);


				last_point_flag = 0;
				z_ditect = 1;
			}
			break;
		}
	}

	if ( z_ditect &&
		(ctx->point_buff[0].p.x
			!= ctx->point_buff[ctx->point_count-1].p.x
			||
		ctx->point_buff[0].p.y
			!= ctx->point_buff[ctx->point_count-1].p.y ) )
		svg_add_point(ctx,ctx->point_buff[0].p,PP_LMOVE);

	svg_flush_points(ctx,1);
}

void svg2gb_add_polygon_from_path_data(SVG_PATH_DATA *data){
SVG2GB_CONTEXT *ctx;
static L_CHAR *const_fill;
static L_CHAR *const_stroke;
L_CHAR *fill;
L_CHAR *stroke;
SVG_LAYER *layer;

	const_fill = nl_copy_str(std_cm, "fill");
	const_stroke = nl_copy_str(std_cm, "stroke");
	
	ctx = svg2gb_get_context();
	fill = svg_find_style(data->style, const_fill);
/*
	if(!fill)
		fill = svg_find_style(ctx->current_layer->styles, const_fill);
*/
	layer = ctx->current_layer;
	while( (!fill) && layer){
		fill = svg_find_style(layer->styles, const_fill);
		layer = layer->parent;
	}
	stroke = svg_find_style(data->style, const_stroke);
	layer = ctx->current_layer;
	while( (!stroke) && layer){
		stroke = svg_find_style(layer->styles, const_stroke);
		layer = layer->parent;
	}
	
	if(fill == NULL){
		fill = l_string(std_cm, "#000000");
	}
	if(stroke == NULL){
		stroke = l_string(std_cm, "#FFFFFF");
	}

	ctx->current_fill_color = str2svg_color(n_string(std_cm, fill));
	ctx->current_stroke_color = str2svg_color(n_string(std_cm, stroke));
	ctx->current_id = data->id;
	
	svg_parse_point_data(data->d);
}



XL_SEXP * xl_path();

void
init_path(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"path"),
		get_func_prim(xl_path,FO_NORMAL,0,1,-1));
}

int path_cnt;


XL_SEXP *
xl_path(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * _sf)
{
XL_SEXP *ss;
L_CHAR *const_d;
L_CHAR *const_id;
L_CHAR *const_style;
SVG2GB_CONTEXT *ctx=NULL;
SVG_PATH_DATA data;
int now;

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

if ( path_cnt == 0 )
ss_printf("path %i      \r",s->h.line);
path_cnt ++;
if ( path_cnt > 200 )
path_cnt = 0;


	const_d = l_string(std_cm, "d");
	const_id = l_string(std_cm, "id");
	const_style = l_string(std_cm, "style");

	{
		XL_SYM_FIELD *sf;
		for( sf = _sf; sf; sf=sf->next){
			if(l_strcmp(sf->name, const_d) == 0){
				data.d = sf->data;
			}
			else if(l_strcmp(sf->name, const_id) == 0){
				data.id = sf->data;
			}
			else if(l_strcmp(sf->name, const_style) == 0){
				data.style = svg_str2style(sf->data);
			}
		}
	}
	
	svg2gb_add_polygon_from_path_data(&data);
	
	if(data.style)
		svg_free_style(data.style);

	return 0;
}
