/**
  * Builds and returns the vehicle.
  *
  * <p>if {@link VehicleType} is not set, default vehicle-type is set with id="default" and
  * capacity=0
  *
  * <p>if startLocationId || locationId is null (=> startLocationCoordinate || locationCoordinate
  * must be set) then startLocationId=startLocationCoordinate.toString() and
  * locationId=locationCoordinate.toString() [coord.toString() --> [x=x_val][y=y_val])
  *
  * <p>if endLocationId is null and endLocationCoordinate is set then
  * endLocationId=endLocationCoordinate.toString()
  *
  * <p>if endLocationId==null AND endLocationCoordinate==null then endLocationId=startLocationId
  * AND endLocationCoord=startLocationCoord Thus endLocationId can never be null even
  * returnToDepot is false.
  *
  * @return vehicle
  * @throws IllegalStateException if both locationId and locationCoord is not set or
  *     (endLocationCoord!=null AND returnToDepot=false) or (endLocationId!=null AND
  *     returnToDepot=false)
  */
 public VehicleImpl build() {
   if (startLocation != null && endLocation != null) {
     if (!startLocation.getId().equals(endLocation.getId()) && !returnToDepot)
       throw new IllegalStateException(
           "this must not be. you specified both endLocationId and open-routes. this is contradictory. <br>"
               + "if you set endLocation, returnToDepot must be true. if returnToDepot is false, endLocationCoord must not be specified.");
   }
   if (startLocation != null && endLocation == null) {
     endLocation = startLocation;
   }
   if (startLocation == null && endLocation == null) {
     throw new IllegalStateException(
         "vehicle requires startLocation. but neither locationId nor locationCoord nor startLocationId nor startLocationCoord has been set");
   }
   skills = skillBuilder.build();
   return new VehicleImpl(this);
 }
  /**
   * Builder that builds the vehicle.
   *
   * <p>By default, earliestDepartureTime is 0.0, latestDepartureTime is Double.MAX_VALUE, it
   * returns to the depot and its {@link VehicleType} is the DefaultType with typeId equal to
   * 'default' and a capacity of 0.
   *
   * @author stefan
   */
  public static class Builder {

    static final Logger log = LogManager.getLogger(Builder.class.getName());

    private String id;

    private double earliestStart = 0.0;

    private double latestArrival = Double.MAX_VALUE;

    private boolean returnToDepot = true;

    private VehicleType type = VehicleTypeImpl.Builder.newInstance("default").build();

    private Skills.Builder skillBuilder = Skills.Builder.newInstance();

    private Skills skills;

    private Location startLocation;

    private Location endLocation;

    private Builder(String id) {
      super();
      this.id = id;
    }

    /**
     * Sets the {@link VehicleType}.<br>
     *
     * @param type the type to be set
     * @throws IllegalStateException if type is null
     * @return this builder
     */
    public Builder setType(VehicleType type) {
      if (type == null) throw new IllegalStateException("type cannot be null.");
      this.type = type;
      return this;
    }

    /**
     * Sets the flag whether the vehicle must return to depot or not.
     *
     * <p>If returnToDepot is true, the vehicle must return to specified end-location. If you omit
     * specifying the end-location, vehicle returns to start-location (that must to be set). If you
     * specify it, it returns to specified end-location.
     *
     * <p>If returnToDepot is false, the end-location of the vehicle is endogenous.
     *
     * @param returnToDepot true if vehicle need to return to depot, otherwise false
     * @return this builder
     */
    public Builder setReturnToDepot(boolean returnToDepot) {
      this.returnToDepot = returnToDepot;
      return this;
    }

    /**
     * Sets start location.
     *
     * @param startLocation start location
     * @return start location
     */
    public Builder setStartLocation(Location startLocation) {
      this.startLocation = startLocation;
      return this;
    }

    public Builder setEndLocation(Location endLocation) {
      this.endLocation = endLocation;
      return this;
    }

    /**
     * Sets earliest-start of vehicle which should be the lower bound of the vehicle's departure
     * times.
     *
     * @param earliest_startTime the earliest start time / departure time of the vehicle at its
     *     start location
     * @return this builder
     */
    public Builder setEarliestStart(double earliest_startTime) {
      this.earliestStart = earliest_startTime;
      return this;
    }

    /**
     * Sets the latest arrival at vehicle's end-location which is the upper bound of the vehicle's
     * arrival times.
     *
     * @param latest_arrTime the latest arrival time of the vehicle at its end location
     * @return this builder
     */
    public Builder setLatestArrival(double latest_arrTime) {
      this.latestArrival = latest_arrTime;
      return this;
    }

    public Builder addSkill(String skill) {
      skillBuilder.addSkill(skill);
      return this;
    }

    /**
     * Builds and returns the vehicle.
     *
     * <p>if {@link VehicleType} is not set, default vehicle-type is set with id="default" and
     * capacity=0
     *
     * <p>if startLocationId || locationId is null (=> startLocationCoordinate || locationCoordinate
     * must be set) then startLocationId=startLocationCoordinate.toString() and
     * locationId=locationCoordinate.toString() [coord.toString() --> [x=x_val][y=y_val])
     *
     * <p>if endLocationId is null and endLocationCoordinate is set then
     * endLocationId=endLocationCoordinate.toString()
     *
     * <p>if endLocationId==null AND endLocationCoordinate==null then endLocationId=startLocationId
     * AND endLocationCoord=startLocationCoord Thus endLocationId can never be null even
     * returnToDepot is false.
     *
     * @return vehicle
     * @throws IllegalStateException if both locationId and locationCoord is not set or
     *     (endLocationCoord!=null AND returnToDepot=false) or (endLocationId!=null AND
     *     returnToDepot=false)
     */
    public VehicleImpl build() {
      if (startLocation != null && endLocation != null) {
        if (!startLocation.getId().equals(endLocation.getId()) && !returnToDepot)
          throw new IllegalStateException(
              "this must not be. you specified both endLocationId and open-routes. this is contradictory. <br>"
                  + "if you set endLocation, returnToDepot must be true. if returnToDepot is false, endLocationCoord must not be specified.");
      }
      if (startLocation != null && endLocation == null) {
        endLocation = startLocation;
      }
      if (startLocation == null && endLocation == null) {
        throw new IllegalStateException(
            "vehicle requires startLocation. but neither locationId nor locationCoord nor startLocationId nor startLocationCoord has been set");
      }
      skills = skillBuilder.build();
      return new VehicleImpl(this);
    }

    /**
     * Returns new instance of vehicle builder.
     *
     * @param vehicleId the id of the vehicle which must be a unique identifier among all vehicles
     * @return vehicle builder
     */
    public static Builder newInstance(String vehicleId) {
      return new Builder(vehicleId);
    }

    public Builder addSkills(Skills skills) {
      this.skillBuilder.addAllSkills(skills.values());
      return this;
    }
  }
 public Builder addSkill(String skill) {
   skillBuilder.addSkill(skill);
   return this;
 }