package pipe.dataLayer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
//import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
//import java.awt.Shape;
//import java.awt.geom.Line2D;
import pipe.gui.ArrowHead;
import pipe.gui.Constants;
import pipe.gui.GuiView;
import pipe.gui.CreateGui;

/**
 * <b>Arc</b> - Petri-Net Arc Class
 *
 * @see <p><a href="..\PNMLSchema\index.html">PNML  -  Petri-Net XMLSchema (stNet.xsd)</a>
 * @see </p><p><a href="..\..\..\UML\dataLayer.html">UML  -  PNML Package </a></p>
 * @version 1.0
 * @author James D Bloom
 */
public class Arc extends PetriNetObject implements Constants {
	
	public final static String type = "Arc";
	/** Current Marking */
	private int weight = 1;
	/** Initial Marking X-axis Offset */
	private Double weightOffsetX = null;
	/** Initial Marking Y-axis Offset */
	private Double weightOffsetY = null;
	/** Inscription */
//	private String inscription = null;
	/** Inscription X-axis Offset */
//	private Double inscriptionOffsetX = null;
	/** Inscription Y-axis Offset */
//	private Double inscriptionOffsetY = null;
	/** Arc is of type Line2D.Double */
//	private Line2D.Double arc = new Line2D.Double(0, 0, 0, 0);
//	private Line2D.Double realarc;
	
	private ArrowHead arrow = new ArrowHead();
	private NameLabel weightLabel = new NameLabel();
	
//	private Rectangle bounds = new Rectangle();
	
	private static Point2D.Double point;
	
	/** references to the objects this arc connects */
	private PlaceTransitionObject source = null;
	private PlaceTransitionObject target = null;
	private boolean deleted = false;			// Used for cleanup purposes
	
	private ArcPath myPath = new ArcPath(this);
	
	/**
	 * Create Petri-Net Arc object
	 *
	 * @param startPositionXInput Start X-axis Position
	 * @param startPositionYInput Start Y-axis Position
	 * @param endPositionXInput End X-axis Position
	 * @param endPositionYInput End Y-axis Position
	 * @param sourceInput Arc source
	 * @param targetInput Arc target
	 * @param idInput Arc id
	 * @param colorInput Color
	 */
	public Arc(double startPositionXInput, double startPositionYInput, double endPositionXInput,
			double endPositionYInput, PlaceTransitionObject sourceInput, PlaceTransitionObject targetInput,
			int weightInput, String idInput, Color colorInput){
	
		this(startPositionXInput,startPositionYInput,endPositionXInput,endPositionYInput,
				sourceInput, targetInput, idInput, colorInput);
    setWeight(weightInput);
	}	
	
	/**
	 * Create Petri-Net Arc object
	 *
	 * @param startPositionXInput Start X-axis Position
	 * @param startPositionYInput Start Y-axis Position
	 * @param endPositionXInput End X-axis Position
	 * @param endPositionYInput End Y-axis Position
	 * @param sourceInput Arc source
	 * @param targetInput Arc target
	 * @param idInput Arc id
	 * @param colorInput Color
	 */
	public Arc(double startPositionXInput, double startPositionYInput, double endPositionXInput,
			double endPositionYInput, PlaceTransitionObject sourceInput, PlaceTransitionObject targetInput,
			String idInput, Color colorInput){
	
		this(startPositionXInput,startPositionYInput,endPositionXInput,endPositionYInput);
		
		id = idInput;
		setSource(sourceInput);
		setTarget(targetInput);
		updateArcPosition();
		updateArcPosition();
	}
	
	
	/**
	 * Create Petri-Net Arc object
	 *
	 * @param startPositionXInput Start X-axis Position
	 * @param startPositionYInput Start Y-axis Position
	 * @param endPositionXInput End X-axis Position
	 * @param endPositionYInput End Y-axis Position
	 * @param idInput Arc id
	 * @param colorInput Color
	 */
	public Arc(double startPositionXInput, double startPositionYInput, double endPositionXInput,
			double endPositionYInput, String idInput){
		this(startPositionXInput, startPositionYInput,endPositionXInput,endPositionYInput);
		id = idInput;
		
	}
	
