/**
  * Processes a finished cloudlet.
  *
  * @param rcl finished cloudlet
  * @pre rgl != $null
  * @post $none
  */
 @Override
 public void cloudletFinish(ResCloudlet rcl) {
   rcl.setCloudletStatus(Cloudlet.SUCCESS);
   rcl.finalizeCloudlet();
   getCloudletFinishedList().add(rcl);
   usedPes -= rcl.getPesNumber();
 }
  /**
   * Updates the processing of cloudlets running under management of this scheduler.
   *
   * @param currentTime current simulation time
   * @param mipsShare array with MIPS share of each processor available to the scheduler
   * @return time predicted completion time of the earliest finishing cloudlet, or 0 if there is no
   *     next events
   * @pre currentTime >= 0
   * @post $none
   */
  @Override
  public double updateVmProcessing(double currentTime, List<Double> mipsShare) {
    setCurrentMipsShare(mipsShare);
    double timeSpam = currentTime - getPreviousTime(); // time since last update
    double capacity = 0.0;
    int cpus = 0;

    for (Double mips : mipsShare) { // count the CPUs available to the VMM
      capacity += mips;
      if (mips > 0) {
        cpus++;
      }
    }
    currentCpus = cpus;
    capacity /= cpus; // average capacity of each cpu

    for (ResCloudlet rcl :
        getCloudletExecList()) { // each machine in the exec list has the same amount of cpu
      rcl.updateCloudletFinishedSoFar((long) (capacity * timeSpam * rcl.getPesNumber()));
    }

    if (getCloudletExecList().size() == 0
        && getCloudletWaitingList().size() == 0) { // no more cloudlets in this scheduler
      setPreviousTime(currentTime);
      return 0.0;
    }

    // update each cloudlet
    int finished = 0;
    int cont = 0;
    List<ResCloudlet> toRemove = new ArrayList<ResCloudlet>();
    for (ResCloudlet rcl : getCloudletExecList()) {
      if (rcl.getRemainingCloudletLength() == 0.0) { // finished anyway, rounding issue...
        toRemove.add(rcl);
        cloudletFinish(rcl);
        finished++;
      }
      cont++;
    }
    getCloudletExecList().removeAll(toRemove);

    // for each finished cloudlet, add a new one from the waiting list
    if (!getCloudletWaitingList().isEmpty()) {
      for (int i = 0; i < finished; i++) {
        toRemove.clear();
        for (ResCloudlet rcl : getCloudletWaitingList()) {
          if ((currentCpus - usedPes) >= rcl.getPesNumber()) {
            rcl.setCloudletStatus(Cloudlet.INEXEC);
            for (int k = 0; k < rcl.getPesNumber(); k++) {
              rcl.setMachineAndPeId(0, i);
            }
            getCloudletExecList().add(rcl);
            usedPes += rcl.getPesNumber();
            toRemove.add(rcl);
            break;
          }
        }
        getCloudletWaitingList().removeAll(toRemove);
      } // for(cont)
    }

    // estimate finish time of cloudlets in the execution queue
    double nextEvent = Double.MAX_VALUE;
    for (ResCloudlet rcl : getCloudletExecList()) {
      double remainingLength = rcl.getRemainingCloudletLength();
      double estimatedFinishTime =
          currentTime + (remainingLength / (capacity * rcl.getPesNumber()));
      if (estimatedFinishTime - currentTime < 0.1) {
        estimatedFinishTime = currentTime + 0.1;
      }
      if (estimatedFinishTime < nextEvent) {
        nextEvent = estimatedFinishTime;
      }
    }
    setPreviousTime(currentTime);
    return nextEvent;
  }
  /**
   * Resumes execution of a paused cloudlet.
   *
   * @param cloudletId ID of the cloudlet being resumed
   * @return $true if the cloudlet was resumed, $false otherwise
   * @pre $none
   * @post $none
   */
  @Override
  public double cloudletResume(int cloudletId) {
    boolean found = false;
    int position = 0;

    // look for the cloudlet in the paused list
    for (ResCloudlet rcl : getCloudletPausedList()) {
      if (rcl.getCloudletId() == cloudletId) {
        found = true;
        break;
      }
      position++;
    }

    if (found) {
      ResCloudlet rcl = getCloudletPausedList().remove(position);

      if ((currentCpus - usedPes) >= rcl.getPesNumber()) { // it can go to the exec list
        rcl.setCloudletStatus(Cloudlet.INEXEC);
        for (int i = 0; i < rcl.getPesNumber(); i++) {
          rcl.setMachineAndPeId(0, i);
        }

        long size = rcl.getRemainingCloudletLength();
        size *= rcl.getPesNumber();
        rcl.getCloudlet().setCloudletLength(size);

        getCloudletExecList().add(rcl);
        usedPes += rcl.getPesNumber();

        // calculate the expected time for cloudlet completion
        double capacity = 0.0;
        int cpus = 0;
        for (Double mips : getCurrentMipsShare()) {
          capacity += mips;
          if (mips > 0) {
            cpus++;
          }
        }
        currentCpus = cpus;
        capacity /= cpus;

        long remainingLength = rcl.getRemainingCloudletLength();
        double estimatedFinishTime =
            CloudSim.clock() + (remainingLength / (capacity * rcl.getPesNumber()));

        return estimatedFinishTime;
      } else { // no enough free PEs: go to the waiting queue
        rcl.setCloudletStatus(Cloudlet.QUEUED);

        long size = rcl.getRemainingCloudletLength();
        size *= rcl.getPesNumber();
        rcl.getCloudlet().setCloudletLength(size);

        getCloudletWaitingList().add(rcl);
        return 0.0;
      }
    }

    // not found in the paused list: either it is in in the queue, executing or not exist
    return 0.0;
  }