package trafficdefinition;

import classes.Pair;
import classes.Project;
import classes.TypeSelection;
import enums.MotionRestriction;
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 java.util.ArrayList;
import java.util.List;
import shapes.Ellipse2DExt;
import shapes.Point2DExt;
import shapes.Rectangle2DExt;
import simulation.VehicleType;
import tdl.XMLHelpers;

/**
 * Class representing a hotspot. A hotspot can be defined as incoming, outgoing, or both. An incoming hotspot serves as a destination for vehicles that start from anywhere on the map. An outgoing hotspot serves as the source of vehicles that drive to anywhere on the map. If the hotspot is both incoming and outgoing, the vehicles that leave the hotspot will drive back where they came from. The hotspot is drawn on screen using an ellipsoid.
 */
public class HotSpot extends TrafficDefinitionElement implements Serializable {

	/**
	 * Enumeration used to define how the incoming time will be used when creating traffic for the hotspot.
	 */
	public enum DirectionInTimeType {
		/**
		 * Specifies that the time entered by the user is the time that the
		 * vehicles will be arriving to their destination. The emission time of
		 * the vehicles is estimated based on their distance from the
		 * destination.
		 */
		TimeOfArrivalToDestination,

		/**
		 * Specifies that the time entered by the user is the time that the
		 * vehicles will be leaving their source (i.e. their emission time)
		 */
		TimeOfDepartureFromSource
	};

	private static final long serialVersionUID = 1L;

	/**
	 * The ellipsoid defining the shape of the hotspot
	 */
	private Ellipse2DExt area;

	/**
	 * The hotspot's center
	 */
	private Point2DExt center;

	/**
	 * The hotspot's color. All created vehicles and routes will have this
	 * color.
	 */
	private Color color = new Color((float) Math.random(), (float) Math.random(), (float) Math.random());

	/**
	 * Flag to know whether this hotspot is an incoming hotspot
	 */
	private boolean DirectionIn = false;

	/**
	 * The time that vehicles will start coming (or arriving) to the hotspot.
	 * How this value will be used is defined by the value of the
	 * DirectionInType variable.
	 */
	private int directionInBeginTime = 0;

	/**
	 * The time that vehicles will stop coming (or arriving) to the hotspot. How
	 * this value will be used is defined by the value of the DirectionInType
	 * variable.
	 */
	private int directionInEndTime = 100;

	/**
	 * How the incoming time will be used when creating traffic for the hotspot
	 */
	private DirectionInTimeType DirectionInType = DirectionInTimeType.TimeOfDepartureFromSource;

	/**
	 * Flag to know whether this hotspot is an outgoing hotspot
	 */
	private boolean DirectionOut = false;

	/**
	 * The time that vehicles will start leaving from the hotspot.
	 */
	private int directionOutBeginTime = 0;

	/**
	 * The time that vehicles will end leaving from the hotspot.
	 */
	private int directionOutEndTime = 100;

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

	/**
	 * The hotspot's horizontal radius
	 */
	private double radiusx;

	/**
	 * The hotspot's vertical radius
	 */
	private double radiusy;

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

	/**
	 * Default constructor. Assigns a random name to the hotspot and selects
	 * the default vehicle type.
	 * @param center 
	 */
	public HotSpot(Point2D.Double center) {
		super();
		
		name = "Hotspot " + String.valueOf(increment++);

		this.center = new Point2DExt(center);

		area = new Ellipse2DExt();

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

	/**
	 * Class constructor that creates a new hotspot as a copy of the specified hotspot
	 * 
	 * @param source
	 */
	public HotSpot(HotSpot source) {
		super(source);
		
		numberOfVehicles = source.numberOfVehicles;
		color = new Color(source.color.getRGB());
		
		area = new Ellipse2DExt(source.area);
		center = new Point2DExt(source.center);
		radiusx = source.radiusx;
		radiusy = source.radiusy;
		
		DirectionIn = source.DirectionIn;
		directionInBeginTime = source.directionInBeginTime;
		directionInEndTime = source.directionInEndTime;
		
		DirectionInType = source.DirectionInType;
		
		DirectionOut = source.DirectionOut;
		directionOutBeginTime = source.directionOutBeginTime;
		directionOutEndTime = source.directionOutEndTime;
		
		vehicleSelection = new TypeSelection<VehicleType>(source.vehicleSelection);
	}

	@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(new Color(color.getRed(), color.getGreen(), color.getBlue(), 100));
		}