	/**
	 * Create Petri-Net Arc object
	 *
	 * @param startPositionXInput Start X-axis Position
	 * @param startPositionYInput Start Y-axis Position
	 * @param endPositionXInput End X-axis Position
	 * @param endPositionYInput End Y-axis Position
	 * @param colorInput Color
	 */
	public Arc(double startPositionXInput, double startPositionYInput, double endPositionXInput,
			double endPositionYInput) {
//		arc.setLine(startPositionXInput, startPositionYInput, endPositionXInput, endPositionYInput);
		arrow.setLocation(startPositionXInput, startPositionYInput, endPositionXInput, endPositionYInput);
		myPath.addPoint((float)startPositionXInput, (float)startPositionYInput, ArcPathPoint.STRAIGHT);
//		myPath.addPoint(100,100, ArcPath.ArcPathPoint.STRAIGHT);
		myPath.addPoint((float)endPositionXInput, (float)endPositionYInput, ArcPathPoint.STRAIGHT);
		myPath.createPath();
		updateBounds();
		
	}
	
	/**
	 * Create Petri-Net Arc object
	 *
	 */
	public Arc(PlaceTransitionObject newSource) {
		source = newSource;
		myPath.addPoint();
		myPath.addPoint();
		myPath.createPath();
	}
	
	/**
	 * Set id
	 *
	 * @param idInput String value for Arc id;
	 */
	public void setId(String idInput) {
		id = idInput;
	}
	
	/**
	 * Set source
	 *
	 * @param sourceInput PlaceTransitionObject value for Arc source;
	 */
	public void setSource(PlaceTransitionObject sourceInput) {
		source = sourceInput;
	}
	
	/**
	 * Set target
	 *
	 * @param targetInput PlaceTransitionObject value for Arc target;
	 */
	public void setTarget(PlaceTransitionObject targetInput) {
		target = targetInput;
		updateArcPosition();
	}
	
	
	/**
	 * Set weight
	 *
	 * @param weightInput String value for Arc weight;
	 */
	public void setWeight(int weightInput) {
		weight = weightInput;
		weightLabel.setText(Integer.toString(weight));

		setWeightLabelPosition();
		
		if (weight != 1){
			addWeightLabelToContainer();
			weightLabel.updateSize();
		}
			

		
		//    try {
		//      if(arrow != null)
		//        arrow.getNameLabel().setText(String.valueOf(Weight));
		//    } catch (Exception e) {
		//      e.printStackTrace();
		//    }
	}
	
	public void setWeightLabelPosition(){
//		Point2D.Float p = null;
		
		if(myPath.getEndIndex() > 0){
			Point2D.Float firstPoint  = myPath.getPathPoint(1).getControl1();
			Point2D.Float secondPoint = myPath.getPathPoint(1).getControl2();

//			double angle = myPath.getStartAngle();
      
//			weightLabel.setPosition((int)(firstPoint.x + (Math.cos(angle + Math.PI/24)*30)),
//								    (int)(firstPoint.y + (Math.sin(angle + Math.PI/24)*30)) );
			
      weightLabel.setPosition(
      		(int)((secondPoint.x+firstPoint.x)*0.5),
			(int)((secondPoint.y+firstPoint.y)*0.5)
      );

		}
		

		
	}
	
	public void addWeightLabelToContainer() {
		if (getParent() != null) {
	  		getParent().add(weightLabel);
	  		//weightLabel.setPosition((int)((myPath.getPoint(1).x) - (myPath.getPoint(0).x)), (int)((myPath.getPoint(1).y) - (myPath.getPoint(0).y)));
	  	}
	}
	
	/**
	 * Set X-axis offset for weight position
	 *
	 * @param weightOffsetXInput Double value for weight X-axis offset
	 */
	public void setWeightOffsetX(double weightOffsetXInput) {
		weightOffsetX = new Double(weightOffsetXInput);
	}
	
	/**
	 * Set Y-axis offset for weight position
	 *
	 * @param weightOffsetYInput Double value for weight Y-axis offset
	 */
	public void setWeightOffsetY(double weightOffsetYInput) {
		weightOffsetY = new Double(weightOffsetYInput);
	}
	
