/*
                             Timesharing Computer
                       Using Discrete State Simulation

                              CS509 Assignment 2
                                 Winter 1995
                                 G. Back

*/


import sim.*;
import queues.*;

////////////////////////////////////////////////////////////////////////////
/////	CPU class
////////////////////////////////////////////////////////////////////////////

class CPU {

    //	2 queues
    static Queue  idle_CPUs = new Queue();
    static Queue  waiting_jobs = new Queue();

    String 	name;
    int 	time_slice;	// time slice (0 denotes infinity)
    int		busy_time;	// absolute busy time
    int		const_time;	// time of construction
 
    // create a new CPU
    CPU(String n, int ts) 
    {
	name = n;
	time_slice = ts;
	const_time = scheduler.clock;
	busy_time = 0;

	// and see if we can help someone
	if( !waiting_jobs.IsEmpty() ) {
	    job	next = (job)waiting_jobs.Dequeue();
	    next.running_on_CPU = this;
	    scheduler.activate( next );
	} else {
	    // otherwise put CPU in queue of idle CPUs
	    idle_CPUs.Enqueue(this);
	}
    }

    void print() 
    {
	System.out.println(name + ": duty cycle  " +
		busy_time / (double)(scheduler.clock-const_time));
    }

    // destroy CPU, remove it from the queue of idling CPUs
    // ASSUMES THAT CPU IS IDLE !!!
    void remove_cpu() 
    {
	idle_CPUs.Remove(this);
        System.out.print("Time " + scheduler.clock + ": deleted ");
        print();
    }
};

////////////////////////////////////////////////////////////////////////////
/////	job class
////////////////////////////////////////////////////////////////////////////

abstract class job extends process {

    int	CPU_wait_time;
    CPU running_on_CPU;

    job(String name) 
    {
	super(name);
	CPU_wait_time = 0;
	running_on_CPU = null;
    }

    // former destructor
    void remove_job()
    {
	System.out.println("Time " + scheduler.clock + ": " + name +
		" deleted; total CPU wait time "+CPU_wait_time); 
    }

    // run for a certain amount of time
    void run(int left)
    {
	while(left > 0) {
	    // see if I have to help myself
	    if (!CPU.idle_CPUs.IsEmpty() && CPU.waiting_jobs.IsEmpty()) {
		running_on_CPU = (CPU)CPU.idle_CPUs.Dequeue();
	    }

	    // if I couldn't help myself and nobody has already given 
	    // a CPU to me, I have to swallow the pill and wait
	    if (running_on_CPU == null) {
		// store the time when we started waiting
		CPU_wait_time -= scheduler.clock;
		// wait for a CPU
		waitq( CPU.waiting_jobs );
		// update waiting time
		CPU_wait_time += scheduler.clock;
	    }
	    // make sure I finally got one
	    if( running_on_CPU == null) {
		scheduler.fatal_error("Didn't get a CPU");
	    }

	    int  	run_for;
	    if (running_on_CPU.time_slice == 0 || 
		left <= running_on_CPU.time_slice) 
		// case I: can run to completion
		run_for = left;
	    else 
		// case II: get only one timeslice
		run_for = running_on_CPU.time_slice;

	    System.out.println("Time " + scheduler.clock +
				": " + name + " begins running on " + 
			        running_on_CPU.name);
	    scheduler.hold( run_for );	
		    
	    System.out.println("Time " + scheduler.clock +
				": " + name + " stops running on " + 
			        running_on_CPU.name);
	    running_on_CPU.busy_time += run_for;
	    left -= run_for;

	    // put CPU in queue of idle CPUs
	    CPU.idle_CPUs.Enqueue(running_on_CPU);
	    running_on_CPU = null;		

	    // see if I must provide someone with a CPU 
	    if (!CPU.waiting_jobs.IsEmpty()) {
		job next = (job)CPU.waiting_jobs.Dequeue();
		next.running_on_CPU = (CPU)CPU.idle_CPUs.Dequeue();
		scheduler.activate( next );
	    }
	} // end while(left > 0) 
    }

    // print job name and CPU it is running on, if any
    public void print()
    {
	if (running_on_CPU != null) {
	    System.out.print(name + " running on ");
	    running_on_CPU.print(); 
	} else 
	    System.out.println(name + " not running");
    }
}


