/*
* svg2poly.ulp
* Feb 2012, By Cruz Monrreal II (Cruz.Monrreal@gmail.com)
*
* Imports select .svg file and will draw it as a set of polygons
* For best results, follow Inkscape instructions as closely as
* possible.
*
*
* NOTE: This is not a universal .svg converter. The spec is
*       simply too large, and the .ulp language is not robust
*       enough to create an entire .svg parser.
*
*/

#usage "<b>Convert a modified SVG file to a Polygon</b><p>\n"
"       run svg2poly -ratio num\n"
"       run svg2poly -ratiox numx -ratioy numy -layer layername -outline thickness\n<p>"
"       The polygons will automatically be centered about the current 'mark'</p>\n"
"		For instructions on how to prepare the SVG file, go to www.thinkmaketest.com\\nn"
"<author>Author: Cruz Monrreal II\n(cruz.monrreal@gmail.com)\n1-Feb-2011</author>"

/* Inputs, Defaults */
string layer = "tDocu";
int outline = 0;
real ratioX = 1.0, ratioY = 1.0;


/* Globals */
string tmp[], master_xml;
int poly_start[], num_pts=0, num_polys=0;
real pts_x[], pts_y[];
real tmp_coords[];

/* Output */
string cmd;

/*
* Stack imlemented to get around lack of recursion
*/
string stack = "";

/*** Stack Helper Functions ***/
void push(string s){ stack = (stack == "") ? s : stack + "|" + s; }
string pull()
{
	string tmp;
	
	if (stack == "")
        // Nothing in stack
		return "";
	else if (strrstr(stack, "|") == -1){
        // Stack only has one element
		tmp = stack;
		stack = "";
		return tmp;
	}

    // Search for the last `|` in string
    // Return string past last `|`
	tmp = strsub(stack, strrstr(stack, "|") + 1);
	stack = strsub(stack, 0, strrstr(stack, "|"));
	return tmp;
}

/*
*  Encodes all necessary information into a single string
*  for stack-based recursion
*/
string pack(string xml, int repush, int num_pts)
{
	string tmp;
	sprintf(tmp, "%s^%d^%d", xml, repush, num_pts);
	return tmp;
}

/*** Functions ***/
void print(string s) {printf("%s\n", s);}

/*
*  Sets up the eagle commands for the script.
*/
void setup_cmd()
{	
    int i;
    
    // Search for parameters
    for(i=0; i<argc; i++){
        if (argv[i] == "-ratio"){   // Scale SVG in the X and Y Axis
            if (i+1 < argc){        // Make sure that there's another argument
                if (strstr(argv[i+1], "-") == -1){    // And that it's not a command
                    ratioX = ratioY = strtod(argv[i+1]);
                    i++;
                }
            }
        }else if (argv[i] == "-ratiox"){    //Scale SVG in X Axis
            if (i+1 < argc){
                if (strstr(argv[i+1], "-") == -1)                {
                    ratioX = strtod(argv[i+1]);
                    i++;
                }
            }
        }else if (argv[i] == "-ratioy"){    // Scale SVG in Y Axis
            if (i+1 < argc){
                if (strstr(argv[i+1], "-") == -1){
                    ratioY = strtod(argv[i+1]);
                    i++;
                }
            }
        }else if (argv[i] == "-layer"){     // Change layer that SVG is drawn on
            if (i+1 < argc){
                if (strstr(argv[i+1], "-") == -1){
                    layer = argv[i+1];
                    i++;
                }
            }
        }else if (argv[i] == "-outline"){   // Draw an outline of a given size
            if (i+1 < argc){
                if (strstr(argv[i+1], "-") == -1){
                    outline = strtol(argv[i+1]);
                    i++;
                }
            }
        }
    }
    
	// Arguments passed to replace defaults
	/*switch (argc){
		case 5:
			outline = strtol(argv[4]);
		case 4:
			layer = argv[3];
		case 3:
			ratioX = strtod(argv[2]);
			ratioY = strtod(argv[1]);
			break;
		case 2:
			ratioX = ratioY = strtod(argv[1]);
	}*/
	
	// Write it all to the eagle command
	sprintf(cmd, "set wire_bend 2;\nchange layer %s;\nset width %d;\npoly", layer, outline);
}



