# include "pcontrol.h"

Stack *JobPool_Pending;
Stack *JobPool_Busy;
Stack *JobPool_Done;
Stack *JobPool_Kill;
Stack *JobPool_Exit;
Stack *JobPool_Crash;

void InitJobStacks () {
  JobPool_Pending = InitStack ();
  JobPool_Busy    = InitStack ();
  JobPool_Done    = InitStack ();
  JobPool_Kill    = InitStack ();
  JobPool_Exit    = InitStack ();
  JobPool_Crash   = InitStack ();
}

char *GetJobStackName (int StackID) {
  switch (StackID) {
    case PCONTROL_JOB_PENDING: return ("PENDING");
    case PCONTROL_JOB_BUSY:    return ("BUSY");
    case PCONTROL_JOB_DONE:    return ("DONE");
    case PCONTROL_JOB_KILL:    return ("KILL");
    case PCONTROL_JOB_EXIT:    return ("EXIT");
    case PCONTROL_JOB_CRASH:   return ("CRASH");
  }
  gprint (GP_ERR, "error: unknown host stack : programming error\n");
  pcontrol_exit (1);
}

Stack *GetJobStack (int StackID) {
  switch (StackID) {
    case PCONTROL_JOB_PENDING: return (JobPool_Pending);
    case PCONTROL_JOB_BUSY:    return (JobPool_Busy);
    case PCONTROL_JOB_DONE:    return (JobPool_Done);
    case PCONTROL_JOB_KILL:    return (JobPool_Kill);
    case PCONTROL_JOB_EXIT:    return (JobPool_Exit);
    case PCONTROL_JOB_CRASH:   return (JobPool_Crash);
  }
  gprint (GP_ERR, "error: unknown job stack : programming error\n");
  pcontrol_exit (1);
}

Stack *GetJobStackByName (char *name) {

  if (!strcasecmp (name, "pending")) return (JobPool_Pending);
  if (!strcasecmp (name, "busy"))    return (JobPool_Busy);
  if (!strcasecmp (name, "done"))    return (JobPool_Done);
  if (!strcasecmp (name, "exit"))    return (JobPool_Exit);
  if (!strcasecmp (name, "crash"))   return (JobPool_Crash);
  return (NULL);
}

/* add job to position in stack, use StackID as default state */
int PutJob (Job *job, int StackID, int where) {

  int stat;
  Stack *stack;

  stack = GetJobStack (StackID);
  if (stack == NULL) return (FALSE);

  /* by default, these are both the same - to override, use PutJobSetState */
  job[0].state = StackID;
  job[0].stack = StackID;
  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
  // XXX need to handle the error conditions, or we drop the host & leak memory
  return (stat);
}
  
/* add job to position in stack.  set state to 'state' */
int PutJobSetState (Job *job, int StackID, int where, int state) {

  int stat;
  Stack *stack;

  stack = GetJobStack (StackID);
  if (stack == NULL) return (FALSE);

  /* alternate state specified by user */
  job[0].state = state;
  job[0].stack = StackID;
  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
  // XXX need to handle the error conditions, or we drop the host & leak memory
  return (stat);
}
  
Job *PullJobByID (IDtype JobID, int *StackID) {

  Job *job;

  *StackID = PCONTROL_JOB_PENDING;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  *StackID = PCONTROL_JOB_BUSY;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  *StackID = PCONTROL_JOB_EXIT;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  *StackID = PCONTROL_JOB_CRASH;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  *StackID = PCONTROL_JOB_DONE;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  *StackID = PCONTROL_JOB_KILL;
  job = PullJobFromStackByID (*StackID, JobID);
  if (job != NULL) return (job);

  return (NULL);
}

/* remove job from position in stack */
Job *PullJobFromStackByID (int StackID, int ID) {

  Job *job;
  Stack *stack;

  stack = GetJobStack (StackID);
  if (stack == NULL) return (NULL);

  job = PullStackByID (stack, ID);
  return (job);
}

IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv) {

  int JobID;
  Job *job;

  ALLOCATE (job, Job, 1);

  job[0].argc     = argc;
  job[0].argv     = argv;
  job[0].hostname = hostname;
  job[0].mode     = mode;
  job[0].host     = NULL;
  job[0].JobID    = NextJobID();
  job[0].Reset    = FALSE;
  job[0].realhost = NULL;

  /* do this step on start? */
  InitIOBuffer (&job[0].stdout_buff, 0x1000);
  InitIOBuffer (&job[0].stderr_buff, 0x1000);

  JobID = job[0].JobID;
  PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
  if (VerboseMode()) gprint (GP_ERR, "added new job\n");
  return (JobID);
}

void DelJob (Job *job) {

  int i;

  FREE (job[0].hostname);
  for (i = 0; i < job[0].argc; i++) {
    FREE (job[0].argv[i]);
  }
  FREE (job[0].argv);

  FreeIOBuffer (&job[0].stdout_buff);
  FreeIOBuffer (&job[0].stderr_buff);

  FREE (job);
}
