// Program Name:  Process Scheduler
// Programmer: Mark Dehus, 42
// Assignment Number: Project #1
// Purpose: To simulate and show the process scheduling
//			system of an operating system.

// Tested and compiled on Minigw (GCC 3.2) with Dev-C++ 4.9.8.0
// there is a couple problems on VC++ because it is outdated
// and no longer meets current ISO C++ - go and update your compiler.

#include <iostream>
#include <string>
#include <stdlib.h>
#include <fstream>
#include "objQueue.h"

using namespace std;
void print(string, char*);
void dump_process(PCB, string);
bool verbose = false;
bool outFile = true;
ofstream debugFile("debug.txt",ios::trunc);

int main(int argc, char *argv[])
{
	// seed random num generator
 	srand(static_cast<unsigned>(time(0)));
 	
 	// Counter and other vars
    int program_counter = 0;
    int execution_time = 0;
    int io_wait_time = 30;
    int time_slice = 10;
    bool run = false;
    bool execute = false;
    char buffer[33];
    char buffer1[33];
    
	// create processes and set value
	// pids are not scientificly random but who cares
	PCB process[5] = {
		PCB("Mohaa.exe", 3000 + (rand()%4000), 300, 2000, 8),
  		PCB("Winword.exe", 3000 + (rand()%4000), 0, 100000, 0),
    	PCB("TaskMngr.exe", 3000 + (rand()%4000), 1000, 20000, 5),
     	PCB("Sol.exe", 3000 + (rand()%4000), 0, 500, 0),
      	PCB("Calc.exe", 3000 + (rand()%4000), 20, 1000, 3)};
    PCB tmp_proc;
    
    for (int i = 0; i < 5; i++)
    	print("Process_Scheduler::Created process "  
     		+ process[i].get_pid() + " - " + process[i].get_name(), "\n"); 	
    
 	// create queues
	objQueue readyQ(5);
	objQueue ioWaitQ(5);
	objQueue jobQ(5);
    print("Process_Scheduler::Created 3 queues", "\n"); 
	
	// print out menu get input decide what todo
	cout << "Process_Scheduler::Start Simulation? (1=yes 0=no): ";
	cin >> run;
	
	// Fill the jobQ
	for (int i = 0; i < 5; i++){
   		tmp_proc.set_state("new");
		jobQ.enqueue(process[i]);		
	}
	
    while (run){
   		// first we need to check the job queue for processes to move
        itoa(program_counter, buffer, 10);
   		if (!jobQ.isEmpty()){
   			for (int i = 0; i < jobQ.count(); i++){
   				jobQ.dequeue(tmp_proc);
   					if (program_counter >= tmp_proc.get_start_time()){
	            		tmp_proc.set_state("ready");
      					readyQ.enqueue(tmp_proc);
      					print("\n" + tmp_proc.get_name()
      						+ "::Moved to the ready queue from job queue"
       						+ " on clock cycle #" + (string)buffer,"\n");
   					} else
   						jobQ.enqueue(tmp_proc);
   			}
      	}
    		
   		// next we check the ready queue and execute the first one in line
   		if (!readyQ.isEmpty()){
     		readyQ.dequeue(tmp_proc); 
       		tmp_proc.set_state("running");		
     	    execute = 1; 
    	    print("\n" + tmp_proc.get_name() 
         		+ "::Started execution on clock cycle #"+ (string)buffer, "\n");	
   		}
   		
        // run whatever process that is in tmp_proc through the cpu    
        while (execute){             
              	execution_time++;
      
                if (execution_time == tmp_proc.get_io_break() ||
                execution_time == 10 && tmp_proc.get_exec_time() > 0){
              		program_counter += execution_time;
	             	itoa(program_counter, buffer, 10);
	             	tmp_proc.inc_tot_exec_time(execution_time);
   	              	tmp_proc.decrease_exec(execution_time);
     		        itoa(tmp_proc.get_exec_time(), buffer1, 10);
                	print(tmp_proc.get_name()
                		+ "::Stopped execution on clock cycle #"
                	 	+ (string)buffer + " with " + (string)buffer1
                   		+ " cycles left", "\n");
                	
                 	// if the process used up its time share that
                	// means it is not waiting for any io so throw
                	// it in the ready queue
                	if (execution_time == 10){
                		tmp_proc.set_state("ready");
                		readyQ.enqueue(tmp_proc);
                		print(tmp_proc.get_name()
                			+ "::Moved to the ready queue from cpu"
                			+ " on clock cycle #" + (string)buffer,"\n");  		
              		} else { // if broke out cause of io.. send it to the ioQ
               			tmp_proc.set_io_finish_time(program_counter + 30);
               			tmp_proc.set_state("waiting");
                  		ioWaitQ.enqueue(tmp_proc);
                  		print(tmp_proc.get_name()
                			+ "::Moved to the io waiting queue from cpu","\n");
               		}
               		
                	execution_time = 0;
                	execute = false;           	
              	 	program_counter++; // Overhead
             	} else if (tmp_proc.get_exec_time() <= 0) {
                	itoa(program_counter, buffer, 10);
       	       		tmp_proc.set_runtime(program_counter - 
          			       	       		tmp_proc.get_start_time());  
            
             		tmp_proc.set_state("halted");
             		dump_process(tmp_proc, buffer);
             		
              		
              		execution_time = 0;
                	execute = false;           	
              	 	program_counter++; // Overhead
           		}           			               	
        }
        
        // check to see if anything is done in the io queue
       	if (!ioWaitQ.isEmpty()){
       		for (int i = 0; i < ioWaitQ.count(); i++){
        		ioWaitQ.dequeue(tmp_proc);
        		if (program_counter >= tmp_proc.get_io_finish_time()){
        			tmp_proc.set_state("ready");
        			readyQ.enqueue(tmp_proc);
        			print("\n" + tmp_proc.get_name()
        				+ "::Finished doing i/o and moved to ready queue","\n");
  				} else {
   					if (!ioWaitQ.isFull())
   						ioWaitQ.enqueue(tmp_proc);
				}
    	   	}			
   		}
        
        program_counter++;
                
        // uncomment and comment other if and set value to have the 
        // system run a set amount of cycles
        //if (program_counter > 500000)
        //	run = false;
        
        if (jobQ.isEmpty() && readyQ.isEmpty() && ioWaitQ.isEmpty())
        	run = false;
    }
	  		
	system("PAUSE");	
	return 0;
}

