/* This file is part of Lcrzo.
*/

/*-------------------------------------------------------------*/
#if defined LCRZODEF_SYSTEM_Unix
#define LCRZO_PRIV_GLOBAL_CHILDPROCESSES_DEBUG 0
#define LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX 1000
lcrzo_uint32 lcrzo_priv_global_childprocesses[LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX];

void lcrzo_priv_childprocesses_sig_received(int num);
void lcrzo_priv_childprocesses_sig_received(int num)
{ int i;

  for ( i=0 ; i< LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX ; i++ )
  { if (lcrzo_priv_global_childprocesses[i]!=0)
    {
#if LCRZO_PRIV_GLOBAL_CHILDPROCESSES_DEBUG==1
      printf("[SIG%d] I(%d) kill process ", num, getpid());
      lcrzo_uint32_print("", lcrzo_priv_global_childprocesses[i], "%d", "");
      printf(" at position %d\n", i);
#endif
      kill(lcrzo_priv_global_childprocesses[i], num);
    }
  }
#if LCRZO_PRIV_DATA_ALLOC_DEBUG==2
  /* debug memory leaks */
  lcrzo_priv_globalvars_close();
  checkmemory_report();
#endif
  raise(SIGKILL);
  return;
}

int lcrzo_priv_childprocesses_killall(void)
{ int i;

  for ( i=0 ; i< LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX ; i++ )
  { if (lcrzo_priv_global_childprocesses[i]!=0)
    {
#if LCRZO_PRIV_GLOBAL_CHILDPROCESSES_DEBUG==1
      printf("I(%d) kill process ", getpid());
      lcrzo_uint32_print("", lcrzo_priv_global_childprocesses[i], "%d", "");
      printf(" at position %d\n", i);
#endif
      kill(lcrzo_priv_global_childprocesses[i], SIGINT);
    }
  }
  return(LCRZO_ERR_OK);
}

int lcrzo_priv_childprocesses_init(void)
{ int i;
  for ( i=0 ; i< LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX ; i++ )
  { lcrzo_priv_global_childprocesses[i]=0;
  }
  signal(SIGINT, lcrzo_priv_childprocesses_sig_received);
  signal(SIGTERM, lcrzo_priv_childprocesses_sig_received);
  return(LCRZO_ERR_OK);
}

int lcrzo_priv_childprocesses_add(lcrzo_uint32 processnumber);
int lcrzo_priv_childprocesses_add(lcrzo_uint32 processnumber)
{ int i;
  for ( i=0 ; i< LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX ; i++ )
  { if (lcrzo_priv_global_childprocesses[i]==0)
    { lcrzo_priv_global_childprocesses[i]=processnumber;
#if LCRZO_PRIV_GLOBAL_CHILDPROCESSES_DEBUG==1
      printf("I(%d) add process ", getpid());
      lcrzo_uint32_print("", processnumber, "%d", "");
      printf(" at position %d\n", i);
#endif
      return(LCRZO_ERR_OK);
    }
  }
  return(lcrzo_ie("lcrzo_priv_childprocesses_add", 
		  "LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX trop petit",
		  "LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX too low"));
}

int lcrzo_priv_childprocesses_del(lcrzo_uint32 processnumber);
int lcrzo_priv_childprocesses_del(lcrzo_uint32 processnumber)
{ int i;
  for ( i=0 ; i< LCRZO_PRIV_GLOBAL_CHILDPROCESSES_MAX ; i++ )
  { if (lcrzo_priv_global_childprocesses[i]==processnumber)
    { lcrzo_priv_global_childprocesses[i]=0;
#if LCRZO_PRIV_GLOBAL_CHILDPROCESSES_DEBUG==1
      printf("I(%d) remove process ", getpid());
      lcrzo_uint32_print("", processnumber, "%d", "");
      printf(" at position %d\n", i);
#endif
    }
  }
  return(LCRZO_ERR_OK);
}

#elif defined LCRZODEF_SYSTEM_Windows

typedef struct
{ int (*pfunc)(lcrzo_data infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
  lcrzo_data infos;
  lcrzo_int32 infossize;
  lcrzo_process parentprocess;
  lcrzo_priv_globalvars *globalptr;
} lcrzo_priv_process_funcinfos;

unsigned __stdcall lcrzo_priv_process_other_init(void *pfuncinfos);
unsigned __stdcall lcrzo_priv_process_other_init(void *pfuncinfos)
{ lcrzo_priv_process_funcinfos *plppfi=(lcrzo_priv_process_funcinfos *)pfuncinfos;
  int ret;

  /* Developper note : we should ensure that the global environnement is not
     changed in the father before the end of the copy (by using an IPC blocking
     the father). It should be done in next versions. */
  ret=lcrzo_priv_globalvars_copy(plppfi->globalptr);
  if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);

  ret=(*(plppfi->pfunc))(plppfi->infos, plppfi->infossize,
			 &(plppfi->parentprocess));
  if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);

  ret=lcrzo_close_forthreadonly();
  lcrzo_process_current_exit(ret);
  /* never reached */
  return(lcrzo_ierp("lcrzo_priv_process_init"));
}
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif


