// Guide to Data Structures
// Section 3.5
// Copyright 2018, by J.T. Streib and T. Soma

import java.util.*;

public class RoundRobin {
private static Scanner scanner = new Scanner(System.in);
private static QueueArrayGeneric<Job> jobQueue
              = new QueueArrayGeneric<Job>();
private static int quantum;
       
public static void main(String[] args) {
 setJobs();
 runScheduler();
}

// obtains information from a user about jobs to be run in CPU
public static void setJobs() {
 Job job;
 int jobNum, cpuTime;

 System.out.print("Enter number of jobs: ");
 jobNum = scanner.nextInt();
 System.out.print("Enter size of quantum: ");
 quantum = scanner.nextInt();
 System.out.println();
   
 for(int i=0; i<jobNum; i++) {
    System.out.print("Enter CPU time for job " + i + ": ");
    cpuTime = scanner.nextInt();
    job = new Job(i, cpuTime);
    jobQueue.enqueue(job);
 }
 System.out.println();
}

// simulates the round-robin scheduler
public static void runScheduler() {
 Job job;
 int cpuTime, accumulatedCpuTime;
 accumulatedCpuTime = 0;
   
 while(!jobQueue.empty()) {
    // front of the queue goes to the CPU
    job = jobQueue.dequeue();
    cpuTime = job.getCpuTime();

    // quantum expires before job finishes
    if(cpuTime - quantum > 0) {
       accumulatedCpuTime = accumulatedCpuTime + quantum;
       // update cpuTime of job and put it back in the queue
       job.setCpuTime(cpuTime - quantum);
       jobQueue.enqueue(job);
    }
    // job finishes before time quantum
    else {
       accumulatedCpuTime = accumulatedCpuTime + cpuTime;            
       // job is done, output a message
       System.out.println("Job " + job.getJobNum()
                      + " finished at " + accumulatedCpuTime);
    }
 }
}

}

public class QueueArrayGeneric<T> {
private final int N = 5;
private int front, rear, count;
private T[] qarray;

public QueueArrayGeneric() {
 front = rear = count = 0;
 qarray = (T[]) new Object[N];
}

public T dequeue() {
 T item = null;
 if(empty())
    System.out.println("Queue is empty, item not removed");
 else {
    item = qarray[front];
    front = (front + 1) % N;
    count--;
 }
 return item;
}

public void enqueue(T item) {
 if(full())
    System.out.println("Queue is full, item not added");
 else {
    qarray[rear] = item;
    rear = (rear + 1) % N;
    count++;
 }
}

public boolean empty() {
 return count <= 0;
}

public boolean full() {
 return count >= N;
}

public int getCount() {
 return count;    
}
}


public class Job {
private int jobNum, cpuTime;

// constructors
public Job() {
 this(0, 0);
}

public Job(int jobNum, int cpuTime) {
 this.jobNum = jobNum;
 this.cpuTime = cpuTime;
}

// returns the jobNum;
public int getJobNum() {
 return jobNum;    
}

// returns the cpuTime;
public int getCpuTime() {
 return cpuTime;    
}

// sets the cpuTime;
public void setCpuTime(int cpuTime) {
 this.cpuTime = cpuTime;       
}
}