//----------------------------------------------------------------------
// IMPLEMENTATION FILE (scheduler.cpp) 
//----------------------------------------------------------------------
#include "bool.h"  
#include "scheduler.h" 

Scheduler::Scheduler(char *p1, char *p2, char *p3)
{       //Create a PCB for each process and queue them up

	Process *PPtr;          //Process object Pointer

	Current = NULL;

	PPtr = new Process(p1);         
	NewPQ.Enqueue(PPtr);

	if(DEBUG == TRUE)
		PPtr->Display();

	PPtr = new Process(p2);
	NewPQ.Enqueue(PPtr);
        if(DEBUG == TRUE)
                PPtr->Display();

	PPtr = new Process(p3);
	NewPQ.Enqueue(PPtr);
        if(DEBUG == TRUE)
                PPtr->Display();

	NumPending = 3;
	
} //End constructor


void Scheduler::CheckNewP(int time)
{
	//Check for newly created processes.  If any are found,
	//Put them in the correct Q

	Process *PPtr;
	int   	loopcnt;

	loopcnt = NewPQ.getCount();
	while(loopcnt > 0)
	{
		PPtr = NewPQ.Dequeue();         
		//test to see if it has been created yet
		if( PPtr->getCreateTime() <= time )
		{       //it has, enqueue it
			Enqueue(PPtr, time);

		        if(DEBUG == TRUE)
                		cout << "Process Created: PID: " << PPtr->getPID() << endl;
		}
		else
		{       //it hasn't
			NewPQ.Enqueue(PPtr);
		}           

		loopcnt--;    

	} //end while
}

Boolean Scheduler::Manage(SchedType sctype, int time)
{
	//Select a process to use the CPU this timeslice
	switch(sctype)
	{
		case RR:
			//cout << "Round Robin Scheduling requested";
			DoRR(time);
			break;
		case FCFS:
			//cout << "FCFS Scheduling Requested";
			DoFCFS(time);
			break;
		case Priority:
			//cout << "Priority Scheduling Requested";
			DoPriority(time);
			break;
		default:
			cout << "Unknown Scheduling Algorithm Requested";
			break;
	}

	if(DEBUG == TRUE)
                cout << "NumPending: " << NumPending << endl;

        return( NumPending > 0 );
}


void Scheduler::ServiceRQ(int time)
{	Process *PPtr;                  //Need a temp Process Pointer
	int	loopcnt;

        //Update statistics on Processes in ReadyQ
        if(DEBUG == TRUE)
                cout << "Process ReadyQ:\n";

	loopcnt = ReadyQ.getCount();
	while(loopcnt > 0)
	{
        	PPtr = ReadyQ.Dequeue();
        	if(DEBUG == TRUE)
                	cout << "Looking at: " << PPtr->getPID() << "\n";
		(void) PPtr->Age(InRQ);
        	if(DEBUG == TRUE)
                	cout << "Replacing: " << PPtr->getPID() << "\n";
		Enqueue(PPtr, time);

		loopcnt--;
	}//end while
}

void Scheduler::ServiceWQ(int time)
{       Process *PPtr;                  //Need a temp Process Pointer
	int loopcnt;

        //Service the processes in the WaitingQ
        if(DEBUG == TRUE)
                cout << "Process WaitingQ:\n";
	
	loopcnt = WaitingQ.getCount();
	while(loopcnt > 0)
        {
		PPtr = WaitingQ.Dequeue();
        	if(DEBUG == TRUE)
                	cout << "Looking at: " << PPtr->getPID() << "\n";
                (void) PPtr->Age(InWQ);
	        if(DEBUG == TRUE)
                	cout << "Replacing: " << PPtr->getPID() << "\n";
		Enqueue(PPtr, time);
		
		loopcnt--;
	}//end while
}