/*-------------------------------------------------------------*/
int lcrzo_process_other_init2(int (*pfunc)(void *infos,
					   lcrzo_int32 infossize, 
					   lcrzo_process *pparentprocess),
			      void *infos,
			      lcrzo_int32 infossize,
			      lcrzo_uint8 (*punixreturn_int2uint8)(int ret),
			      int (*punixreturn_uint82int)(lcrzo_uint8 ret),
			      lcrzo_process *pchildprocess)
{ 
  /* parameter verification */
  if (pfunc==NULL) return(LCRZO_ERR_PANULLPTR);
  if (pchildprocess==NULL) return(LCRZO_ERR_PANULLPTR);

#if defined LCRZODEF_SYSTEM_Unix
  { lcrzo_process currentprocess;
    lcrzo_er(lcrzo_process_current_init(&currentprocess));
    pchildprocess->threadid=0;
    pchildprocess->threadhandle=0;
    /* prepare for return of the child */
    if (punixreturn_uint82int!=NULL)
    { pchildprocess->punixreturn_uint82int=punixreturn_uint82int;
    }
    else
    { pchildprocess->punixreturn_uint82int=&lcrzo_process_uint82int_std;
    }
    /* create child */
    pchildprocess->processid=fork();    
    if ( (int)pchildprocess->processid < 0 ) return(LCRZO_ERR_FUFORK);
    if ( pchildprocess->processid==0 )
    { lcrzo_priv_globalvars *ptr;
      int ret;
#if LCRZO_PRIV_DATA_ALLOC_DEBUG==2
      /* debug memory leaks */
      ret=checkmemory_flush();
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
      ret=checkmemory_init();
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
#endif
      /* change the return politics */
      ret=lcrzo_priv_globalvars_ptr_get(&ptr);
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
      if (punixreturn_int2uint8!=NULL)
        ptr->pprocess_int2uint8=punixreturn_int2uint8;
      else
        ptr->pprocess_int2uint8=&lcrzo_process_int2uint8_std;
      /* reinit random seed */
      ret=lcrzo_rand_init(0);
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
      /* prepare for killing its own childs */
      ret=lcrzo_priv_childprocesses_init();
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
      /* call the main function */
      ret=(*pfunc)(infos, infossize, &currentprocess);
      if (ret!=LCRZO_ERR_OK) lcrzo_process_current_exit(ret);
      lcrzo_process_current_exit(ret);
      /* never reached */
    }
    /* never reached by child*/
    lcrzo_er(lcrzo_priv_childprocesses_add(pchildprocess->processid));
  }
#elif defined LCRZODEF_SYSTEM_Windows
  { lcrzo_priv_process_funcinfos *plppfi;
    /* launch process */
    lcrzo_er(lcrzo_data_alloc(sizeof(lcrzo_priv_process_funcinfos),
			      &(pchildprocess->pfuncinfos)));
    plppfi=(lcrzo_priv_process_funcinfos *)(pchildprocess->pfuncinfos);
    plppfi->pfunc=pfunc;
    lcrzo_er(lcrzo_data_initm_data(infos, infossize, &(plppfi->infos),
				   &(plppfi->infossize)));
    lcrzo_er(lcrzo_process_current_init(&(plppfi->parentprocess)));
    lcrzo_er(lcrzo_priv_globalvars_ptr_get(&(plppfi->globalptr)));
#if 0
    pchildprocess->threadhandle=CreateThread(NULL, 0, lcrzo_priv_process_init,
					     plppfi, 0,
					     &(pchildprocess->threadid));
    /* never reached by child */
    if ( pchildprocess->threadhandle == NULL )
      return(LCRZO_ERR_FUCREATETHREAD);
#else
    { unsigned long h;
      unsigned t;
      h=_beginthreadex(NULL, 0, lcrzo_priv_process_other_init, plppfi, 0, &t);
      pchildprocess->threadid=t;
      /* never reached by child */
      if ( h==0 ) return(LCRZO_ERR_FUCREATETHREAD);
      pchildprocess->threadhandle=(HANDLE)h;
    }
#endif

    /* set process id */
    pchildprocess->processid=_getpid();
  }
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

  /* for return value */
  pchildprocess->hasended=LCRZO_FALSE;
  lcrzo_priv_notset_int(pchildprocess->returnedvalue);

  return(LCRZO_ERR_OK);
  /* just for the compiler warning */
  punixreturn_int2uint8=NULL;
  punixreturn_uint82int=NULL;
}


