#include "spread.h"

//void heartbeat();

int To_exit = 0;

int sp_connect (char *sp_server, char *user, char *unicastAddr, mailbox *mbox) {
  int ret;
  sp_time test_timeout;
  
  test_timeout.sec = 5;
  test_timeout.usec = 0;

  ret = SP_connect_timeout(sp_server, user, 0, 1, mbox, unicastAddr, test_timeout);
  
  if( ret != ACCEPT_SESSION ) {
    SP_error( ret );
    return -1;
  }
#ifdef DEBUG
  printf("User: connected to %s with private group %s\n", sp_server, my_infSpread.privateGroup);
#endif
  return 0;
}

int sp_disconnect (mailbox mbox) {
  SP_disconnect (mbox);
}

void sp_initEventHandler (mailbox mbox) {
  E_init();
  E_attach_fd( mbox, READ_FD, sp_readMessage, 0, NULL, HIGH_PRIORITY );
}

void sp_startHandler () {
  E_handle_events();
}

void sp_stopHandler () {
  E_exit_events ();
}

void	*sp_eventHandler() {
  for(;;) {
    sp_readMessage();
  }
  return( 0 );
}


int sp_join (mailbox mbox, char group[80]) {
  int ret;

  ret = SP_join(mbox, group);
  if( ret < 0 ) {
    SP_error( ret );
    return -1;
  }
  return 0;
}

int sp_leave (mailbox mbox, char group[80]) {
  int ret;

  ret = SP_leave(mbox, group);
  if( ret < 0 ) {
    SP_error( ret );
    return -1;
  }

  return 0;
}

/*void sp_heartbeat () {

  heartbeat_time.sec = 4;
  heartbeat_time.usec = 0;

  E_queue (heartbeat, 0, NULL, heartbeat_time);

}

*/