string matrix_to_translate(string translate){
	// Might need to implement sin & cos...
	
	
	
	
	return translate;
}

/*
*  Converts svg formatted coordinates to usable peices
*  WARNING: Function assumes string is formatted correctly
*/
int svgcoords_to_coords(string s)
{
	string coords[];
	strsplit(coords, s, ',');
	tmp_coords[0] = strtod(coords[0]);
	tmp_coords[1] = strtod(coords[1]);
	
	// Lower the point resolution
	sprintf(s, "%2.2f,%2.2f", tmp_coords[0], tmp_coords[1]);
	strsplit(coords, s, ',');
	tmp_coords[0] = strtod(coords[0]);
	tmp_coords[1] = strtod(coords[1]);
	
	return 0;
}

/*
*  Extract points from a given svg path.
*  ASSUMES: User followed instructions on site
*     File was saved as Plain SVG & NO Relative Moves allowed
*/
void extract_pts(string pts, string transform)
{
	string svg_cmds[];
	int num = strsplit(svg_cmds, pts, ' '),
		new_pts = 0;
	int i = 3;
	
	// Path format:
	//  M x,y L x,y L x,y L x,y ....
	poly_start[num_pts] = 1;
	svgcoords_to_coords(svg_cmds[1]);
	pts_x[num_pts] = tmp_coords[0];
	pts_y[num_pts++] = tmp_coords[1];
	new_pts++;
	
	printf("%2d) %2.2f,%2.2f\n", num_polys, tmp_coords[0], tmp_coords[1]);
	
	// Assume that the only commands left are 'L'
	for(; i<num; i+=2){
		svgcoords_to_coords(svg_cmds[i]);
		
		if (tmp_coords[0] != pts_x[num_pts-1] || tmp_coords[1] != pts_y[num_pts-1]){	
			poly_start[num_pts] = 0;
			pts_x[num_pts] = tmp_coords[0];
			pts_y[num_pts] = tmp_coords[1];
			
			//if (transform == "")
				printf("    %2.2f,%2.2f\n", pts_x[num_pts], pts_y[num_pts]);
			poly_start[num_pts++] = 0;
			new_pts++;
		}
	}
	
	// Apply transform if needed
	if (transform != ""){
		//print(" Local transform: " + transform);
		if (strstr(transform, "translate") != -1){
			int a = strchr(transform, '('),
				b = strchr(transform, ')', a+1);
			int offset = num_pts - new_pts;

			// Extract translation offsets
			svgcoords_to_coords(strsub(transform, a+1, b-a-1));

			// Translate points
			for(int i=0; i < num/2; i++){
				pts_x[i + offset] += tmp_coords[0];
				pts_y[i + offset] += tmp_coords[1];
				//printf("%f, %f\n", pts_x[i + offset], pts_y[i + offset]);
			}
		}else if (strstr(transform, "matrix") != -1){
			// Do Matrix math (yay...)
			/* REMEMBER: Inkscape y axis is inverted...*/
			print(" TODO: Implement Matrix Transform");
		}else
			print("Unknown path transformation:\n " + transform);
	}
	
	//print("");
	num_polys++;
}

