#include "scheduler.h"
#include "../io/io.h"
#include "../memory/memory.h"

#ifdef ARCH_I386
#include "../../arch/i386/scheduler/scheduler.h"
#endif

struct task_struct process_table[MAX_PROCESSES];
uint32_t           current_pid;

void init_process_table() {
  for (uint32_t i = 0; i < MAX_PROCESSES; i++) {
    process_table[i].state = TASKSTATE_unused;
  }
  current_pid = 129;
}

uint32_t get_next_pid(uint32_t start, enum task_state_t state) {
  uint32_t i = start % MAX_PROCESSES;
  for (; process_table[i].state != state; i = (i + 1) % MAX_PROCESSES) {
  }
  return i;
}

char *balloc_pid(uint32_t size, uint32_t pid) {
  char *addr = process_table[pid].allocd_addr;
  process_table[pid].allocd_addr += size;
  return addr;
}

char *balloc(uint32_t size) { return balloc_pid(size, current_pid); }

void start_process(char *name, char *entrypoint) {
  uint32_t pid = get_next_pid(0, TASKSTATE_unused);

  process_table[pid].address_base =
      (unsigned int)(ADDRESS_SPACE_SIZE * (pid + 6));

  // TODO: Copy code into process' address space
  // memcpy(entrypoint, (char*) process_table[pid].address_base, 500000);
  // entrypoint = (char*) process_table[pid].address_base;

  process_table[pid].allocd_addr = (char *)process_table[pid].address_base;
  process_table[pid].state = TASKSTATE_ready;
  process_table[pid].process_name = balloc_pid(16, pid);
  process_table[pid].pid = pid;
  strncpy(name, process_table[pid].process_name, 16);

  initialize_registers(&process_table[pid].registers, entrypoint,
                       process_table[pid].address_base, ADDRESS_SPACE_SIZE);
}

void single_process_debug(uint32_t pid) {
  struct task_struct proc = process_table[pid];

  printf_nolock("pid: %u (%s)\n", pid, proc.process_name);
  printf_nolock("eip: %x esp: %x (%d)\n", proc.registers.eip,
                proc.registers.useresp, proc.registers.useresp);
  printf_nolock("ass: %d ase: %d\n\n", proc.address_base, proc.registers.ebp);
}

void process_debug() {
  printf_nolock("RAZZLE Debugger: Process Table | current pid: %u\n",
                current_pid);
  for (int i = 0; i < MAX_PROCESSES; i++) {
    if (process_table[i].state != TASKSTATE_unused)
      single_process_debug(i);
  }
}

void schedule(struct regs *r) {
  if (current_pid < MAX_PROCESSES) {
    store_registers(r, &process_table[current_pid].registers);
  }

  current_pid =
      get_next_pid((current_pid + 1) % MAX_PROCESSES, TASKSTATE_ready);

  switch_context(r, &process_table[current_pid].registers);
}
