#include "round_robin.h"
#include "main.h"
#include <stdlib.h>

// Exact quantum does not matter much for sandbox testing;
// arbitrarily set to 100 time units
cpu_time_t quantum = AVG_BURST;

void round_robin_run(process processes[], int num_processes) {
  // Initialize memory
  for (int i = 0; i < num_processes; i++) {
    size_t memory_heuristic = processes[i].burst / quantum + 1;
    processes[i].starts = malloc(memory_heuristic * sizeof(cpu_time_t));
    processes[i].durations = malloc(memory_heuristic * sizeof(cpu_time_t));
  }

  process *ready_queue[num_processes];
  int num_ready = 0;
  int num_processed = 0;
  int num_run = 0;
  cpu_time_t next_free = 0;
  while (num_run < num_processes) {
    // Add new arrivals to ready queue
    for (int i = num_processed;
         i < num_processes && processes[i].arrival <= next_free; i++) {
      ready_queue[num_ready] = &processes[i];
      num_ready++;
      num_processed++;
    }

    // Idle if ready queue is empty
    if (num_ready == 0) {
      next_free = processes[num_processed].arrival;
      continue;
    }

    // Run next process (potentially partially)
    process *next = ready_queue[0];
    num_ready--;
    for (int i = 0; i < num_ready; i++) {
      ready_queue[i] = ready_queue[i + 1];
    }

    size_t num_sch = next->num_schedules;
    next->starts[num_sch] = next_free;
    if (next->executed + quantum < next->burst) {
      // Partial execution
      next->durations[num_sch] = quantum;
      next->executed += quantum;

      // Place back into execution
      ready_queue[num_ready] = next;
      num_ready++;
    } else {
      next->durations[num_sch] = next->burst - next->executed;
      num_run++;
    }
    next_free = next->starts[num_sch] + next->durations[num_sch];
    next->num_schedules++;
  }
}

scheduler round_robin = (scheduler){.name = "roundrobin",
                                    .description = NULL,
                                    .run = round_robin_run,
                                    .uses_priority = 0};
