//###################################################################################### 
package pipe.dataLayer;
//###################################################################################### 
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;

import pipe.gui.CreateGui;
//###################################################################################### 
/**
 * <b>Place</b> - Petri-Net Place 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 Place extends PlaceTransitionObject {
//###################################################################################### 
	public final static String type = "Place";
	/** Initial Marking */
	private Integer initialMarking = null;
	
	/** Current Marking */
	private Integer currentMarking = null;
	
	/** Initial Marking X-axis Offset */
	private Double markingOffsetX = null;
	
	/** Initial Marking Y-axis Offset */
	private Double markingOffsetY = null;
	
	public static final int DIAMETER = PLACE_TRANSITION_HEIGHT;
		
	/** Token Width */
	public static int tWidth = 5;
	
	/** Token Height */
	public static int tHeight = 5;
	
	/** Ellipse2D.Double place */
	private static Ellipse2D.Double	place = new Ellipse2D.Double(0, 0, DIAMETER, DIAMETER);
	private static Shape proximityPlace = 
			(new BasicStroke(PLACE_TRANSITION_PROXIMITY_RADIUS)).createStrokedShape(place);
//###################################################################################### 
	/**
	 * Create Petri-Net Place object
	 *
	 * @param positionXInput X-axis Position
	 * @param positionYInput Y-axis Position
	 * @param idInput Place id
	 * @param nameInput Name
	 * @param nameOffsetXInput Name X-axis Position
	 * @param nameOffsetYInput Name Y-axis Position
	 * @param initialMarkingInput Initial Marking
	 * @param markingOffsetXInput Marking X-axis Position
	 * @param markingOffsetYInput Marking Y-axis Position
	 * @param color Color
	 */
	public Place(double positionXInput, double positionYInput, String idInput, String nameInput, double nameOffsetXInput, double nameOffsetYInput, int initialMarkingInput, double markingOffsetXInput, double markingOffsetYInput){
		super(positionXInput, positionYInput, idInput, nameInput, nameOffsetXInput, nameOffsetYInput);
		initialMarking = new Integer(initialMarkingInput);
		currentMarking = new Integer(initialMarkingInput);
		markingOffsetX = new Double(markingOffsetXInput);
		markingOffsetY = new Double(markingOffsetYInput);
		componentWidth = DIAMETER;
		componentHeight = DIAMETER;
		setCentre((int)positionX, (int)positionY);
		updateBounds();
	}
//###################################################################################### 
	/**
	 * Create Petri-Net Place object
	 *
	 * @param positionXInput X-axis Position
	 * @param positionYInput Y-axis Position
	 * @param idInput Place id
	 * @param color Color
	 */
	public Place(double positionXInput, double positionYInput, String idInput){
		super(positionXInput, positionYInput, idInput);
		componentWidth = DIAMETER;
		componentHeight = DIAMETER;
		updateBounds();
	}
//###################################################################################### 
	/**
	 * Create Petri-Net Place object
	 *
	 * @param positionXInput X-axis Position
	 * @param positionYInput Y-axis Position
	 * @param color - modified by aed02
	 */
	public Place(double positionXInput, double positionYInput){
		super(positionXInput, positionYInput);
		componentWidth = DIAMETER;
		componentHeight = DIAMETER;
		setCentre((int)positionX, (int)positionY);
		updateBounds();
		
	}
//###################################################################################### 
	/**
	 * Create empty Petri-Net Place object
	 *
	 */
	public Place(){
	}
//###################################################################################### 
	/**
	 * Paints the Place component taking into account the number of tokens from the currentMarking
	 * @param g The Graphics object onto which the Place is drawn.
	 */
	public void paintComponent(Graphics g) {
		
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		
		Insets insets = getInsets();
		int x = insets.left;
		int y = insets.top;
		g2.translate(COMPONENT_DRAW_OFFSET, COMPONENT_DRAW_OFFSET);
		int currentWidth = getWidth() - x - insets.right;
		int currentHeight = getHeight() - y - insets.bottom;
		
		g2.setStroke(new BasicStroke(1.0f));	
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
    if(selected && !ignoreSelection)
      g2.setColor(SELECTION_FILL_COLOUR);
    else
      g2.setColor(ELEMENT_FILL_COLOUR);
		g2.fill(place);

		if (selected && !ignoreSelection)
			g2.setPaint(SELECTION_LINE_COLOUR);
		else
			g2.setPaint(ELEMENT_LINE_COLOUR);
		g2.draw(place);
		
		int marking = getCurrentMarking();
		
		// structure sees how many markings there are and fills the place in with the appropriate number.
		switch(marking)
		{
		case 5: g.drawOval(x+6,y+6,tWidth,tHeight);
		g.fillOval(x+6,y+6,tWidth,tHeight);
		case 4: g.drawOval(x+18,y+20,tWidth,tHeight);
		g.fillOval(x+18,y+20,tWidth,tHeight);
		case 3:g.drawOval(x+6,y+20,tWidth,tHeight);
		g.fillOval(x+6,y+20,tWidth,tHeight);
		case 2: g.drawOval(x+18,y+6,tWidth,tHeight);
		g.fillOval(x+18,y+6,tWidth,tHeight);
		case 1: g.drawOval(x+12,y+13,tWidth,tHeight);
		g.fillOval(x+12,y+13,tWidth,tHeight);break;
		case 0: break;
		default: g.drawString(String.valueOf(marking),x+5,y+20);
		}
		
	}