		if (isSelected) {
			g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 200));
		}

		// Draw the ellipsoid
		if (area != null) {
			g.fill(area);
		}

		// Draw the handles
		super.Draw(g, zoomFactor, isDeleted, isSelected);

		g.setColor(prev);
	}

	public Rectangle2DExt getBounds() {
		Rectangle2D bounds = area.getBounds2D();
		
		return new Rectangle2DExt(bounds.getCenterX(),bounds.getCenterY(),bounds.getWidth(),bounds.getHeight());
	}

	/**
	 * Creates the handles that enable UI manipulation of the hotspot
	 */
	public void createHandles() {
		List<Handle> handles = new ArrayList<Handle>();

		Handle centerHandle = new Handle(center, "center", MotionRestriction.None);
		Handle XRadiusHandle = new Handle(new Point2D.Double(center.x + radiusx, center.y), "XRadius", MotionRestriction.Horizontal);
		Handle YRadiusHandle = new Handle(new Point2D.Double(center.x, center.y + radiusy), "YRadius", MotionRestriction.Vertical);

		handles.add(centerHandle);
		handles.add(XRadiusHandle);
		handles.add(YRadiusHandle);

		addHandles(handles);
	}

	/**
	 * @return  the area
	 * @uml.property  name="area"
	 */
	public Ellipse2DExt getArea() {
		return area;
	}

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

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

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

	/**
	 * @return  the directionInType
	 * @uml.property  name="directionInType"
	 */
	public DirectionInTimeType getDirectionInType() {
		return DirectionInType;
	}

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

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

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

	@Override
	public String getToolTip() {
		String tooltip = "<html>" + "<b>HotSpot</b> " + name + "<br>" + "<b>Number of vehicles: </b> " + String.valueOf(numberOfVehicles) + "<br>" + "<b>Direction: </b>";

		if (DirectionIn) {
			tooltip += "Incoming ";
		}

		if (DirectionOut) {
			tooltip += "Outgoing ";
		}

		return tooltip;
	}

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

	public void handleLocationChanged(Handle h) {
		if (h.getName().equals("center")) {
			center = new Point2DExt(h.getLocation());

			handles.get("XRadius").setUnrestrictedLocation(new Point2D.Double(center.x + radiusx, center.y));
			handles.get("YRadius").setUnrestrictedLocation(new Point2D.Double(center.x, center.y + radiusy));
		} else if (h.getName().equals("XRadius")) {
			radiusx = h.getLocation().x - center.x;
		} else if (h.getName().equals("YRadius")) {
			radiusy = h.getLocation().y - center.y;
		}

		area.setFrameFromCenter(center, new Point2D.Double(center.x + radiusx, center.y + radiusy));
	}

	@Override
	public boolean Hit(Point p) {

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

		return false;
	}

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

	/**
	 * @return  the directionIn
	 * @uml.property  name="directionIn"
	 */
	public boolean isDirectionIn() {
		return DirectionIn;
	}

	/**
	 * @return  the directionOut
	 * @uml.property  name="directionOut"
	 */
	public boolean isDirectionOut() {
		return DirectionOut;
	}

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

	/**
	 * @param directionIn  the directionIn to set
	 * @uml.property  name="directionIn"
	 */
	public void setDirectionIn(boolean directionIn) {
		DirectionIn = directionIn;
	}

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

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

	/**
	 * @param directionInType  the directionInType to set
	 * @uml.property  name="directionInType"
	 */
	public void setDirectionInType(DirectionInTimeType directionInType) {
		DirectionInType = directionInType;
	}

	/**
	 * @param directionOut  the directionOut to set
	 * @uml.property  name="directionOut"
	 */
	public void setDirectionOut(boolean directionOut) {
		DirectionOut = directionOut;
	}

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

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

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

	public void setRadius(Point2D.Double p) {
		radiusx = p.x - center.x;
		radiusy = p.y - center.y;

		area.setFrameFromCenter(center, new Point2D.Double(center.x + radiusx, center.y + radiusy));
	}

	/**
	 * @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 HotSpot(this);
	}

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

		sb.append("<hotspot name=\""+name+"\" incoming=\""+String.valueOf(DirectionIn)+"\" incomingTimeType=\""+this.DirectionInType.name()+"\" incomingStartingTime=\""+String.valueOf(directionInBeginTime)+"\" incomingEndingTime=\""+String.valueOf(directionInEndTime)+"\" outgoing=\""+String.valueOf(DirectionOut) +"\" outgoingStartingTime=\""+String.valueOf(directionOutBeginTime)+"\" outgoingEndingTime=\""+String.valueOf(directionOutEndTime)+"\" numberOfVehicles=\""+String.valueOf(numberOfVehicles)+"\">\n");

		sb.append(XMLHelpers.vehicleSelectionToXML("vehicleSelection",vehicleSelection));
		
		sb.append("<areaShape>");
		
		sb.append(XMLHelpers.shapeToXML(area));
		
		sb.append("</areaShape>\n");

		sb.append("</hotspot>\n");
		
		return sb.toString();
	}

}
