package trafficdefinition;

import classes.Pair;
import classes.Project;
import classes.TypeSelection;
import helpers.DrawingHelper;
import helpers.TimeConverter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import roadnetwork.Edge;
import shapes.Line2DExt;
import shapes.Point2DExt;
import shapes.Rectangle2DExt;
import simulation.VehicleType;

/**
 * Class representing a flow of vehicles from one edge to another. The flow is drawn on screen using an arrow from the source edge to the destination edge. Vehicles will be emitted at random points in time between beginTime and endTime.
 */
public class Flow extends TrafficDefinitionElement implements Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * The time that vehicles will start being emitted
	 */
	private int beginTime = 0;

	/**
	 * The time that vehicles will end being emitted
	 */
	private int endTime = 100;

	/**
	 * The number of vehicles to emit
	 */
	private int numberOfVehicles = 100;

	/**
	 * The flow's color. All created vehicles and routes will have this color.
	 */
	private Color color = Color.BLUE;

	/**
	 * The edge from which the vehicles will start
	 */
	private Edge start = null;

	/**
	 * The edge to which the vehicles will end
	 */
	private Edge end = null;

	/**
	 * The selection of the vehicle types that will be created for this area flow.
	 * @uml.property  name="vehicleSelection"
	 * @uml.associationEnd  
	 */
	private TypeSelection<VehicleType> vehicleSelection;

	/**
	 * The line used to draw the flow
	 */
	private Line2DExt shape;

	/**
	 * The point from which the line starts. This is the starting edge's center.
	 */
	private Point2DExt from;

	/**
	 * Default constructor. Assigns a random name to area flow and selects the
	 * default vehicle type.
	 * 
	 */
	public Flow() {
		super();
		
		name = "Flow " + String.valueOf(increment++);

		vehicleSelection = new TypeSelection<VehicleType>();
		vehicleSelection.add(new Pair<VehicleType, Float>(Project.getCurrentlyLoadedProject().getDefaultVehicleType(), 1.f));
	}

	/**
	 * Class constructor that creates a new flow as a copy of the specified flow
	 * 
	 * @param source
	 */
	public Flow(Flow source) {
		super(source);
		
		beginTime = source.beginTime;
		endTime = source.endTime;
		numberOfVehicles = source.numberOfVehicles;
		color = new Color(source.color.getRGB());

		start = source.start;
		end = source.end;
		
		shape = new Line2DExt(source.shape);
		from = new Point2DExt(source.from);

		vehicleSelection = new TypeSelection<VehicleType>(source.vehicleSelection);

		SetDependencies();
	}

	@Override
	public void Draw(Graphics2D g, double zoomFactor, boolean isDeleted, boolean isSelected) {
		// Save the previous color to restore it later
		Color prev = g.getColor();

		if (!isSelected) {
			g.setColor(color);
		}

		if (isSelected) {
			g.setColor(Color.GREEN);
		}

		// Draw the direction arrow
		if (shape != null) {

			DrawingHelper.drawArrowHead(g, shape.x1, shape.y1, shape.x2, shape.y2, 1.f);

			g.draw(shape);
		}

		g.setColor(prev);
	}

	public Rectangle2DExt getBounds() {
		return new Rectangle2DExt((Rectangle2D.Double)shape.getBounds2D());
	}

	/**
	 * @return  the beginTime
	 * @uml.property  name="beginTime"
	 */
	public int getBeginTime() {
		return beginTime;
	}

	/**
	 * @return  the color
	 * @uml.property  name="color"
	 */
	public Color getColor() {
		return color;
	}

	/**
	 * @return  the end
	 * @uml.property  name="end"
	 */
	public Edge getEnd() {
		return end;
	}

	/**
	 * @return  the endTime
	 * @uml.property  name="endTime"
	 */
	public int getEndTime() {
		return endTime;
	}

	/**
	 * @return  the numberOfVehicles
	 * @uml.property  name="numberOfVehicles"
	 */
	public int getNumberOfVehicles() {
		return numberOfVehicles;
	}

	/**
	 * @return  the start
	 * @uml.property  name="start"
	 */
	public Edge getStart() {
		return start;
	}

	@Override
	public String getToolTip() {
		return "<html>" + "<b>Flow</b> " + name + "<br>" + "<b>From:</b> " + start.getId() + "<br>" + "<b>To:</b> " + end.getId() + "<br>" + "<b>Number of vehicles:</b> " + numberOfVehicles + "<br>" + "<b>Start time:</b> " + TimeConverter.toString(beginTime) + "<br>" + "<b>End time:</b> " + TimeConverter.toString(endTime) + "</html>";
	}

	/**
	 * @return  the vehicleSelection
	 * @uml.property  name="vehicleSelection"
	 */
	public TypeSelection<VehicleType> getVehicleSelection() {
		return vehicleSelection;
	}

	public void handleLocationChanged(Handle h) {
		// No handles for flows
	}

	@Override
	public boolean Hit(Point p) {

		if (shape.getBounds2D().contains(p.x, p.y)) {
			if (shape.contains(p.x, p.y)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public boolean Hit(Rectangle transformedRectangle) {
		return shape.intersects(transformedRectangle);
	}

	/**
	 * @param beginTime  the beginTime to set
	 * @uml.property  name="beginTime"
	 */
	public void setBeginTime(int beginTime) {
		this.beginTime = beginTime;
	}

	/**
	 * @param color  the color to set
	 * @uml.property  name="color"
	 */
	public void setColor(Color color) {
		this.color = color;
	}

	/**
	 * Adds the dependencies to the starting and ending edges so that they
	 * cannot be deleted.
	 * 
	 */
	public void SetDependencies() {
		start.AddDependentTrafficElement(this);
		end.AddDependentTrafficElement(this);
	}

	/**
	 * Removes the dependencies from the starting and ending edges. This method
	 * is called before the flow is deleted.
	 * 
	 */
	public void ReleaseDependencies() {
		start.RemoveDependentTrafficElement(this);
		end.RemoveDependentTrafficElement(this);
	}

	/**
	 * @param end  the end to set
	 * @uml.property  name="end"
	 */
	public void setEnd(Edge end) {
		this.end = end;
	}

	/**
	 * @param endTime  the endTime to set
	 * @uml.property  name="endTime"
	 */
	public void setEndTime(int endTime) {
		this.endTime = endTime;
	}

	public void setFrom(Point2D.Float from) {
		this.from = new Point2DExt(from.x, from.y);
	}

	/**
	 * @param numberOfVehicles  the numberOfVehicles to set
	 * @uml.property  name="numberOfVehicles"
	 */
	public void setNumberOfVehicles(int numberOfVehicles) {
		this.numberOfVehicles = numberOfVehicles;
	}

	/**
	 * @param start  the start to set
	 * @uml.property  name="start"
	 */
	public void setStart(Edge start) {
		this.start = start;
	}

	public void setTo(Point2D.Double to) {
		shape = new Line2DExt(from, to);
	}

	/**
	 * @param vehicleSelection  the vehicleSelection to set
	 * @uml.property  name="vehicleSelection"
	 */
	public void setVehicleSelection(TypeSelection<VehicleType> vehicleSelection) {
		this.vehicleSelection = vehicleSelection;
	}

	@Override
	public TrafficDefinitionElement elementClone() {
		return new Flow(this);
	}

	@Override
	public String toXML() {
		StringBuilder sb = new StringBuilder();

		sb.append("<flow name=\""+name+"\" from=\""+start.getId()+"\" to=\""+end.getId()+"\" startingTime=\""+String.valueOf(beginTime)+"\" endingTime=\""+String.valueOf(endTime)+"\" numberOfVehicles=\""+String.valueOf(numberOfVehicles)+"\">\n");
		
		sb.append("<vehicleSelection>\n");
		
		for(Pair<VehicleType,Float> vs:vehicleSelection){
			sb.append("<selectedVehicle vehicleType=\""+ vs.getFirst().getName()+"\" probability=\""+vs.getSecond().toString()+"\"/>\n");			
		}
		
		sb.append("</vehicleSelection>\n");
		
		sb.append("</flow>\n");
		
		return sb.toString();
	}
}
