/* 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.
 *
 * Framework created by Henry M. Walker on 27 September 2004
 * Last revised by Janet Davis, 24 September 2006
 */

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

/* Minimum time between events */
#define MIN_TIME_INCREMENT 0.01 

/* set the following constant as follows:
 *  0 represents a nonpreemptive scheduling algorithm
 *  any positive number represents the time quantum for a preemptive
 *  scheduling algorithm
 */
#ifndef TIME_QUANTUM
#define TIME_QUANTUM 0.0
#endif

/* set SCHEDULER_OVERHEAD to the time required for each call of the scheduler */
#ifndef SCHEDULER_OVERHEAD
#define SCHEDULER_OVERHEAD 0.35
#endif

/* specify file listing the jobs to simulate */
#define JOB_FILE_NAME "/home/davisjan/csc/213/examples/scheduler_job_data.txt"

/* The Event and eventListStruct structures form the basis 
 * for the main simulation program. */
struct Event {
  double arrivalTime; /* time when Event entered system */
  double cpuTime;     /* overall CPU time required for the event */
  int priority;       /* priority value if priority scheduling used */
  double cpuTimeLeft; /* CPU time remaining for processing the event */
  int hasStarted;     /* Initially false; set to true when the process 
                       * has been scheduled for the first time. */
};

typedef void EventHandlerFunc (struct Event *);

struct eventListStruct {
  double arrivalTime;
  struct Event * job;
  EventHandlerFunc * eventHandler;  /* called fire in Nutt */
  struct eventListStruct * next;
};

struct performanceData {
  int jobsStarted;
  int jobsCompleted;
  double totalProcTime;
  double totalWaitTime;
};

struct performanceData statistics;

/* each scheduling algorithm just include the following definitions:
 *
 *   struct jobQueue:  a structure in which entering jobs are placed
 *   void initializeJobQueue (jobQueue *): initializes job queue to no jobs
 *   void insertJob (Event, jobQueue *):  adds given Event to JobQueue
 *   Event * selectJob (jobQueue *):  selects and returns next job Event,
 *                                    deleting it from the jobQueue
 */

/* 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 */

#include "scheduler_fcfs.c"

/* these functions handle the various events generated by the simulation */
void initializeEventList(void);
void executeNextEvent(void);
void enqueueEvent (EventHandlerFunc * eventHandler, struct Event * job, double arrivalTime);
void registerJob (struct Event* job);
void processJob (struct Event* job);
void scheduler (struct Event* job);
void printEventList(void);
void printStatistics(void);

/* debugging flags (use with gcc -Dflag -o foo foo.c)
 * input:  print input cases as they are read from the file
 * eventlist:  print event list in main simulation loop
 * printstats:  print times in main simulation loop
 */

/* 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 eventlist
      printEventList();
    #endif

    executeNextEvent();

    #ifdef printstats
      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 start 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");

  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 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 scheduling 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 scheduling 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);
}