/*
*  Traverse through the tree of groups, adding points to paths,
*  while applying group transforms in the process
*/
void extract_nested_data(string xml)
{
	string ops[];
	string children[];
	int size;
	
	push(pack(xml, 0, num_pts));
	
	while(stack != ""){
		strsplit(ops, pull(), '^');
		
		// Find # of children
		size = xmlelements(children, ops[0], "g/g");
		
		if (size != 0 && ops[1] == "0"){
			// Repush
			push(ops[0] + "^1^" + ops[2]);
			
			// Reverse push children
			for(int i=size-1; i>=0; i--)
				push(pack(children[i], 0, num_pts));
		}else{
			// Search for and add points
			print("Searching group " + xmlattribute(ops[0], "g", "id"));
			
			string paths[];
			int num_paths = xmlelements(paths, ops[0], "g/path");
			
			for(int i=0; i<num_paths; i++){
				print(" Found Path " + xmlattribute(paths[i], "path", "id"));
				
				// Extract points from each line/path
				extract_pts(xmlattribute(paths[i], "path", "d"), xmlattribute(paths[i], "path", "transform"));
			}
			
			// Find and apply group transform
			string transform = xmlattribute(ops[0], "g", "transform");
			if (transform != ""){
				// Figure out where to start the transform
				int offset = 0;
				
				if (ops[1] != "0")
					offset = strtol(ops[2]);
				
				print(" Found transform " + transform);
				
				if (strstr(transform, "translate") != -1){
					int a = strchr(transform, '('),
						b = strchr(transform, ')', a+1);

					// Extract translation offsets
					svgcoords_to_coords(strsub(transform, a+1, b-a-1));

					// Translate points
					for(int i=offset; i < num_pts; i++){
						pts_x[i] += tmp_coords[0];
						pts_y[i] += tmp_coords[1];
						//printf("%f, %f\n", pts_x[i + offset], pts_y[i + offset]);
					}
				}else if (strstr(transform, "matrix") != -1){
					// Do Matrix math (yay...)
					/* REMEMBER: Inkscape y axis is inverted...*/
					print(" TODO: Implement Matrix Transform");
				}else
					print("Unknown path transformation:\n " + transform);
				
			}else{
				print(" No transform found");
			}		
		}
	}
}

/*
*  Writes all activity to a log file, while doing its thing
*/
output("svg2poly.log", "wt")
{
	setup_cmd();
	
	// Select file
	string file = dlgFileOpen("Select a file", "", "*.svg"); 
	
	if (file != ""){
		int i=0, j=0;
		string svg[];
		master_xml= "";
		
		int len = fileread(svg, file);
		
		// Read file into one massive xml line
		for(; i<len; i++){
			//print(svg[i]);
			master_xml += svg[i];
		}
		
		// Extract Points and apply transformations
		extract_nested_data(xmlelement(master_xml, "svg/g"));
		
		// Correct Inkscape's Formatting issue
		for(i=0; i<num_pts; i++)
			pts_y[i] *= -1;
		
		// Center object
		real min_x, min_y,
			max_x, max_y;
		min_x = max_x = pts_x[0];
		min_y = max_y = pts_y[0];
		
		for(i=1; i<num_pts; i++){
			// Find Min & Max
			if (pts_x[i] < min_x)
				min_x = pts_x[i];
			if (pts_x[i] > max_x)
				max_x = pts_x[i];
				
			if (pts_y[i] < min_y)
				min_y = pts_y[i];
			if (pts_y[i] > max_y)
				max_y = pts_y[i];
		}
		
		real x_center = (max_x - min_x)/2,
			y_center = (max_y - min_y)/2;
		
		// Apply offsets to all points
		for(i=0; i < num_pts; i++){
			pts_x[i] -= min_x + x_center;
			pts_y[i] -= min_y + y_center;
		}
		printf("%f %f\n", x_center, y_center);
		
		// Generate eagle command
		for(i=0; i<num_pts; i++){
			if (poly_start[i] == 1){
				//printf("%2d) %3.4f %3.4f\n", j++, pts_x[i] * ratioX, pts_y[i] * ratioY);
				if (i != 0)
					sprintf(cmd, "%s;\npoly (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
				else
					sprintf(cmd, "%s (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
			}else{
				//printf("    %3.4f %3.4f\n", pts_x[i] * ratioX, pts_y[i] * ratioY);
				sprintf(cmd, "%s (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
			}
		}
		
		cmd += ";\n";
		print("\nEagle Command:\n" + cmd);
		exit(cmd);
	}
	
	exit("");
}