void	sp_readMessage() {

  char		mess[102400];
  char		sender[MAX_GROUP_NAME];
  char		target_groups[100][MAX_GROUP_NAME];
  group_id	*grp_id;
  int32		*num_vs;
  char		*vs_members;
  int		num_groups;
  int		num_bytes;
  int		service_type;
  int16		mess_type;
  int		endian_mismatch;
  int		i;
  int		ret;

  service_type = 0;

  ret = SP_receive( my_infSpread.Mbox, &service_type, sender, 100, &num_groups, target_groups,
		    &mess_type, &endian_mismatch, sizeof(mess), mess );
#ifdef DEBUG
  printf("\n============================\n");
#endif
  if( ret < 0 ) {
    if ( (ret == GROUPS_TOO_SHORT) || (ret == BUFFER_TOO_SHORT) ) {
      service_type = DROP_RECV;
      printf("\n========Buffers or Groups too Short=======\n");
      ret = SP_receive( my_infSpread.Mbox, &service_type, sender, 100, &num_groups, target_groups, 
			&mess_type, &endian_mismatch, sizeof(mess), mess );
    }
  }
  if (ret < 0 ) {
    if( ! To_exit ) {
      SP_error( ret );
#ifdef DEBUG
      printf("\n============================\n");
      printf("\nBye.\n");
#endif
    }
    exit( 0 );
  }
  if( Is_regular_mess( service_type ) )	{
    mess[ret] = 0;
    if     ( Is_unreliable_mess( service_type ) ) 
#ifdef DEBUG
      printf("received UNRELIABLE ");
#endif
    else if( Is_reliable_mess(   service_type ) ) 
#ifdef DEBUG
      printf("received RELIABLE ");
#endif
    else 
      if( Is_fifo_mess(       service_type ) ) 
#ifdef DEBUG
	printf("received FIFO ");
#endif
    else 
      if( Is_causal_mess(     service_type ) ) 
#ifdef DEBUG
	printf("received CAUSAL ");
#endif
    else 
      if( Is_agreed_mess(     service_type ) ) 
#ifdef DEBUG
	printf("received AGREED ");
#endif
    else if( Is_safe_mess(       service_type ) ) 
#ifdef DEBUG
      printf("received SAFE ");


    printf("message from %s, of type %d, (endian %d) to %d groups \n(%d bytes): %s\n",
	   sender, mess_type, endian_mismatch, num_groups, ret, mess );
#endif
  }
  else if( Is_membership_mess( service_type ) ) {
          if  ( Is_reg_memb_mess( service_type ) ) {
	    num_bytes = SP_get_gid_offset_memb_mess();
	    grp_id = (group_id *)&mess[num_bytes];
	    num_bytes = SP_get_num_vs_offset_memb_mess(); 
	    num_vs = (int32 *)&mess[num_bytes];
	    num_bytes = SP_get_vs_set_offset_memb_mess();
	    vs_members = &mess[num_bytes];
#ifdef DEBUG
	    printf("Received REGULAR membership for group %s with %d members, where I am member %d:\n",
		   sender, num_groups, mess_type );
	    for( i=0; i < num_groups; i++ )
	      printf("\t%s\n", &target_groups[i][0] );
	    printf("grp id is %d %d %d\n",grp_id->id[0], grp_id->id[1], grp_id->id[2] );
#endif
	    if( Is_caused_join_mess( service_type ) ) {
#ifdef DEBUG
	      printf("Due to the JOIN of %s\n", vs_members );
#endif
	    }else if( Is_caused_leave_mess( service_type ) ) {
#ifdef DEBUG
	      printf("Due to the LEAVE of %s\n", vs_members);
#endif
	    }else if( Is_caused_disconnect_mess( service_type ) ){
#ifdef DEBUG
	      printf("Due to the DISCONNECT of %s\n", vs_members );
#endif
	    }else if( Is_caused_network_mess( service_type ) ){
#ifdef DEBUG
	      printf("Due to NETWORK change. ");
	      printf("VS set has %d members:\n", *num_vs );
	      for( i=0; i < *num_vs; i++, vs_members+= MAX_GROUP_NAME )
		printf("\t%s\n", vs_members );
#endif
	    }
	  } else if( Is_transition_mess(   service_type ) ) {
#ifdef DEBUG
	    printf("received TRANSITIONAL membership for group %s\n", sender );
#endif
	  }else if( Is_caused_leave_mess( service_type ) ){
#ifdef DEBUG
	    printf("received membership message that left group %s\n", sender );
#endif
	  }else 
#ifdef DEBUG
	    printf("received incorrecty membership message of type 0x%x\n", service_type );
#endif
  } else if ( Is_reject_mess( service_type ) ) {
#ifdef DEBUG
    printf("REJECTED message from %s, of servicetype 0x%x messtype %d, (endian %d) to %d groups \n(%d bytes): %s\n",
	   sender, service_type, mess_type, endian_mismatch, num_groups, ret, mess );
#endif
  }else 
#ifdef DEBUG
    printf("received message of unknown message type 0x%x with ret %d\n", service_type, ret);



  printf("\n");
  printf("User> ");
#endif
  fflush(stdout);
}


/*void heartbeat () {
  printf ("\n\nHeartbeat\n\n");
}
*/

