#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cbthread.h"
#include "printqsim.h"

typedef void (*func)();

void submit_job_slot_ready(Spq *s);
void job_ready(Spq *s);

typedef struct {
  cbthread_gsem user_sem;
  cbthread_gsem printer_sem;
  Job **buffer;
  int head;
  int tail;
  int njobs;
  func *user_funcs;
  Job **user_jobs;
  func *printer_funcs;
} Info;

void initialize_v(Spq *s)
{
  Info *info;

  info = (Info *) malloc(sizeof(Info));
  info->user_sem = cbthread_make_gsem(0);
  info->printer_sem = cbthread_make_gsem(0);
  info->buffer = (Job **) malloc(sizeof(Job *)*s->bufsize);
  info->user_funcs = (func *) malloc(sizeof(func)*s->nusers);
  info->printer_funcs = (func *) malloc(sizeof(func)*s->nprinters);
  info->user_jobs = (Job **) malloc(sizeof(Job *)*s->nusers);
  info->head = 0;
  info->tail = 0;
  info->njobs = 0;
  s->v = (void *) info;
}

void submit_job_cont(Spq *s)
{
  Info *info;

  info = (Info *) s->v;
  if (info->njobs != s->bufsize) {
    info->buffer[info->tail] = info->user_jobs[s->id];
    info->tail = (info->tail + 1) % s->bufsize;
    info->njobs++;
    if (cbthread_gsem_getval(info->printer_sem) < 0) cbthread_gsem_V(info->printer_sem);
    (*info->user_funcs[s->id])(s);
    cbthread_exit();
  } else {
    cbthread_gsem_P(info->user_sem, submit_job_cont, s);
  }
}

void submit_job(Spq *s, Job *j, void (*function)())
{
  Info *info;

  info = (Info *) s->v;
  info->user_funcs[s->id] = function;
  info->user_jobs[s->id] = j;
  submit_job_cont(s);
}

void get_print_job_cont(Spq *s)
{
  Info *info;

  info = (Info *) s->v;
  if (info->njobs == 0) {
    cbthread_gsem_P(info->printer_sem, get_print_job_cont, s);
  } else {
    s->job = info->buffer[info->head];
    info->head = (info->head + 1) % s->bufsize;
    info->njobs--;
    if (cbthread_gsem_getval(info->user_sem) < 0) cbthread_gsem_V(info->user_sem);
    (*info->printer_funcs[s->id])(s);
  }
}
void get_print_job(Spq *s, void (*function)())
{
  Info *info;

  info = (Info *) s->v;
  info->printer_funcs[s->id] = function;
  get_print_job_cont(s);
}


