/* Program to simulate a scheduler for an operating system.  
 * Since details of the scheduler are placed in an include file, 
 * this program may be used with different scheduling algorithms.
 *
 * The main framework for this lab follows Lab Exercise 7.1 in Nutt.
 * The generalization to multiple scheduling algorithms, however,
 * requires some adjustments.
 *
 * Debugging flags (use with cc -Dflag -o foo foo.c):
 *  PRINT_INPUT -  print input cases as they are read from the file
 *  PRINT_EVENT_LIST -  print event list in main simulation loop
 *  PRINT_STATS -  print times in main simulation loop
 *
 * Framework created by Henry M. Walker on 27 September 2004
 * Revised by 
 *   Janet Davis, 24 September 2006
 *   Jerod Weinman, 27 May 2008
 */

#include <stdlib.h> /* for malloc */
#include <stdio.h>  /* for file I/O and standard I/O */

#include "scheduler.h" /* data structures and scheduler functions */
#include "scheduler_simulation.h"

struct performanceData statistics;

/* the following global variables are used by numerous functions */
double simTime = 0.0;  /* the clock for this simulation */
struct eventListStruct * eventList = NULL; /* list of events, ordered by time */

#ifndef SCHEDULER_OVERHEAD
#define SCHEDULER_OVERHEAD 0.35
#endif

#define TIME_QUANTUM 0.0

/* The main() function initializes the event list, job queue, and
 * statistics.  It then enters the main event handling loop.  When there
 * are no more events, it prints out the final statistics.
 */
int main( void ) {

     printf( "Beginning simulation\n" );
     printf( "Scheduler overhead: %3.2f\n", SCHEDULER_OVERHEAD );
     printf( "Time quantum:       %3.2f\n", TIME_QUANTUM );

     eventList = NULL;
     initializeEventList();
     initializeJobQueue();

     /* initialize performance statistics */
     statistics.jobsStarted = 0;
     statistics.jobsCompleted = 0;
     statistics.totalProcTime = 0.0;
     statistics.totalWaitTime = 0.0;

     /* main event loop */
     while ( eventList != NULL ) {

#ifdef PRINT_EVENT_LIST
          printEventList();
#endif

          executeNextEvent();

#ifdef PRINT_STATS
          printf("accumulated times:\n");
          printStatistics();
#endif
     }

     /* print summary of performance data */
     printf ("Simulation completed\n");
     printf ("Summary statistics:\n");
     printStatistics();
     return (0);
}

/* Handles the next event.
 * Preconditions:
 *   eventList is not null.
 * Postconditions:
 *   simTime is advanced to the completion of the next event.
 *   eventList becomes eventList->next.
 *   The removed eventListStruct is freed.
 */
void executeNextEvent( void ) {
     struct eventListStruct* thisEvent;

     /* remove next event from eventList */
     thisEvent = eventList;
     eventList = eventList->next;

     /* perform work for thisEvent */
     (*thisEvent->eventHandler) (thisEvent->job) ;

     /* free space for this event */
     free (thisEvent);
}

/* enter the Event on the eventList, to start at the given arrivalTime
 * and to invoke the given eventHandler when the event executes
 * Preconditions:
 *   events in eventList are ordered by start time
 * Postconditions:
 *   a new event with the given eventHandler, job, and arrivalTime
 *     is inserted in eventList
 *   events in eventList are ordered by arrival time
 */
void enqueueEvent( EventHandlerFunc * eventHandler, 
                   struct Event * job, 
                   double arrivalTime ) {

     struct eventListStruct *eventNode;
     struct eventListStruct *temp;

     /* create event node for insertion into event list */
     eventNode 
          = (struct eventListStruct *) malloc(sizeof(struct eventListStruct));
     eventNode->arrivalTime = arrivalTime;
     eventNode->job = job;
     eventNode->eventHandler = eventHandler;

     /* put create-job-event in event list */
     /* order events by arrival time */
     if ((eventList == NULL) || (arrivalTime < eventList->arrivalTime)) 
     {
          /* insert at front of event list */
          eventNode->next = eventList;
          eventList = eventNode;
     } 
     else 
     {
          temp = eventList;

          while ((temp->next != NULL) && 
                 (arrivalTime > (temp->next)->arrivalTime)) 
          {
               temp = temp->next;
          }

          eventNode->next = temp->next;
          temp->next = eventNode;
     }
}

/* read jobs for the simulation from a file.
 * Preconditions:
 *   JOB_FILE_NAME is defined
 *   The file named contains a list of jobs where each line gives:
 *     arrivalTime duration priority
 * Postconditions:
 *   All jobs listed in JOB_FILE_NAME are on the eventList
 *   eventList includes an event to run the scheduler at time 0
 *   eventList is sorted by event arrival time
 */