void Scheduler::DoRR(int time)
{	EventType eventtype, oldevent;
	static int TScount = 0;

	if(DEBUG == TRUE)
		if(Current != NULL)
		{
			cout << "Current: " << Current->getPID() << "\n";
			cout << "Time:	  " << time << "\n";
			cout << "Event:   " << Current->getCurEvent() <<"\n";
			cout << "TScount: " << TScount << "\n";
		}
		else cout << "No Current Process.\n";

	//If there is no current process, find one.
	if(Current == NULL)
        {
                //Get the next process that is ready
                if( ReadyQ.IsEmpty() == FALSE )
		{
                        Current = ReadyQ.Dequeue();
        		if(DEBUG == TRUE)
                		cout << "New Current = " << Current->getPID() << "\n";
		}
                else  //No processes ready.   
                        cout << "No proc ready at Time =" << time << "\n";
	}
	//Service the ReadyQ
	ServiceRQ(time);

	//Service the WaitingQ
	ServiceWQ(time);

        //Service the Current process on the CPU
        if(Current != NULL)
        {
        	if(DEBUG == TRUE)
                	cout << "Service Current\n";

		//Save The old event code.
		oldevent = Current->getCurEvent();

                //Update Current
                eventtype = Current->Age(OnCPU);

		TScount++;

		//Requeue this process if necessary.
		// 1) is It done using the CPU
                if(eventtype == IO || eventtype == SL || eventtype == NONE)
                {       //Put this process back into the appropriate queue
                        Enqueue(Current, time);
                        Current = NULL;
                        //Reset the Time Slice Count
                        TScount = 0;  
                }
                else if(TScount >= TimeSlice && oldevent != CC)
		{	//2) if the timeslice is up and this WAS not in CC
			Enqueue(Current, time);
			Current = NULL;
			TScount = 0;
		}
		//else the process stays on the CPU
		
		if(TScount > TimeSlice)
		{
			cout << "Process: " << ReadyQ.PeekFrontPID();
			cout << " missed time cycle: " << time << endl;	
		}

        }
        else
        {       //No process needs the CPU this cycle
                cout << "CPU is idle - time: " << time << endl;
        }


}

void Scheduler::DoPriority(int time)
{	int     loopcnt;		//Loop Counter
 	Process *PPtr1=NULL;		//Need 2 temp Process Pointers
	Process *PPtr2=NULL;
	EventType eventtype, oldevent;	//a few event type holders
	static int oldp = -1;		//Flag and storage for Priority Inheritance

        if(DEBUG == TRUE)
        	if(Current != NULL)
        	{
        	        cout << "Current: " << Current->getPID() << "\n";
        	        cout << "Time:    " << time << "\n";
        	        cout << "Event:   " << Current->getCurEvent() <<"\n";
        	}
        	else cout << "No Current Process.\n";   

	//Find the highest priority Ready process.
	loopcnt = ReadyQ.getCount();
	if(loopcnt > 0)
	{
		PPtr1 = ReadyQ.Dequeue();  
		loopcnt--;
	}
        while(loopcnt > 0)
        {
                PPtr2 = ReadyQ.Dequeue();
             	if(PPtr2->getPriority() < PPtr1->getPriority())
		{
			Enqueue(PPtr1, time);
			PPtr1 = PPtr2;
                }
		else
			Enqueue(PPtr2, time);
                
                loopcnt--;
        }//end while finding HPRP

        if(DEBUG == TRUE)
                if(PPtr1 != NULL)
			cout << "HPRP: " << PPtr1->getPriority() << endl;

	if(PPtr1 != NULL)
	{
		if(Current == NULL)
		{
			Current = PPtr1;
			PPtr1 = NULL;
        		if(DEBUG == TRUE)
                	cout << "New Current = " << Current->getPID() << endl;
		}
		else
		{
			if(PPtr1->getPriority() < Current->getPriority())
				if(Current->getCurEvent() == CC)
				{
                        		//This process can not be preempted
                                	cout << "Priority Inheritance: \n";
					cout << "PID " << Current->getPID();
					cout << " was Priority: " << Current->getPriority();
					cout << " inherited PID: " << PPtr1->getPID();
					cout << "'s Priority: " << PPtr1->getPriority();
					cout << " Time: " << time; 
					cout << endl;
					
					oldp = Current->getPriority();
					Current->setPriority(PPtr1->getPriority());

					Enqueue(PPtr1, time);
				}
				else
				{	Enqueue(Current, time);
					Current = PPtr1;
					PPtr1 = NULL;
        				if(DEBUG == TRUE)
                			{	cout << "New Current Process: ";
						cout << Current->getPID() << endl;
					}
				}
			else
				Enqueue(PPtr1, time);
		}

	}
	else	//PPtr == Nulle
		if(Current == NULL)
			//No process needs the CPU this cycle
                	cout << "CPU is idle - time: " << time << endl;

        //Service the ReadyQ
        ServiceRQ(time);
                
        //Service the WaitingQ
        ServiceWQ(time);

        //Service the Current process on the CPU   
        if(Current != NULL)
        {
        	if(DEBUG == TRUE)
			cout << "Service Current\n";

		oldevent = Current->getCurEvent();
		eventtype = Current->Age(OnCPU);
		//Restore priority if was a CC, but isnt anymore
		if(oldp != -1 && oldevent == CC && eventtype != CC)
		{
			Current->setPriority(oldp);
			cout << "Reverting PID: " << Current->getPID();
			cout << "'s priority back to: " << oldp;
			cout << " Time: " << time << endl;
			oldp = -1;
		}
                //Requeue this process if necessary.
                if(eventtype == IO || eventtype == SL || eventtype == NONE)
                {       //Put this process back into the appropriate queue
                        Enqueue(Current, time);
                        Current = NULL;
                }
                //else the process stays on the CPU
        }
}

