#define _PROCESS_1
/*
#undef DEBUG
#undef DBGLVL
#define DEBUG 0
#define DBGLVL -1
*/

#include <sys/time.h>
#include <unistd.h>		/* Unix calls */
#include "thread/queue.h"	/* Queueing jobs */
#include "thread/debug.h"	/* What I need is a list, not a queue */

#include "includes.h"
#include "forker.h"
#include "pcoreipc.h"
#include "system.h"

#define TICKS_PER_SEC	(20)
#define TIME_INTERVAL	(1000000L/TICKS_PER_SEC)

/*
The main loop of the system I/O module is here.

* Constantly select () input/output.
* Meant previously: SIGUSR1 means we have a new job to do, so add it to the
 select () list. NO: this means that if many thread ask I/O, we'll be spending
 all time in signal handling, and do no I/O !!!

* Perhaps we should always open /dev/null, so we always have a file
 descriptor ready for some jobs...

* Read and Write jobs messages are done through the message passing facility;
 currently, we support only SYSV IPC, which allows each thread to send or
 receive messages.
* We only support messages up to 504 bytes long, so an implementation of
 messages through a pipe will work, as POSIX ensures atomicity of pipe
 read/write for less than 512 bytes (actually, this is _POSIX_PIPE_BUF, as
 defined in <limits.h>).
* A message is identified by its job number, the address of the routine to
 complete the job or a message type number (differenciated by that the address
 should be greater than the size of the). Number may be useful for externally
 linked modules, but suddenly I doubt it.
* All messages are acknowledged when the job is completed, so the corresponding
 thread in the runtime process will
* "destroy job" messages are acknowledged by the system process, so the
 runtime can free buffers related to a job.
* The result of reads and writes is (currently) done through writing buffers
 in shared memory. Buffers are managed by the runtime process.
* The jobs are put in a queue; they are added at one end, while priority is
given to jobs at the other end.
* When a job did do blocking I/O, but isn't finished, it looses its priority.
* Each time a select() was done, all jobs are tried, but if an IO was done,
each job is preceded by a test that no message to kill it was sent.
* A message to kill an inexistent job is simply ignored and acknowledged
with a special flag. (with a debugging message), 'cause perhaps it was killed
a little time before it was completed, and the kill message arrives after the
completion message was sent.

*/



/*********************** Miscellaneous facilities ***************************/

static void io_sync (void) {		/* synchronize the I/O process */
  /* Nothing to do. Go directly to the request server */
  struct sembuf semops [1] = {
    {sem_runloop_busy,0,0}
/*,  {sem_timer_busy,  0,0}  */
  };
  ___(2,"Waiting for the other process.\n") ;
  TRYM1( semop (pcore_sem_id,semops,1) );
}


static volatile void kill_self (int value) {

/* Send a "Terminating" IPC message to the other process .... */

  SHM_G(terminating=1) ;

/* Wait for the others to exit */
  io_sync () ;
  ___(1,"Exiting...\n") ;
  pcore_free_ipc () ;
  exit (value) ;
}

/************************** Job handling ********************************/

static Queue Job_Queue ;

static void Init_Job_Queue (void) {
  Job_Queue = q_create () ;
}

pcs_job *  pcs_Create_Job	(void)
{
  return MEM_Alloc (sizeof(pcs_job)) ;
}

void	   pcs_Delete_Job	(pcs_job * job)
{
  MEM_Free (job) ;
}

void	   pcs_Register_Job	(pcs_job * job)
{
  /* .... */
}

void	   pcs_UnRegister_Job	(pcs_job * job)
{
 /* .... */
}

void	   pcs_Acknowledge	(int id,int code)
{
 /* .... */
}


/************************** Passing Messages ********************************/

/*
To simplify the handling, we pass a priority code,
then the address of a routine with arguments for it
 (integer values, or pointers to shared memory).
*/

static void Read_Message (void) {
}

static void Read_Messages (void) {
  /* read all messages present, then return */
}

void Send_Message () { /* ... */
}

/******************************* Selecting **********************************/

static struct timeval	Timeout ;
static int		sys_num_fds ;	/* ??? first select() parameter */
static fd_set		sys_ifds ;	/* input fd's */
static fd_set		sys_ofds ;	/* output fd's */
static fd_set		sys_efds ;	/* fd's with exception on it */

static void Add_Job_to_SEL_SET (any_ptr scratch,pcs_job* job) {
}

static void MakeSEL_SET (void) {
 ___(1009,"Making job queue -> select fd sets mapping\n") ;
  FD_ZERO (&sys_ifds) ;
  FD_ZERO (&sys_ofds) ;
  FD_ZERO (&sys_efds) ;
  q_journey(Job_Queue,(JFun_t*) Add_Job_to_SEL_SET,NULL) ;

  sys_num_fds=0 ; /* what shouldI put here ??? */
}

static void SetTimeout (struct timeval * tv)
{
  ___(1009,"Reset select() time out\n") ;
  tv -> tv_sec = 0 ;
  tv -> tv_usec = TIME_INTERVAL ;
}

static void sys_main_loop (void)
{
  ___(5,"Entering Main system loop.\n") ;
  
  while (!SHM_G(terminating))
    {

      if ( q_is_empty(Job_Queue) )
	{
	  usleep (TIME_INTERVAL) ;
	}
      else
	{
	  ___(1007,"Setting select() parameters.\n") ;
	  MakeSEL_SET () ;
	  SetTimeout (&Timeout) ;
	  
	  ___(1006,"Selecting\n") ;
	  if( select (sys_num_fds,&sys_ifds,&sys_ofds,&sys_efds,&Timeout)==-1 )
	    {
	      /* on error */
	      switch (errno)
		{
		case EINTR:
		  ___(5,"Caught an interrupt while selecting. "
		      "Should be SIGUSR1 or SIGIO.\n") ;
		  break ;
		default:
		  ___(1,"Error while selecting: ") ;
		  perror (NULL) ;
		  kill_self (errno) ;
		}
	    }
	  else 
	    {
	      if (!Timeout.tv_usec)
		{
		  ___(100,"Time out ! IPC test flags: %d %d\n",
		      SHM_G(ipc_test_flag),shm_base[10000]) ;
		}
	      else
		{
		  ___(5,"Doing the I/O.\n") ;
		  /* .... */
		}
	    }
	}
    }
  ___(5,"Exiting Main system loop: terminating !\n") ;
}



/**************************** Entry Point ******************************/

volatile void do_system (void) {
/*** Initing ***/
 ___(1,"Running as process %d\n",current_process) ;
  MEM_BootStrap () ;	/* turn on debugging */
  Init_Job_Queue () ;

/*** Main loop ***/
  sys_main_loop () ;

/*** Exiting ***/
  kill_self (0) ;
}
