#include "Scheduler.h"
#include "main.h"
#ifdef ultrix
#include <signal.h>
void microsleep (unsigned int microseconds);
#endif
#pragma implementation

static int proc_count;

Scheduler::Scheduler (){
  proc_queue = NULL;
  tail_pointer = NULL;
  proc_count = 0;
}

Scheduler::~Scheduler (){
  struct proc_node *temp;
  while (proc_queue)
    {delete proc_queue->process;
     temp = proc_queue->next;
     delete proc_queue;
     proc_queue = temp;}
}

int Scheduler::add_process (int obj_on , Value* mthd_nm , Val_List* actuals,
			    int ticks){

  struct proc_node *temp;
  
//  Value* tempval;
//  cout << "Starting method " << *mthd_nm->str << " on object " << obj_on;
//  if (actuals)
//    {tempval = actuals->nth(1)->copy();
//     cout << " first argument: ";
//     tempval->print_val();
//     tempval->release();}
//  cout << "\n";

  if (db->lookup_var (obj_on, mthd_nm->sym))
    {temp = new struct proc_node;
     if (!tail_pointer)
       {temp->next = (temp->prev = NULL);
	tail_pointer = (proc_queue = temp);}
     else
       {temp->prev = tail_pointer;
	temp->next = NULL;
	tail_pointer->next = temp;
	tail_pointer = temp;}
     temp->process =
       new Process (obj_on , proc_count , mthd_nm, ticks, actuals);
     return proc_count++;}
  else
    return -1;
}

/**
*** this is not particularly efficient, but hopefully a better scheduler
*** will magically appear in my hands...
**/
int Scheduler::rm_process (int pid){
  struct proc_node *temp;
  for (temp = proc_queue ; temp ; temp = temp->next)
    if (temp->process->process_id == pid)
      {if (temp->prev)
	 {if (temp->next)
	    {temp->prev->next = temp->next;
	     temp->next->prev = temp->prev;}
	  else
	    {tail_pointer = temp->prev;
	     temp->prev->next = NULL;}}
       else
	 {proc_queue = temp->next;
	  if (temp->next)
	    temp->next->prev = NULL;
	  else
	    tail_pointer = NULL;}
       delete temp->process;
       delete temp;
       return 1;}
  return 0;
}


#ifdef ultrix
// ultrix does not define usleep ... to get a sleep of less than a
// second, i had to write it myself.  grr.
void alarm_handler(int ignored){
}

void microsleep (unsigned int microseconds){
  struct itimerval time1, time2;
  signal (SIGALRM , alarm_handler);
  time1.it_interval.tv_sec = 0;
  time1.it_interval.tv_usec = microseconds;
  time1.it_value.tv_sec = 0;
  time1.it_value.tv_usec = microseconds;
  time2.it_interval.tv_sec = 0;
  time2.it_interval.tv_usec = microseconds;
  time2.it_value.tv_sec = 0;
  time2.it_value.tv_usec = microseconds;
  setitimer (ITIMER_REAL , &time1 , &time2);
  pause();
  time1.it_interval.tv_sec = 0;
  time1.it_interval.tv_usec = 0;
  time1.it_value.tv_sec = 0;
  time1.it_value.tv_usec = 0;
  time2.it_interval.tv_sec = 0;
  time2.it_interval.tv_usec = 0;
  time2.it_value.tv_sec = 0;
  time2.it_value.tv_usec = 0;
  setitimer (ITIMER_REAL , &time1 , &time2);
  signal (SIGALRM , SIG_IGN);
}
#endif

void Scheduler::one_cycle  (int slice_size){
  struct proc_node *temp, *temp2;
  int retval, allsleeping=1;
  for (temp=proc_queue ; temp ;)
    {if (! (retval = temp->process->give_ticks (slice_size)))
       {temp2 = temp->next;
	rm_process (temp->process->process_id);
	temp = temp2;}
     else
       {temp = temp->next;
	if (retval>0)
	  allsleeping=0;}}
  if (allsleeping)
#ifdef ultrix
    microsleep (NICE_SLEEP);
#else
    usleep (NICE_SLEEP);
#endif
}