void print(string message, char* trail)
{
    if (outFile){
        ofstream debugFile("debug.txt",ios::app);
        debugFile << message << trail;
        debugFile.close();
    }    
	if (verbose)
		cout << message << trail;
}

void dump_process(PCB proc, string cycle)
{	
	int pid;
	string state;
	int start_time;
	int exec_time;
	int io_break;
	int running_time;
	float cpu_usage;
	float exec;
	float running;
	
	if (!verbose && !outFile){
		cout << "\nProcesss_Scheduler::" + proc.get_name()
			+ " ended on clock cycle #" 
			+ cycle + "\n";
   	} else if (!verbose && outFile) {
    	print("\nProcesss_Scheduler::" + proc.get_name()
    		+ " ended on clock cycle #" 
    		+ cycle, "\n");
    	cout << "\nProcesss_Scheduler::" + proc.get_name()
    		+ " ended on clock cycle #" 
       		+ cycle + "\n";
  	} else {
    	print("\nProcesss_Scheduler::" + proc.get_name()
    		+ " ended on clock cycle #" 
    		+ cycle, "\n");
    }
    
    proc.dump_vars(pid, state, start_time, exec_time, io_break, running_time);
    exec = (float)exec_time;
    running = (float)running_time;
    cpu_usage = exec / running;
    
    cout << proc.get_name() << "::statistics" 
    	<< "\n-------------------------------" << "\n\tPID: " << pid 
     	<< "\n\tSTATE: " << state << "\n\tAVG IO BREAK: " << io_break 
      	<< "\n\tRUNNING TIME: " << running_time << "\n\tEXECUTION TIME: "
       	<< exec_time << "\n\tCPU USAGE: " << cpu_usage << "\n" 
       	<< "-------------------------------\n";
    	
}

