/* Un programme serveur udp pour gerer une boucle
 * sur une grappe de machines...
 * Ph. Langevin.
 * Septembre 2006, Novembre  2006
 * Septembre 2009 
*/

#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <time.h>
#include "bigloop.h"

#define UN ( (ullong) 1 ) 

int   IDENT  = 0;
int   PORT   = 0;
char *SERVER = NULL;
ullong FIRST = 0, STEP = 0, LAST   = 0;
uint  NBSTEP = 0, SPLIT=0;

struct sockaddr_in SERVEUR;
struct in_addr    ADDRSERV;
struct sockaddr_in  CLIENT;
int sock_server;
ticket current;
int bigloopargs(int argc, char* argv[])
{
  char *optliste = "a:i:f:l:p:s:hx:F:L:S:X:";
  int   s, opt;
  while ( ( opt = getopt(argc, argv, optliste )) >=0 ) {
	    switch ( opt ){
	    case 'i' : IDENT    = atoi(optarg);         break;
	    case 'p' : PORT     = atoi(optarg);         break;
            case 'a' : SERVER   = strdup(optarg);       break;
            case 's' : STEP     = atoll(optarg);        break;
	    case 'l' : LAST     = atoll(optarg);        break;
	    case 'f' : FIRST    = atoll(optarg);        break;
            case 'S' : s = atoi(optarg); STEP  = UN << s;  break;
	    case 'L' : s = atoi(optarg); LAST  = UN << s;  break;
	    case 'F' : s = atoi(optarg); FIRST = UN << s;  break;
            case 'X' : s = atoi(optarg); SPLIT = UN << s;  break;
	    case 'x' : SPLIT    = atoi(optarg);         break;
            case 'h' :
	    default  : printf("\nbigloop usage : %s", optliste);
                       return 0;
	    }
	  }
return 1;
}


int getticket( int sock, ticket *p )
{
  int nb;
  uint len = sizeof( struct sockaddr_in );
  struct sockaddr addr;
  nb = recvfrom( sock, p, sizeof( ticket ), 0  , &addr, &len);
  if ( nb <= 0 ) return 0;
  return 1;
}


struct in_addr adresse( char *str )
{ struct in_addr res;
  struct hostent *infos; 
  infos = gethostbyname( str );
  if ( ! infos ) {
           perror("\nget hostname:");
           exit(1);
	 };
  memcpy((char *) &res, infos->h_addr_list[0], infos->h_length);
  return res;
}
 
void pticket( ticket p )
{ printf("\n\n");
  switch ( p.op ){
     case READY   : printf("RDY "); break;
     case   JOB   : printf("JOB "); break;
     case  STOP   : printf("STP "); break;
     case  END    : printf("END "); break;
     case  PID    : printf("PID "); break;
     case  GET    : printf("GET "); break;
     case  VALUE  : printf("VAL "); break; 
     case  SCORE  : printf("SCR "); break;
     default      : printf("UNK "); break;
  }
  printf("%u:%u %Ld %Ld %Ld : ", p.idt, p.pid, p.deb, p.fin, p.scr);
  printf(" %s:%d", inet_ntoa( CLIENT.sin_addr ) , CLIENT.sin_port);
} 

void initbigloop( void )
{ char line[128], str[128];
  FILE * src;
  src = fopen( "bigloop.conf", "r");
  if ( ! src ){
    perror("\nconfiguation file not found!");
    exit( 1 );
  }
  while ( ! feof( src) ){
    line[0] = 0;
    fscanf( src, "%s", line);
    switch( line[0] ){
	    case 'i' : 
	      if ( IDENT == 0 )
	          sscanf(line, "ident=%d", &IDENT);
	      break;
	    case 'p' :
	      if ( PORT == 0 )
		sscanf(line, "port=%d",  &PORT);
	      break;
            case 'a' : 
              if ( SERVER == NULL ){
	      sscanf(line, "address=%s",  (char*) &str);
	      SERVER = strdup( str );
	      }
	      break;
            case 's' :
	      if ( STEP ==  0 )
		sscanf(line, "step=%Ld", &STEP);
	      break;
	    case 'f' : 
	      if ( FIRST == 0 )
		sscanf(line, "first=%Ld", &FIRST);
	      break;
	    case 'l' :
	      if ( LAST == 0 )
		sscanf(line, "last=%Ld", &LAST);
	      break;
           case 'S' :
	     if ( STEP ==  0 ){
		sscanf(line, "Step=%Ld", &STEP);
		STEP = UN << STEP;
	     }
	      break;
	    case 'F' : 
	      if ( FIRST == 0 ){
		sscanf(line, "First=%Ld", &FIRST);
		FIRST = UN << FIRST;
	      }
	      break;
	    case 'L' :
	      if ( LAST == 0 ){
		sscanf(line, "Last=%Ld", &LAST);
		LAST = UN << LAST;
	      }
	      break;
	    }
  }
  fclose(src);
  if ( SPLIT ) STEP = ( LAST - FIRST) / SPLIT;
  if ( ! STEP ) {
    printf("\nbad increment!");
    exit(0);
  }
  NBSTEP = ( LAST - FIRST ) / STEP;

  ADDRSERV =   adresse( SERVER );
  initsocketserver();
}

void bigloopparms( void )
{
  printf("\nusing server : %s-%d", SERVER, PORT);
  printf("\nidentificator: %d ", IDENT);
  printf("\nloop from %Ld to %Ld by %Ld (%d steps)", FIRST, LAST, STEP, NBSTEP);
  printf("\nusually it takes a while...\n");
}