void	User_command() {

  char	command[130];
  char	mess[MAX_MESSLEN];
  char	group[80];
  char	groups[10][MAX_GROUP_NAME];
  int	num_groups;
  int	mess_len;
  int	ret;
  int	i;

  for( i=0; i < sizeof(command); i++ ) command[i] = 0;

  if( fgets( command, 130, stdin ) == 0 ) Bye();
  
  switch( command[0] )
    {
    case 'j':
      ret = sscanf( &command[2], "%s", group );
      if( ret < 1 ) {
	printf(" invalid group \n");
	break;
      }
      ret = sp_join (my_infSpread.Mbox, group);
      if( ret < 0 ) 
	exit (0);
      
      break;
      
    case 'l':
      ret = sscanf( &command[2], "%s", group );
      if( ret < 1 ) 
	{
	  printf(" invalid group \n");
	  break;
	}
      ret = sp_leave(my_infSpread.Mbox, group);
      if( ret < 0 ) 
	Bye();
      
      break;
      
    case 's':
      num_groups = sscanf(&command[2], "%s%s%s%s%s%s%s%s%s%s", 
			  groups[0], groups[1], groups[2], groups[3], groups[4],
			  groups[5], groups[6], groups[7], groups[8], groups[9] );
      if( num_groups < 1 ) 
	{
	  printf(" invalid group \n");
	  break;
	}
      printf("enter message: ");
      ret = (int) fgets( mess, 200, stdin );
      if( ret==0 ) 
	Bye();
      mess_len = strlen( mess );

      ret= SP_multigroup_multicast( my_infSpread.Mbox, SAFE_MESS, num_groups, (const char (*)[MAX_GROUP_NAME]) groups, 1, mess_len, mess );
      if( ret < 0 ) {
	SP_error( ret );
	Bye();
      }
      Num_sent++;
      
      break;
      
    case 'b':
      ret=sscanf( &command[2], "%s", group );
      if( ret != 1 ) strcpy( group, "dummy_group_name" );
      printf("enter size of each message: ");
      ret = (int) fgets( mess, 200, stdin );
      if( ret==0 ) Bye();
      ret=sscanf(mess, "%d", &mess_len );
      if( ret !=1 ) mess_len = Previous_len;
      if( mess_len < 0 ) mess_len = 0;
      if( mess_len > MAX_MESSLEN ) mess_len = MAX_MESSLEN;
      Previous_len = mess_len;
      printf("sending 10 messages of %d bytes\n", mess_len );
      for( i=0; i<10; i++ ) {
	Num_sent++;
	sprintf( mess, "mess num %d ", Num_sent );
	
	ret= SP_multicast( my_infSpread.Mbox, FIFO_MESS, group, 2, mess_len, mess );
		    
	if( ret < 0 ) {
	  SP_error( ret );
	  Bye();
	}
	printf("sent message %d (total %d)\n", i+1, Num_sent );
      }
      break;
      
    case 'q':
      Bye();
      break;
      
    default:
      printf("\nUnknown commnad\n");
      Print_menu();
      
      break;
    }
  printf("\nUser> ");
  fflush(stdout);
}

void Print_menu() {
  printf("\n");
  printf("==========\n");
  printf("User Menu:\n");
  printf("----------\n");
  printf("\n");
  printf("\tj <group> -- join a group\n");
  printf("\tl <group> -- leave a group\n");
  printf("\n");
  printf("\ts <group> -- send a message\n");
  printf("\tb <group> -- send a burst of messages\n");
  printf("\n");
  printf("\tq -- quit\n");
  fflush(stdout);
}

void	Usage(int argc, char *argv[]) {
  sprintf( my_infSpread.user, "user" );
  sprintf( my_infSpread.spreadName, "4802@localhost");
  while( --argc > 0 ) {
    argv++;
    
    if( !strncmp( *argv, "-u", 2 ) ) {
      if (argc < 2) Print_help();
      strcpy( my_infSpread.user, argv[1] );
      argc--; argv++;
    }else if( !strncmp( *argv, "-r", 2 ) ) {
      strcpy( my_infSpread.user, "" );
    }else if( !strncmp( *argv, "-s", 2 ) ){
      if (argc < 2) Print_help();
      strcpy( my_infSpread.spreadName, argv[1] ); 
      argc--; argv++;
    }else Print_help();
  }
}


void    Print_help(){
  printf( "Usage: user\n%s\n%s\n%s\n%s\n%s\n",
	  "\t[-u <user name>]  : unique (in this machine) user name",
	  "\t[-s <address>]    : either port or port@machine",
	  "\t[-r ]    : use random user name");
  exit( 0 );
}

void	Bye() {
  To_exit = 1;
  
  printf("\nBye.\n");

  //E_dequeue (heartbeat, 0, NULL);
  
  sp_disconnect( my_infSpread.Mbox );
  
  exit( 0 );
}