//###################################################################################### 
	/**
	 * Set initial marking
	 *
	 * @param initialMarkingInput Integer value for initial marking
	 */
	public void setInitialMarking(int initialMarkingInput) {
		initialMarking = new Integer(initialMarkingInput);
	}
//###################################################################################### 
	/**
	 * Set current marking
	 *
	 * @param currentMarkingInput Integer value for current marking
	 */
	public void setCurrentMarking(int currentMarkingInput) {
		currentMarking = new Integer(currentMarkingInput);
	}
//###################################################################################### 
	/**
	 * Set X-axis offset for initial marking
	 *
	 * @param  markingOffsetXInput Integer value for X-axis offset of initial marking
	 */
	public void setmarkingOffsetX(double markingOffsetXInput) {
		markingOffsetX = new Double(markingOffsetXInput);
	}
//###################################################################################### 
	/**
	 * Set Y-axis offset for initial marking
	 *
	 * @param  markingOffsetYInput Integer value for Y-axis offset of initial marking
	 */
	public void setmarkingOffsetY(double markingOffsetYInput) {
		markingOffsetY = new Double(markingOffsetYInput);
	}
//###################################################################################### 
	/**
	 * Get initial marking
	 *
	 * @return Integer value for initial marking
	 */
	public int getInitialMarking() {
		if(initialMarking == null)
			return 0;
		return initialMarking.intValue();
	}
//###################################################################################### 
	/**
	 * Get current marking
	 *
	 * @return Integer value for current marking
	 */
	public int getCurrentMarking() {
		if (currentMarking == null)
			return 0;
		else
			return currentMarking.intValue();
	}
//###################################################################################### 
	/**
	 * Get X-axis offset for initial marking
	 *
	 * @return Double value for X-axis offset of initial marking
	 */
	public double getMarkingOffsetX() {
		if(markingOffsetX == null)
			return 0;
		return markingOffsetX.intValue();
	}
//###################################################################################### 
	/**
	 * Get Y-axis offset for initial marking
	 *
	 * @return Double value for X-axis offset of initial marking
	 */
	public double getMarkingOffsetY() {
		if(markingOffsetY == null)
			return 0;
		return markingOffsetY.intValue();
	}
//###################################################################################### 
	/**
	 * Get initial marking
	 *
	 * @return Integer value for initial marking
	 */
	public Integer getInitialMarkingObject() {
		return initialMarking;
	}
//###################################################################################### 
	/**
	 * Get current marking
	 *
	 * @return Integer value for current marking
	 */
	public Integer getCurrentMarkingObject() {
		return currentMarking;
	}
//###################################################################################### 
	/**
	 * Get X-axis offset for initial marking
	 *
	 * @return Double value for X-axis offset of initial marking
	 */
	public Double getMarkingOffsetXObject() {
		return markingOffsetX;
	}
//###################################################################################### 
	/**
	 * Get Y-axis offset for initial marking
	 *
	 * @return Double value for X-axis offset of initial marking
	 */
	public Double getMarkingOffsetYObject() {
		return markingOffsetY;
	}
//###################################################################################### 
	/** Returns the width bounds we want to use when initially creating the place on the gui
	 * @return Width bounds of Place
	 */
	public int boundsWidth() {
		return WIDTH +1;
	}
//###################################################################################### 
	/** Returns the height bounds we want to use when initially creating the place on the gui
	 * @return Height bounds of Place
	 */
	public int boundsHeight() {
		return HEIGHT +1;
	}
//###################################################################################### 
	public boolean contains(int x, int y) {
		someArc = CreateGui.getView().createArc;
		if (someArc != null)			// Must be drawing a new Arc if non-NULL.
		{
			if ((proximityPlace.contains(x-COMPONENT_DRAW_OFFSET, y-COMPONENT_DRAW_OFFSET)
					|| place.contains(x-COMPONENT_DRAW_OFFSET, y-COMPONENT_DRAW_OFFSET))
					&& areNotSameType(someArc.getSource())){
				// assume we are only snapping the target...
        if (someArc.getTarget() != this)
          someArc.setTarget(this);
        someArc.updateArcPosition();
				return true;
			}
			else {
				if (someArc.getTarget() == this) {
					someArc.setTarget(null);
					updateConnected();
				}
				return false;
			}
			
		}
		else
			return place.contains(x-COMPONENT_DRAW_OFFSET, y-COMPONENT_DRAW_OFFSET);
	}
//###################################################################################### 	
	/* (non-Javadoc)
	 * @see pipe.dataLayer.PlaceTransitionObject#updateEndPoint(pipe.dataLayer.Arc)
	 */
	public void updateEndPoint(Arc arc) {
		if (arc.getSource()==this) {
      // Make it calculate the angle from the centre of the place rather than the current start point
      arc.setSourceLocation(this.positionX+DIAMETER/2,this.positionY+DIAMETER/2);
			double angle = arc.getArcPath().getStartAngle();
			arc.setSourceLocation(0.5*DIAMETER*(Math.sin(-angle))+positionX+centreOffsetLeft(),
					  0.5*DIAMETER*(Math.cos(-angle))+positionY+centreOffsetTop());
		} else {
      // Make it calculate the angle from the centre of the place rather than the current target point
      arc.setTargetLocation(this.positionX+DIAMETER/2,this.positionY+DIAMETER/2);
			double angle = arc.getArcPath().getEndAngle();
			arc.setTargetLocation(0.5*DIAMETER*(Math.sin(-angle))+positionX+centreOffsetLeft(),
					  0.5*DIAMETER*(Math.cos(-angle))+positionY+centreOffsetTop());			
		}
	}
//###################################################################################### 
}
//###################################################################################### 