package com.bbn.poi.xdgf.parsers;


import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Comparator;

import org.apache.poi.POIXMLException;
import org.apache.poi.xdgf.usermodel.XDGFShape;

import com.bbn.poi.xdgf.parsers.rx.SpatialTools;
import com.github.davidmoten.rtree.geometry.Rectangle;
import com.tinkerpop.blueprints.Vertex;

public class ShapeData {

	// orders by largest first
	public static class OrderByLargestAreaFirst implements Comparator<ShapeData> {
		@Override
		public int compare(ShapeData o1, ShapeData o2) {
			return Float.compare(o2.area, o1.area);
		}
	}
	
	public Vertex vertex;
	public Long parentId = null;
	
	// in global coordinates
	public Rectangle bounds;
	public float area;
	
	// in global coordinates
	public Path2D path1D = null;
	public Point2D path1Dstart = null;
	public Point2D path1Dend = null;
	
	public Path2D path2D = null;
	
	public Point2D textCenter = null;
	
	// don't store the actual shape, just useful attributes needed later
	// -> allows us to join/split shapes at will
	public long shapeId;
	public boolean hasText;
	public boolean isTextbox;
	
	public Color lineColor;
	public Integer linePattern;
	
	public boolean removed = false;
	
	
	public ShapeData(Vertex vertex, XDGFShape shape, AffineTransform globalTransform) {
		
		// calculate bounding boxes + other geometry information we'll need later
		Path2D.Double shapeBounds = shape.getBoundsAsPath();
		
		shapeBounds.transform(globalTransform);
		
		if (shape.isShape1D()) {
			path1D = shape.getPath();
			path1D.transform(globalTransform);
			
			calculate1dEndpoints();
		} else {
			
			// grow the shape boundary slightly to account for rounding errors
			Rectangle2D tmp = shapeBounds.getBounds2D();
			AffineTransform at = new AffineTransform();
			
			double w = tmp.getWidth();
			double h = tmp.getHeight();
			
			at.scale((w+0.0002)/w, (h+0.0002)/h);
			at.translate(-0.0001, -0.0001);
			
			shapeBounds.transform(at);
			path2D = shapeBounds;
		}
		
		this.vertex = vertex;
		this.shapeId = shape.getID();
		this.bounds = SpatialTools.getShapeBounds(shapeBounds);
		this.area = this.bounds.area();
		
		this.lineColor = shape.getLineColor();
		this.linePattern = shape.getLinePattern();
		
		XDGFShape parentShape = shape.getParentShape();
		if (parentShape != null)
			parentId = parentShape.getID(); 
		
		hasText = shape.hasText();
		isTextbox = hasText && !shape.hasMaster() && !shape.hasMasterShape();
		
		if (hasText)
			textCenter = globalTransform.transform(shape.getText().getTextCenter(), null);
	}
	
	// clone 1d shapes
	public ShapeData(long shapeId, Vertex vertex, ShapeData other, Path2D.Double new1dPath) {
		
		this.shapeId = shapeId;
		this.vertex = vertex;
		
		parentId = other.parentId;
		lineColor = other.lineColor;
		linePattern = other.linePattern;
		
		path1D = new1dPath;
		calculate1dEndpoints();
		
		bounds = SpatialTools.getShapeBounds(new1dPath.getBounds2D());
		area = this.bounds.area();
		
		hasText = false;
		isTextbox = false;
	}
	
	public boolean is1d() {
		return path1D != null;
	}
	
	public Path2D getPath() {
		return path1D != null ? path1D : path2D;
	}
	
	protected void calculate1dEndpoints() {
		// can't use beginX et al here, as it's in parent coordinates
		double[] coords = new double[6];
		if (path1D.getPathIterator(null).currentSegment(coords) != PathIterator.SEG_MOVETO) {
			throw new POIXMLException("Invalid 1d path");
		}
		
		path1Dstart = new Point2D.Double(coords[0], coords[1]);
		path1Dend = path1D.getCurrentPoint();
	}
}

@@ -336,16 +336,56 @@
 Shape;%0A%0A
+import com.bbn.poi.xdgf.geom.GeomUtils;%0A
 import c
@@ -421,24 +421,24 @@
 atialTools;%0A
-
 import com.g
@@ -1645,16 +1645,66 @@
 sform);%0A
+%09%09shapeBounds = GeomUtils.roundPath(shapeBounds);%0A
 %09%09%0A%09%09if 
@@ -1792,16 +1792,57 @@
 sform);%0A
+%09%09%09path1D = GeomUtils.roundPath(path1D);%0A
 %09%09%09%0A%09%09%09c
@@ -1879,356 +1879,8 @@
 e %7B%0A
-%09%09%09%0A%09%09%09// grow the shape boundary slightly to account for rounding errors%0A%09%09%09Rectangle2D tmp = shapeBounds.getBounds2D();%0A%09%09%09AffineTransform at = new AffineTransform();%0A%09%09%09%0A%09%09%09double w = tmp.getWidth();%0A%09%09%09double h = tmp.getHeight();%0A%09%09%09%0A%09%09%09at.scale((w+0.0002)/w, (h+0.0002)/h);%0A%09%09%09at.translate(-0.0001, -0.0001);%0A%09%09%09%0A%09%09%09shapeBounds.transform(at);%0A
 %09%09%09p
@@ -3399,16 +3399,16 @@
 ds%5B1%5D);%0A
-
 %09%09path1D
@@ -3442,10 +3442,267 @@
 t();%0A%09%7D%0A
+%09%0A%09public float getCenterX() %7B%0A%09%09return bounds.x1() + (bounds.x2() - bounds.x1())/2.0F; %0A%09%7D%0A%09%0A%09public float getCenterY() %7B%0A%09%09return bounds.y1() + (bounds.y2() - bounds.y1())/2.0F; %0A%09%7D%0A%09%0A%09public String toString() %7B%0A%09%09return %22%5BShapeData %22 + shapeId + %22%5D%22;%0A%09%7D%0A
 %7D%0A

Fix floating point errors in shape paths by rounding