	/**
	 * Set inscription
	 *
	 * @param inscriptionInput String value for Arc inscription;
	 */
//	public void setInscription(String inscriptionInput) {
//		inscription = inscriptionInput;
//	}
	
	/**
	 * Set X-axis offset for inscription position
	 *
	 * @param inscriptionOffsetXInput Double value for inscription X-axis offset
	 */
//	public void setInscriptionOffsetX(double inscriptionOffsetXInput) {
//		inscriptionOffsetX = new Double(inscriptionOffsetXInput);
//	}
	
	/**
	 * Set Y-axis offset for inscription position
	 *
	 * @param inscriptionOffsetYInput Double value for inscription Y-axis offset
	 */
//	public void setInscriptionOffsetY(double inscriptionOffsetYInput) {
//		inscriptionOffsetY = new Double(inscriptionOffsetYInput);
//	}
	
	/**
	 * Get id
	 *
	 * @return String value for Arc id;
	 */
	public String getId() {
		if(id != null) {
			return id;
		} else {
			if(source != null && target != null) {
				return source.getId() + " to " + target.getId();
			} else {
				return "";
			}
		}
	}
	
	/**
	 * Get source returns null if value not yet entered
	 *
	 * @return String value for Arc source;
	 */
	public PlaceTransitionObject getSource() {
		return source;
	}
	
	/**
	 * Get target returns null if value not yet entered
	 *
	 * @return String value for Arc target;
	 */
	public PlaceTransitionObject getTarget() {
		return target;
	}
	
	/**
	 * Get X-axis value of start position
	 *
	 * @return Double value for X-axis of start position
	 */
	public double getStartPositionX() {
		return myPath.getPoint(0).getX();
	}
	
	/**
	 * Get Y-axis value of start position
	 *
	 * @return Double value for Y-axis of start position
	 */
	public double getStartPositionY() {
		return myPath.getPoint(0).getY();
	}
	
	/**
	 * Get X-axis value of end position
	 *
	 * @return Double value for X-axis of end position
	 */
	public double getEndPositionX() {
		return myPath.getPoint(myPath.getEndIndex()).getX();
	}
	
	/**
	 * Get Y-axis value of end position
	 *
	 * @return Double value for Y-axis of end position
	 */
	public double getEndPositionY() {
		return myPath.getPoint(myPath.getEndIndex()).getY();
	}
	
	/**
	 * Get weight
	 *
	 * @return Integer value for Arc weight;
	 */
	public int getWeight() {
		return weight;
	}
	
	/**
	 * Get X-axis offset for weight position
	 *
	 * @return Double value for weight X-axis offset
	 */
	public double getWeightOffsetX() {
		if(weightOffsetX == null)
			return 0;
		return weightOffsetX.doubleValue();
	}
	
	/**
	 * Get Y-axis offset for weight position
	 *
	 * @return Double value for weight Y-axis offset
	 */
	public double getWeightOffsetY() {
		if(weightOffsetY == null)
			return 0;
		return weightOffsetY.doubleValue();
	}
	
	/**
	 * Get inscription returns null if value not yet entered
	 *
	 * @return String value for Arc inscription;
	 */
//	public String getInscription() {
//		return inscription;
//	}
	
	/**
	 * Get X-axis offset for inscription position
	 *
	 * @return Double value for inscription X-axis offset
	 */
//	public double getInscriptionOffsetX() {
//		if(inscriptionOffsetX == null)
//			return 0;
//		return inscriptionOffsetX.doubleValue();
//	}
	
	/**
	 * Get Y-axis offset for inscription position
	 *
	 * @return Double value for inscription Y-axis offset
	 */
//	public double getInscriptionOffsetY() {
//		if(inscriptionOffsetY == null)
//			return 0;
//		return inscriptionOffsetY.doubleValue();
//	}
	
	
	/**
	 * Get weight returns null if value not yet entered
	 *
	 *
	 * @return Integer value for Arc weight;
	 */
	public int getWeightObject() {
		return weight;
	}
	
	/**
	 * Get X-axis offset for weight position
	 * returns null if value not yet entered
	 *
	 * @return Double value for weight X-axis offset
	 */
	public Double getWeightOffsetXObject() {
		return weightOffsetX;
	}
	