void Scheduler::DoFCFS(int time)
{	EventType eventtype;
	
	//If there is no process on the CPU, get one from the ReadyQ
	if(Current == NULL)
		if(ReadyQ.IsEmpty() != TRUE)
			Current = ReadyQ.Dequeue();

	//service all processes
        ServiceRQ(time);
        ServiceWQ(time);
        if(Current != NULL)
        {
        	if(DEBUG == TRUE)
                	cout << "Service Current\n";

		eventtype = Current->Age(OnCPU);
	}
	else
		cout << "No process on CPU - Time: " << time << endl;

 	//Requeue this process if necessary.
        if(eventtype == IO || eventtype == SL || eventtype == NONE)
        {       //Put this process back into the appropriate queue
                Enqueue(Current, time);
                Current = NULL;
        }
        //else the process stays on the CPU

}

void Scheduler::Enqueue(Process *PPtr, int time)
{	EventType eventtype;
	
	eventtype = PPtr->getCurEvent();

	switch(eventtype)
        {
        	case IO:
                case SL:
                	//Put it back in the WaitingQ
		        if(DEBUG == TRUE)
				cout << "Enqueue: " << PPtr->getPID() << "  in WaitQ\n";
                        WaitingQ.Enqueue(PPtr);
                        break;
		case CC:
                case CR:
                	//Put it back into the ReadyQ
		        if(DEBUG == TRUE)
                		cout << "Enqueue: " << PPtr->getPID() << "  in ReadyQ\n";
                        ReadyQ.Enqueue(PPtr);
                        break;
                case NONE:
                        //Put it back in the TermQ 
		        if(DEBUG == TRUE)
                		cout << "Enqueue: " << PPtr->getPID() << "  in TermQ\n";
			PPtr->setTurnTime(time+1 - PPtr->getCreateTime());
			TermQ.Enqueue(PPtr);
                        NumPending--;
                        break;
        	default:
                        //Unknown event state!
 			cout << "Error: Unknown event state\n";
			//exit(-1);??????????????????????????????????????????????????????
                        break;
       } //switch

}

void Scheduler::Report()
{	int loopcnt;
	Process *CurP = NULL;
	float AvgWT = 0;
	float AvgTT = 0;
	int NumProc;	

	NumProc = TermQ.getCount();
	loopcnt = TermQ.getCount();
	while(loopcnt > 0)
	{
		CurP = TermQ.Dequeue();		

		AvgWT += CurP->getTurnTime();
		AvgTT += CurP->getWaitTime();

		cout << "Process: " << CurP->getPID() << endl;
		cout << "\tWaiting Time = " << CurP->getWaitTime() << endl;
		cout << "\tTurnaround Time = " << CurP->getTurnTime() << endl;
		
		TermQ.Enqueue(CurP);
		CurP = NULL;
		
		loopcnt--;
	}

	AvgWT = AvgWT / NumProc;
	AvgTT = AvgTT / NumProc;

	cout << "Average Turnaround Time = " << AvgTT << endl;
	cout << "Average Waiting Time    = " << AvgWT << endl;

}

Scheduler::~Scheduler()
{
	//Chain to other destructors.
}   


