package emn.fr.ascola.view;

import java.awt.BasicStroke;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.CubicCurve2D;

import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import prefuse.Constants;

import prefuse.render.EdgeRenderer;
import prefuse.util.GraphicsLib;
import prefuse.visual.EdgeItem;
import prefuse.visual.VisualItem;

/**
 * Multiple and loop edge renderer.
 * This solution assumes that we have a fields "count" int.class
 *  in the edge which gives the edge numbering between same pair of nodes
 * @author jroyer
 *  20/4/2008
 *  1/6/2009
 *  TODO problem with the interactive selection
 */
public class MultiEdgeRenderer extends EdgeRenderer {

	/**
	 * Constructor.
	 * @param edgeTypeCurve type curve
	 * @param edgeArrowForward arrow head
	 */
	public MultiEdgeRenderer(int edgeTypeCurve, int edgeArrowForward) {
		super(edgeTypeCurve, edgeArrowForward);
	}
	
	/**
	 * Compute the control point for a Bezier curve. x1,y1 and x4,y4 are the
	 * ends of the segments
	 * @param eitem edge item to display
	 * @param cp control points (Bezier 2)
	 * @param x1 coordinate x for origin
	 * @param y1 coordinate y for origin
	 * @param x4 coordinate x for end
	 * @param y4 coordinate y for end
	 */
	protected void getCurveControlPoints(EdgeItem eitem, Point2D[] cp,
			double x1, double y1, double x4, double y4) {
		double dx = x4 - x1, dy = y4 - y1;
		// modify to add an offset relative to what this edge's index is
		dx =  dx * (1 + eitem.getInt("count"));
		dy =  dy * (1 + eitem.getInt("count"));
		cp[0].setLocation(x1 + 2 * dx / 3, y1);
		cp[1].setLocation(x4 - dx / 8, y4 - dy / 8);
	}

	/**
	 * Copy from EgdeRenderer and adaptation for loop.
	 * This compute the display shape of an item.
	 * @param item the visual item
	 */
	protected Shape getRawShape(VisualItem item) {
		EdgeItem edge = (EdgeItem) item;
		VisualItem item1 = edge.getSourceItem();
		VisualItem item2 = edge.getTargetItem();
		int type = m_edgeType;

		getAlignedPoint(m_tmpPoints[0], item1.getBounds(), m_xAlign1, m_yAlign1);
		getAlignedPoint(m_tmpPoints[1], item2.getBounds(), m_xAlign2, m_yAlign2);
		m_curWidth = (float) (m_width * getLineWidth(item));

		// create the arrow head, if needed
		EdgeItem e = (EdgeItem) item;
		if (e.isDirected() && m_edgeArrow != Constants.EDGE_ARROW_NONE) {
			// get starting and ending edge endpoints
			boolean forward = (m_edgeArrow == Constants.EDGE_ARROW_FORWARD);
			Point2D start = null, end = null;
			start = m_tmpPoints[forward ? 0 : 1];
			end = m_tmpPoints[forward ? 1 : 0];

			// compute the intersection with the target bounding box
			VisualItem dest = forward ? e.getTargetItem() : e.getSourceItem();
			int i = GraphicsLib.intersectLineRectangle(start, end, dest
					.getBounds(), m_isctPoints);
			if (i > 0)
				end = m_isctPoints[0];

			// create the arrow head shape
			AffineTransform at = getArrowTrans(start, end, m_curWidth);
			m_curArrow = at.createTransformedShape(m_arrowHead);

			// update the endpoints for the edge shape
			// need to bias this by arrow head size
			Point2D lineEnd = m_tmpPoints[forward ? 1 : 0];
			lineEnd.setLocation(0, -m_arrowHeight);
			at.transform(lineEnd, lineEnd);
		} else {
			m_curArrow = null;
		}

		// create the edge shape
		Shape shape = null;
		double n1x = m_tmpPoints[0].getX();
		double n1y = m_tmpPoints[0].getY();
		double n2x = m_tmpPoints[1].getX();
		double n2y = m_tmpPoints[1].getY();
		switch (type) {
		case Constants.EDGE_TYPE_LINE:
				m_line.setLine(n1x, n1y, n2x, n2y);
				shape = m_line;
			break;
		case Constants.EDGE_TYPE_CURVE:
			int count = edge.getInt("count");
			// to solve loop edges
			if (item1 == item2) {
				//shape = new QuadCurve2D.Double(n1x + 15, n1y - 15, n1x + 80 + 30 * count, n1y - 80 + 30 * count, n2x, n2y);
				shape = new QuadCurve2D.Double(n1x + 10, n1y - 10, n1x - 100 + 50 * count, n1y - 100 - 10 * count, n2x, n2y);
			} else {
				getCurveControlPoints(edge, m_ctrlPoints, n1x, n1y, n2x, n2y);
				shape = new CubicCurve2D.Double(n1x, n1y, m_ctrlPoints[0].getX(), m_ctrlPoints[0].getY(), m_ctrlPoints[1].getX(),
						m_ctrlPoints[1].getY(), n2x, n2y);
			}
			break;
		default:
			throw new IllegalStateException("Unknown edge type");
		}
		// return the edge shape
		return shape;
	}
}