	/**
	 * Get Y-axis offset for weight position
	 * returns null if value not yet entered
	 *
	 * @return Double value for weight Y-axis offset
	 */
	public Double getWeightOffsetYObject() {
		return weightOffsetY;
	}
	
	/**
	 * Get X-axis offset for inscription position
	 * returns null if value not yet entered
	 *
	 * @return Double value for inscription X-axis offset
	 */
//	public Double getInscriptionOffsetXObject() {
//		return inscriptionOffsetX;
//	}
	
	/**
	 * Get Y-axis offset for inscription position
	 * returns null if value not yet entered
	 *
	 * @return Double value for inscription Y-axis offset
	 */
//	public Double getInscriptionOffsetYObject() {
//		return inscriptionOffsetY;
//	}
	
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//		g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
		
		g2.translate(COMPONENT_DRAW_OFFSET-myPath.getBounds().getX(),
				COMPONENT_DRAW_OFFSET-myPath.getBounds().getY());

		if (selected && !ignoreSelection)
			g2.setPaint(SELECTION_LINE_COLOUR);
		else
			g2.setPaint(ELEMENT_LINE_COLOUR);
		
		g2.fill(arrow);
		g2.draw(myPath);
		
		
//		if (selected)
//		{
//			myPath.createSelection();
//			g2.fill(myPath.getArcSelection());
//			g2.setPaint(SELECTION_LINE_COLOUR);
//			g2.fill(myPath.getPointSelection());
//		}
		
	}

	/** Updates the start position of the arc, resets the arrowhead and updates the bounds*/
	public void updateArcPosition() {
		if (source != null)
			source.updateEndPoint(this);
		
		if (target != null)
			target.updateEndPoint(this);
    
    myPath.createPath();
	}
	
	public void updateArrow() {
    if(myPath.getEndIndex()!=-1) arrow.setLocation(myPath.getPoint(myPath.getEndIndex()).getX(),
				  		  myPath.getPoint(myPath.getEndIndex()).getY(), myPath.getEndAngle()+Math.PI);	
	}
	
	public void setEndPoint(double x, double y, boolean type) {
		myPath.setPointLocation(myPath.getEndIndex(),x,y);
		myPath.setPointType(myPath.getEndIndex(),type);
		updateArcPosition();
	}
	
	public void setTargetLocation(double x, double y) {
		myPath.setPointLocation(myPath.getEndIndex(),x,y);
		updateArrow();
		myPath.createPath();
		updateBounds();
		repaint();
	}
	public void setSourceLocation(double x, double y) {
		myPath.setPointLocation(0,x,y);
		updateArrow();
		myPath.createPath();
		updateBounds();
		repaint();
	}
	
	/** Updates the bounding box of the arc component based on the arcs bounds*/
	public void updateBounds() {
		bounds = myPath.getBounds();
		bounds.grow(COMPONENT_DRAW_OFFSET,COMPONENT_DRAW_OFFSET);
		setBounds(bounds);
	}
	
	public ArcPath getArcPath() {
		return myPath;
	}
	
	public boolean contains(int x, int y) {
		point = new Point2D.Double(x+myPath.getBounds().getX()-COMPONENT_DRAW_OFFSET,
				   y+myPath.getBounds().getY()-COMPONENT_DRAW_OFFSET);
		if (CreateGui.getApp().getMode() == SELECT)
		{
			if (myPath.proximityContains(point) || selected)	// show also if Arc itself selected
				myPath.showPoints();
			else
				myPath.hidePoints();
		}
		return myPath.contains(point);
	}

//######################################################################################
	public void addedToGui()	// called by GuiView upon adding component.
	{
		myPath.addPointsToGui((GuiView)getParent());
		myPath.createPath();
		updateArcPosition();
	}
//######################################################################################
	public void delete() {
		if (!deleted)
		{
			myPath.delete();
			super.delete();
			deleted = true;
		}
	}
//######################################################################################
	public void setPathToTransitionAngle(int angle){
		myPath.setTransitionAngle(angle);
	}
//######################################################################################
}
//######################################################################################