// client methods

//mark=envoyer
//kwds=sendto
int sndticketoserver( ticket p )
{ 
  int nb;
  socklen_t len = sizeof( SERVEUR );
  int sock = socket( AF_INET, SOCK_DGRAM, 0);
  nb = sendto( sock, &p, sizeof( ticket ) , 0, (struct sockaddr *) & SERVEUR, len);
  close(sock);
  if ( nb < 0 ) {
    perror("sendticketoserver");
    return 0;
  }
  return 1;
}


int registration( void )
{ int nb; 
  int sock = socket( AF_INET, SOCK_DGRAM, 0);
  uint len = sizeof( struct sockaddr_in );
  current.op  = READY; current.idt = IDENT;
  current.scr = 0; current.pid = 0;
  current.deb = 0; current.fin = 0;
  nb = sendto( sock, &current, sizeof( ticket ) , 0, (struct sockaddr *) & SERVEUR, len);
  if ( nb < 0 ) {
    perror("registration");
    return 0;
  }
  getticket( sock, &current );
  close( sock ); 
  return current.op == PID;
}

int sendend( ullong wf)
{ int nb; 
  int sock = socket( AF_INET, SOCK_DGRAM, 0);
  uint len = sizeof( struct sockaddr_in );
  current.op  = END;
  current.scr = wf;
  nb = sendto( sock, &current, sizeof( ticket ) , 0, (struct sockaddr *) & SERVEUR, len);
  if ( nb < 0 ) {
    perror("sendend");
    return 0;
  }
  close( sock ); 
  return 1;
}
int sendvalue( ullong val)
{ int nb; 
  int sock = socket( AF_INET, SOCK_DGRAM, 0);
  uint len = sizeof( struct sockaddr_in );
  current.op  = VALUE;
  current.scr = val;
  nb = sendto( sock, &current, sizeof( ticket ) , 0, (struct sockaddr *) & SERVEUR, len);
  if ( nb < 0 ) {
    perror("send value");
    return 0;
  }
  close( sock ); 
  return 1;
}

int newjob( void )
{ int nb; 
  int sock = socket( AF_INET, SOCK_DGRAM, 0);
  uint len = sizeof( struct sockaddr_in );
  current.op  = GET;
  current.scr = 0; current.deb = 0; current.fin = 0;
  nb = sendto( sock, &current, sizeof( ticket ) , 0, (struct sockaddr *) & SERVEUR, len);
  if ( nb <= 0 ) {
    perror("newjob");
    return 0;
  }
  getticket( sock, &current );
  close( sock ); 
  if ( current.op == STOP )
    return 0;
  return 1;
}
// server methods

//mark=zombie
//kwds=MSG_DONTWAIT
int getticketfromzombie( ticket *p )
{ int retry = 2;
  int nb;
  uint len = sizeof( struct sockaddr_in );
  while ( retry-- ){
    nb = recvfrom( sock_server, p, sizeof( ticket )  , MSG_DONTWAIT, (struct sockaddr *) &CLIENT, &len);
    if ( nb <= 0 ) {
      if ( errno != EAGAIN ) 
         perror("zombie");
      else sleep( 10 );
    }
  }   
  return ( nb > 0);
}
//mark=recevoir
//kwds=recvfrom
int getticketfromclient( ticket *p )
{ int nb;
  uint len = sizeof( struct sockaddr_in );
  nb = recvfrom( sock_server, p, sizeof( ticket )  , 0, (struct sockaddr *) &CLIENT, &len);
  if ( nb <= 0 ) {
    perror("getticketfromclient");
    return 0;
  }   
  if ( p->idt != IDENT ){
    pticket( *p );
    printf("\nbad ident (%d) :  ignored", p->idt );
    return 0;
  }
  return 1;
}

int sndticketoclient( ticket p )
{ 
  int nb;
  uint len;
  len = sizeof( CLIENT );
  nb = sendto( sock_server, &p, sizeof( ticket ) , 0, (struct sockaddr *) &CLIENT, len);
  if ( nb < 0 ) {
    perror("sendticketoclient");
    return 0;
  }
  return 1;
}

int sendstop( void )
{
  current.op  = STOP;
  current.scr = 0;
  current.deb = 0;
  current.fin = 0;
  if ( ! sndticketoclient( current) ) {
    perror("send stop");
    return 0;
  }
  return 1;
}

int sendpid( int pid )
{
  current.op  = PID;
  current.pid = pid;
  if ( ! sndticketoclient( current) ) {
    perror("send pid");
    return 0;
  }
  return 1;
}

int sendjob( ullong s, ullong f, long long score)
{
  current.deb = s;
  current.fin = f;
  current.op  = JOB;
  current.scr = score;
  if ( ! sndticketoclient ( current ) ){
    printf("send job");
    return 0;
  }
  return 1;
}
//begin=prise
//kwds=initsocketserver,initserver
void initsocketserver( void )
{ memset( & SERVEUR, 0 , sizeof( SERVEUR ) );
  SERVEUR.sin_family = AF_INET;       
  SERVEUR.sin_port = htons( PORT );    
  SERVEUR.sin_addr = ADDRSERV;
}
void initserver( void )
{ 
  sock_server  = socket( AF_INET, SOCK_DGRAM, 0);
  if ( bind( sock_server, (struct sockaddr *) &SERVEUR, sizeof(SERVEUR)) <0) {
      perror("intit socket"); 
      exit(1);
    } 
}
//end
void stopserver( void )
{
  close( sock_server );
}
