// ========================================================================================
// IMPORTANT: You should not edit this file
// ========================================================================================

#include <iostream>
#include <fstream>
#include <cassert>
#include <string>
#include <ctime>
#include <sys/time.h>
#include <sys/resource.h>

#include "timestamp.h"
#include "job.h"
#include "simulator.h"

int main(int argc, char* argv[]) {

  // ========================================================================================
  // PARSE THE COMMAND LINE ARGUMENTS
  assert (argc >= 3);
  std::ifstream istr(argv[1]);
  int num_processors = std::stoi(argv[2]);
  assert (istr.good());
  assert (num_processors >= 1);
  
  std::string visualization_file = "";
  std::string log_file = "";
  bool prioritize_by_niceness = false;
  double timestep = -1;
  
  for (int i = 3; i < argc; i++) {
    if (std::string(argv[i]) == "--vis") {
      i++;
      assert (i < argc);
      visualization_file = argv[i];
    } else if (std::string(argv[i]) == "--log") {
      i++;
      assert (i < argc);
      log_file = argv[i];
    } else if (std::string(argv[i]) == "--nice") {
      prioritize_by_niceness = true;
    } else if (std::string(argv[i]) == "--timestep") {
      i++;
      assert (i < argc);
      timestep = std::stof(argv[i]);
      assert (timestep > 0.001 && timestep <= 10.0);
    } else {
      std::cerr << "UNKNOWN COMMAND LINE ARGUMENT: " << argv[i];
      exit(1);
    }
  }

  // we will always start the simulation at 11pm
  TimeStamp simulation_start_time(23,0,0);

  // create the simulation object
  Simulator simulator(simulation_start_time, num_processors, timestep, prioritize_by_niceness, visualization_file, log_file);

  // ========================================================================================
  // RUN THE SIMULATION

  // Mark the real time before we start simulation
  clock_t time_before_simulation = clock();

  // parsing variables
  int hours, minutes, seconds, milliseconds;
  char tmp1, tmp2, tmp3;


  TimeStamp LAST;
  
  while (istr >> hours >> tmp1 >> minutes >> tmp2 >> seconds >> tmp3 >> milliseconds) {

    // Read the upload timestamp for the next job in the input file.
    assert (tmp1 == ':');
    assert (tmp2 == ':');
    assert (tmp3 == '.');
    assert (hours == 23);
    assert (minutes >= 0 && minutes < 60);
    assert (seconds >= 0 && seconds < 60);
    assert (milliseconds >= 0 && milliseconds < 1000);
    TimeStamp upload_time(hours, minutes, seconds, milliseconds);

    if (upload_time == LAST) {
      std::cout << "LAST MATCHES " << LAST << std::endl;
    }
    assert (LAST < upload_time);
    LAST = upload_time;
    
    // Advance the simulation foward (if needed) so that the
    // simulation time is greater than or equal to the upload
    // timestamp of this job.

    while (true) {
      simulator.cleanupJobs();
      simulator.startJobs();

      int timestep_ms = simulator.timestepMilliseconds(upload_time);
      
      if (timestep_ms == 0) {
        break;
      } else {
        simulator.printVisualizationRow();
        simulator.tickMilliseconds(timestep_ms);
      }
    }
    
    // Read the the job data and add it to the simulation.
    std::string course, gradeable, username;
    double runtime;
    int nice;
    istr >> course >> gradeable >> username >> runtime >> nice;

    assert (runtime > 0);
    assert (nice >= -20 && nice <= 20);
    Job job(course, gradeable, username, upload_time, runtime, nice);
    simulator.addJob(job);
  }
  
  // We have read all of the jobs from the input file, but there may
  // still be autograding jobs running and/or autograding jobs waiting
  // in the queue.  Continue to run the simulation until all jobs are
  // finished.

  while (true) {
    simulator.cleanupJobs();
    simulator.startJobs();
    simulator.printVisualizationRow();
    if (simulator.simulationComplete()) break;
    int timestep_ms = simulator.timestepMilliseconds();
    assert (timestep_ms > 0);
    simulator.tickMilliseconds(timestep_ms);
  }

  // Mark the real time after simulation is finished.
  clock_t time_after_simulation = clock();
  double simulation_running_time = double(time_after_simulation-time_before_simulation) / double(CLOCKS_PER_SEC);

  // ========================================================================================  
  // Print the summary statistics to std::cout
  simulator.printSummaryStatistics(simulation_running_time);
}
