// PathVisio,
// a tool for data visualization and analysis using Biological Pathways
// Copyright 2006-2011 BiGCaT Bioinformatics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package org.pathvisio.core.model;

import java.awt.geom.Point2D;

import org.pathvisio.core.view.LinAlg;
import org.pathvisio.core.view.LinAlg.Point;


/**
 * Implement this to provide a line shape for connectors.
 *
 * Different implementations may draw a Line in different ways, for example with
 * curved or straight lines.
 */
public interface ConnectorShape {

	public static final int AXIS_X = 0;
	public static final int AXIS_Y = 1;

	/**
	 * Force the connector to redraw it's path. The cache for segments,
	 * waypoints and shape.
	 * @param restrictions The ConnectorRestrictions that provides the start, end and
	 * preferred waypoints
	 */
	public void recalculateShape(ConnectorRestrictions restrictions);

	/**
	 * Get the Shape that represents the connector path
	 */
	public java.awt.Shape getShape();



	 /**
	  *  Calculate shape from the width of the line endings
	  *  This gets
	  */
	 public java.awt.Shape calculateAdjustedShape(double startLineEndingWidth, double endLineEndingWidth);


	/**
	 * Get the individual segments of the path
	 */
	public Segment[] getSegments();


	/**
	 * Get the waypoints through which the connector passes
	 */
	public WayPoint[] getWayPoints();

	/**
	 * Checks whether the waypoints as provided by the ConnectorRestrictions
	 * are valid and will be used to draw the connector path
	 * @return true if the waypoints are used, false if not
	 */
	public boolean hasValidWaypoints(ConnectorRestrictions restrictions);

	/**
	 * A waypoint, a point through which the connector passes. Each waypoint
	 * will have a handle in the view, so the user can modify it's position.
	 */
	public class WayPoint extends Point2D.Double {
		public WayPoint(Point2D position) {
			super(position.getX(), position.getY());
		}

		public WayPoint(double x, double y) {
			super(x, y);
		}
	}

	/**
	 * A single segment of the connector path.
	 * This is simply a combination of a start and end Point2D.
	 */
	public class Segment {
		private Point2D start, end;

		protected Segment(Point2D start, Point2D end) {
			this.start = start;
			this.end = end;
		}

		public Point2D getMEnd() {
			return end;
		}

		public Point2D getMStart() {
			return start;
		}

		public void setMEnd(Point2D end) {
			this.end = end;
		}

		public void setMStart(Point2D start) {
			this.start = start;
		}

		/** the center of the bounding box around start, end */
		public Point2D getMCenter() {
			return new Point2D.Double(
					start.getX() + (end.getX() - start.getX()) / 2,
					start.getY() + (end.getY() - start.getY()) / 2
			);
		}

		public double getMLength() {
			return LinAlg.distance(new Point(start), new Point(end));
		}
		/** The co-ordinates of the new end point
		 * if the length of the seqment is reduced by
		 * the parameter.
		 * */
		public Point2D calculateNewEndPoint(double reduceBy) {
			double xs = this.start.getX();
			double xe = this.end.getX();
			double ys = this.start.getY();
			double ye = this.end.getY();

			double theta = Math.atan2(ye - ys, xe - xs); //angle in polar coordinates
			double r = reduceBy;	//radius in polar coordinates

			double xshift = r*Math.cos(theta);
			double yshift = r*Math.sin(theta);

			xe = xe - xshift;
			ye = ye - yshift;

			return new Point2D.Double(xe,ye);
		}


		/** The co-ordinates of the new start point
		 * if the length of the seqment is reduced by
		 * the parameter. Easier to use the NewEndPoint
		 * method than rewrite it
		 * CAUTION:: may need examining with curved lines, The Math may
		 * not hold
		 * Also caution for elbow lines where the last line
		 * may be smaller than the width of the arrow shape
		 * We may have to put this function in each individual
		 * Connector Shape implementation
		 * */
		public Point2D calculateNewStartPoint(double reduceBy) {
			return calculateNewEndPoint(this.getMLength() - reduceBy);
			//OK for now but CAUTION for curved lines & elbow lines
			//
		}


		public String toString() {
			return start + ", " + end;
		}
	}

	/**
	 * Translates a 1-dimensional line coordinate to a 2-dimensional
	 * view coordinate.
	 * The 1-dimensional line coordinate is position objects that are attached
	 * to the line.
	 */
	public Point2D fromLineCoordinate(double l);

	/**
	 * Translates a 2-dimensional view coordinate to a 1-dimensional
	 * line coordinate.
	 */
	public double toLineCoordinate(Point2D v);
}