void initializeEventList( void ) {

     FILE *jobFile;
     struct Event *job;
     int arrivalTime;
     float duration;
     int priority;

     printf ("reading job list from file:  \"%s\"\n", JOB_FILE_NAME);
     jobFile = fopen (JOB_FILE_NAME, "r");

     if (jobFile == NULL)
     {
          perror("Unable to open job file");
          exit (1);
     }

     while( fscanf(jobFile, "%d %f %d", &arrivalTime, &duration, &priority) 
            != EOF )
     {
          /* first create event for beginning job */
          job = (struct Event *) malloc(sizeof(struct Event));
          job->cpuTime = duration;
          job->cpuTimeLeft = duration;
          job->arrivalTime = arrivalTime;
          job->priority = priority;
          job->hasStarted = 0;

          enqueueEvent (registerJob, job, arrivalTime);

#ifdef PRINT_INPUT
          printf ("reading file: \tarrival: %d, \tduration:  %8.2f, \tpriority:  %d\n",
                  arrivalTime, duration, priority);
#endif
     }

     /* Enqueue the scheduler to start the simulation. */
     enqueueEvent( scheduler, NULL, 0 );
}


/* event handlers used by this simulation */

/* insert new job into ready queue */
void registerJob( struct Event* job ) {
     /*int queueBeganEmpty = jobQueueIsEmpty();*/
     insertJob(job);
}

/* simulate running of job */
void processJob( struct Event* job )
{
     if (TIME_QUANTUM > 0) 
     {
          printf ("preemptive %f\n", job->cpuTimeLeft);

          if (TIME_QUANTUM >= job->cpuTimeLeft) 
          {
               /* job will finish in this time slice */

               /* YOUR CODE HERE */
               /* compilation of statistics goes here */
               /* job struct freed from memory */
               /* this section to be completed in step B1 of the lab */
          }
          else
          {
               /* job will require an additional time slice */

               /* YOUR CODE HERE */
               /* remaining job time updated, and job returns to ready state */
               /* this section to be completed in step B1 of the  lab */
          }
     }
     else 
     {
          /* nonpreemptive algorithm */
          /* job runs to completion */

          /* update statistics for jobs that have been started */
          statistics.jobsStarted++;
          statistics.totalWaitTime += simTime - job->arrivalTime;
          job->hasStarted = 1; 

          /* advance the simulation time */
          simTime += job->cpuTimeLeft;

          /* update statistics for jobs that have completed */
          statistics.jobsCompleted++;
          statistics.totalProcTime += job->cpuTimeLeft;

          /* free the memory! */
          free( job );
     }

     /* after processing on job, time for scheduler again */
     enqueueEvent( &scheduler, NULL, simTime );
}

/* select next job for execution and place it on the eventList */
void scheduler( struct Event* job ) {
     struct Event* newJob;

     simTime += SCHEDULER_OVERHEAD;

     newJob = selectJob();
     if (newJob == NULL) {
          if (eventList == NULL)  {
               /* all done! */
               return;
          } else {
               /* increment time to next meaningful event */
               simTime = eventList->arrivalTime;
               enqueueEvent( &scheduler, NULL, simTime );
          }
     } else {
          processJob(newJob);
     }
}

/* print the contents of the event list */
void printEventList( void )  {
     struct eventListStruct* temp = eventList;
     printf ("Current event list:\n");
     while (temp != NULL) {
          if (temp->eventHandler == &registerJob) 
               printf ("\tregistering job");
          else if (temp->eventHandler == &processJob) 
               printf ("\tprocessing job");
          else
               printf ("\tscheduler");
          printf ("\tarrival time: %8.2f\n", temp->arrivalTime);
          temp = temp->next;
     }
}

/* print current statistics */
void printStatistics( void ) {
     printf("\tCurrent time:\t\t%8.2f\n", simTime);
     printf("\tNumber of jobs started:\t%8d\n", statistics.jobsStarted);
     printf("\tNumber of jobs completed:%7d\n", statistics.jobsCompleted);
     printf("\tSystem throughput:\t%8.4f jobs per unit time\n", statistics.jobsStarted/ simTime); 
     printf("\tTotal proc time:\t%8.2f\n", statistics.totalProcTime);
     printf("\tTotal wait time:\t%8.2f\n", statistics.totalWaitTime);
     printf("\tAverage wait time:\t%8.2f\n", 
            statistics.totalWaitTime / statistics.jobsStarted);
}
