/**
  * 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.getNumberOfPes();
 }
 /**
  * Returns one cloudlet to migrate to another vm.
  *
  * @return one running cloudlet
  * @pre $none
  * @post $none
  */
 @Override
 public Cloudlet migrateCloudlet() {
   ResCloudlet rcl = getCloudletExecList().remove(0);
   rcl.finalizeCloudlet();
   Cloudlet cl = rcl.getCloudlet();
   usedPes -= cl.getNumberOfPes();
   return cl;
 }
 /**
  * Get utilization created by all cloudlets.
  *
  * @param time the time
  * @return total utilization
  */
 @Override
 public double getTotalUtilizationOfCpu(double time) {
   double totalUtilization = 0;
   for (ResCloudlet gl : getCloudletExecList()) {
     totalUtilization += gl.getCloudlet().getUtilizationOfCpu(time);
   }
   return totalUtilization;
 }
  /**
   * Pauses execution of a cloudlet.
   *
   * @param cloudletId ID of the cloudlet being paused
   * @return $true if cloudlet paused, $false otherwise
   * @pre $none
   * @post $none
   */
  @Override
  public boolean cloudletPause(int cloudletId) {
    boolean found = false;
    int position = 0;

    // first, looks for the cloudlet in the exec list
    for (ResCloudlet rcl : getCloudletExecList()) {
      if (rcl.getCloudletId() == cloudletId) {
        found = true;
        break;
      }
      position++;
    }

    if (found) {
      // moves to the paused list
      ResCloudlet rgl = getCloudletExecList().remove(position);
      if (rgl.getRemainingCloudletLength() == 0) {
        cloudletFinish(rgl);
      } else {
        rgl.setCloudletStatus(Cloudlet.PAUSED);
        getCloudletPausedList().add(rgl);
      }
      return true;
    }

    // now, look for the cloudlet in the waiting list
    position = 0;
    found = false;
    for (ResCloudlet rcl : getCloudletWaitingList()) {
      if (rcl.getCloudletId() == cloudletId) {
        found = true;
        break;
      }
      position++;
    }

    if (found) {
      // moves to the paused list
      ResCloudlet rgl = getCloudletWaitingList().remove(position);
      if (rgl.getRemainingCloudletLength() == 0) {
        cloudletFinish(rgl);
      } else {
        rgl.setCloudletStatus(Cloudlet.PAUSED);
        getCloudletPausedList().add(rgl);
      }
      return true;
    }

    return false;
  }
  /**
   * Receives an cloudlet to be executed in the VM managed by this scheduler.
   *
   * @param cloudlet the submited cloudlet
   * @param fileTransferTime time required to move the required files from the SAN to the VM
   * @return expected finish time of this cloudlet, or 0 if it is in the waiting queue
   * @pre gl != null
   * @post $none
   */
  @Override
  public double cloudletSubmit(Cloudlet cloudlet, double fileTransferTime) {
    // it can go to the exec list
    if ((currentCpus - usedPes) >= cloudlet.getNumberOfPes()) {
      ResCloudlet rcl = new ResCloudlet(cloudlet);
      rcl.setCloudletStatus(Cloudlet.INEXEC);
      for (int i = 0; i < cloudlet.getNumberOfPes(); i++) {
        rcl.setMachineAndPeId(0, i);
      }
      getCloudletExecList().add(rcl);
      usedPes += cloudlet.getNumberOfPes();
    } else { // no enough free PEs: go to the waiting queue
      ResCloudlet rcl = new ResCloudlet(cloudlet);
      rcl.setCloudletStatus(Cloudlet.QUEUED);
      getCloudletWaitingList().add(rcl);
      return 0.0;
    }

    // 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;

    // use the current capacity to estimate the extra amount of
    // time to file transferring. It must be added to the cloudlet length
    double extraSize = capacity * fileTransferTime;
    long length = cloudlet.getCloudletLength();
    length += extraSize;
    cloudlet.setCloudletLength(length);
    return cloudlet.getCloudletLength() / capacity;
  }
  /**
   * Gets the status of a cloudlet.
   *
   * @param cloudletId ID of the cloudlet
   * @return status of the cloudlet, -1 if cloudlet not found
   * @pre $none
   * @post $none
   */
  @Override
  public int getCloudletStatus(int cloudletId) {
    for (ResCloudlet rcl : getCloudletExecList()) {
      if (rcl.getCloudletId() == cloudletId) {
        return rcl.getCloudletStatus();
      }
    }

    for (ResCloudlet rcl : getCloudletPausedList()) {
      if (rcl.getCloudletId() == cloudletId) {
        return rcl.getCloudletStatus();
      }
    }

    for (ResCloudlet rcl : getCloudletWaitingList()) {
      if (rcl.getCloudletId() == cloudletId) {
        return rcl.getCloudletStatus();
      }
    }

    return -1;
  }
  /**
   * 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

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

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

    // update each cloudlet
    int finished = 0;
    List<ResCloudlet> toRemove = new ArrayList<ResCloudlet>();
    for (ResCloudlet rcl : getCloudletExecList()) {
      // finished anyway, rounding issue...
      if (rcl.getRemainingCloudletLength() == 0) {
        toRemove.add(rcl);
        cloudletFinish(rcl);
        finished++;
      }
    }
    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.getNumberOfPes()) {
            rcl.setCloudletStatus(Cloudlet.INEXEC);
            for (int k = 0; k < rcl.getNumberOfPes(); k++) {
              rcl.setMachineAndPeId(0, i);
            }
            getCloudletExecList().add(rcl);
            usedPes += rcl.getNumberOfPes();
            toRemove.add(rcl);
            break;
          }
        }
        getCloudletWaitingList().removeAll(toRemove);
      }
    }

    // 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.getNumberOfPes()));
      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);

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

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

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

        // 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.getNumberOfPes()));

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

        long size = rcl.getRemainingCloudletLength();
        size *= rcl.getNumberOfPes();
        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;
  }
  /**
   * Cancels execution of a cloudlet.
   *
   * @param cloudletId ID of the cloudlet being cancealed
   * @return the canceled cloudlet, $null if not found
   * @pre $none
   * @post $none
   */
  @Override
  public Cloudlet cloudletCancel(int cloudletId) {
    // First, looks in the finished queue
    for (ResCloudlet rcl : getCloudletFinishedList()) {
      if (rcl.getCloudletId() == cloudletId) {
        getCloudletFinishedList().remove(rcl);
        return rcl.getCloudlet();
      }
    }

    // Then searches in the exec list
    for (ResCloudlet rcl : getCloudletExecList()) {
      if (rcl.getCloudletId() == cloudletId) {
        getCloudletExecList().remove(rcl);
        if (rcl.getRemainingCloudletLength() == 0) {
          cloudletFinish(rcl);
        } else {
          rcl.setCloudletStatus(Cloudlet.CANCELED);
        }
        return rcl.getCloudlet();
      }
    }

    // Now, looks in the paused queue
    for (ResCloudlet rcl : getCloudletPausedList()) {
      if (rcl.getCloudletId() == cloudletId) {
        getCloudletPausedList().remove(rcl);
        return rcl.getCloudlet();
      }
    }

    // Finally, looks in the waiting list
    for (ResCloudlet rcl : getCloudletWaitingList()) {
      if (rcl.getCloudletId() == cloudletId) {
        rcl.setCloudletStatus(Cloudlet.CANCELED);
        getCloudletWaitingList().remove(rcl);
        return rcl.getCloudlet();
      }
    }

    return null;
  }