/*
 * This file is part of the Owl Positioning System (OwlPS) project.
 * It is subject to the copyright notice and license terms in the
 * COPYRIGHT.t2t file found in the top-level directory of this
 * distribution and at
 *   https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
 * No part of the OwlPS Project, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms
 * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
 * distributed along with this file, either separately or by replacing
 * this notice by the COPYRIGHT.t2t file's contents.
 *
 ***********************************************************************
 *
 * This is the main source file of OwlPS Client, the program used to
 * send positioning requests to the infrastructure.
 */


#include <owlps-client-config.h>

#include <owlps-client.h>

#ifdef OWLPS_CLIENT_RECEIVES_POSITION
# include <owlps-resultreader.h>
#endif // OWLPS_CLIENT_RECEIVES_POSITION

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include <signal.h>

#include <net/if.h>
#include <rpc/types.h> // for MAXHOSTNAMELEN


/* Number of packets to send */
#define DEFAULT_NBPKT_CALIB 20 // 20 packets when calibrating
#define DEFAULT_NBPKT_NORMAL 10 // 10 packets when requesting the position

/* Delay between two packet transmissions (in milliseconds) */
#define DEFAULT_DELAY_CALIB 50 // Calibration request
#define DEFAULT_DELAY_NORMAL 25 // Localisation request

/* Delay between two requests in loop mode (in milliseconds) */
#define DEFAULT_FLOOD_DELAY 1000

/* Maximal size of a packet */
#define MAX_PKT_SIZE 1450u

/* Program arguments (getopt string) */
#define OPTIONS "DF::hi:I:l::n:N:p:qs:t:vV"


/* Function headers */
void parse_command_line(const int argc, char *const *argv) ;
void parse_main_options(const int argc, char *const *argv) ;
void check_destination_host(void) ;
void parse_calibration_data(const int argc, char *const *argv) ;
void check_configuration(void) ;
void print_configuration(void) ;
void create_socket(void) ;
void send_request(void) ;
void make_packet(void) ;
void add_padding(void) ;
uint_fast16_t initialise_common_fields(const uint_fast8_t packet_type) ;
uint_fast16_t initialise_calibration_fields(uint_fast16_t offset) ;
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
int receive_position(void) ;
#endif // OWLPS_CLIENT_RECEIVES_POSITION
void print_usage(void) ;
void print_version(void) ;


/* Options */
struct {
  bool daemon ;
  bool verbose ;
  char dest_host[MAXHOSTNAMELEN] ; // Destination host of the packets
  uint_fast16_t dest_port ;
  char iface[IFNAMSIZ + 1] ; // Source network interface
  int_fast32_t delay ; // Time between two packet transmissions
  uint_fast16_t nb_pkt ; // Number of packets to send
  uint_fast16_t pkt_size ; // Size of the packet to send
  int_fast32_t flood_delay ; // Time between two request transmissions
  bool add_flood_delay ; // Add the delay to the transmission time?
  uint_fast16_t nb_requests ; // Number of requests to send
  uint_fast16_t listening_port ;
  // Calibration data:
  owl_direction direction ;
  float x ;
  float y ;
  float z ;
} options = {
  false, // daemon
  true, // verbose
  "", // dest_host
  OWL_DEFAULT_REQUEST_PORT, // dest_port
  "", // iface
  -1, // delay
  0, // nb_pkt
  0, // pkt_size
  -1, // flood_delay
  false, // add_flood_delay
  0, // nb_requests
  0, // listening_port
  0, 0, 0, 0 // Calibration data
} ;

char *program_name = NULL ;

// true if the packet is a calibration request, false if it is
// a simple positioning request:
bool is_calibration_request = false ;

int sockfd ; // Sending socket descriptor
struct sockaddr server ; // Server info
uint8_t *packet = NULL ; // Packet to send
uint_fast16_t packet_size ; // Packet size



int main(int argc, char *argv[])
{
  struct sigaction action ; // Signal handler structure
  int ret = 0 ;
  // Number of requests we still have to transmit:
  uint_fast16_t nb_requests_left = 1 ;
  owl_timestamp start_time ; // Time of the transmission's start

  owl_run = true ;

  program_name = argv[0] ;
  parse_command_line(argc, argv) ;

  if (options.daemon)
    {
      if (options.verbose)
        fprintf(stderr, "Detaching to background...\n") ;
      if (daemon(0, 0))
        perror("Cannot daemonize") ;
    }

  /* Set up signal handlers */
  action.sa_flags = 0 ;
  sigemptyset(&action.sa_mask) ;
  action.sa_handler = owl_sigint_handler ;
  sigaction(SIGINT, &action, NULL) ;
  action.sa_handler = owl_sigterm_handler ;
  sigaction(SIGTERM, &action, NULL) ;

  create_socket() ;

  /* Transmit the first request */
  if (! options.add_flood_delay)
    owl_timestamp_now(&start_time) ;
  send_request() ;
  if (options.nb_requests)
    nb_requests_left = options.nb_requests - 1 ;

  /* Transmit the next requests, if any */
  while (owl_run && options.flood_delay >= 0 && nb_requests_left > 0)
    {
      uint_fast32_t trx_time, sleep_time ;
      owl_timestamp now ;

      if (options.verbose && options.nb_requests)
        printf("%"PRIuFAST16" more requests to transmit.\n",
               nb_requests_left) ;

      if (options.add_flood_delay)
        owl_msleep(options.flood_delay) ;
      else
        {
          owl_timestamp_now(&now) ;
          trx_time = owl_time_elapsed_ms(&start_time, &now) ;
          if (options.verbose)
            printf("Transmission time was %"PRIuFAST32" ms", trx_time) ;

          // Sleep only if the sleep delay is greater than the
          // transmission time
          if (trx_time < (uint_fast32_t) options.flood_delay)
            {
              sleep_time = options.flood_delay - trx_time ;
              if (options.verbose)
                printf(", sleeping for %"PRIuFAST32" ms...\n",
                       sleep_time) ;
              owl_msleep(sleep_time) ;
            }
          else if (options.verbose)
            printf(" > %"PRIdFAST32" ms, no sleeping required.\n",
                   options.flood_delay) ;

          owl_timestamp_now(&start_time) ;
        }

      if (owl_run) // owl_run can have been set to false during the sleep
        {
          send_request() ;
          if (options.nb_requests)
            --nb_requests_left ;
        }
    }

  close(sockfd) ;

#ifdef OWLPS_CLIENT_RECEIVES_POSITION
  if (options.listening_port > 0)
    ret = receive_position() ;
#endif // OWLPS_CLIENT_RECEIVES_POSITION

  return ret ;
}



void parse_command_line(const int argc, char *const *argv)
{
  parse_main_options(argc, argv) ;
  check_destination_host() ;
  parse_calibration_data(argc, argv) ;
  check_configuration() ;

  if (options.verbose)
    print_configuration() ;
}



void parse_main_options(const int argc, char *const *argv)
{
  int opt ;
  long arg_long ; // Integer value of optarg
  char *endptr ; // Return value of strto*()

  while ((opt = getopt(argc, argv, OPTIONS)) != -1)
    {
      switch (opt)
        {
        case 'D' :
          options.daemon = true ;
          break ;
        case 'F' :
          /* If -F is present, first set the flood delay to its default
           * value (this can appear suboptimal when the user supplies a
           * value, but will simplify the operations).
           */
          options.flood_delay = DEFAULT_FLOOD_DELAY ;

          /* Facultative getopt options do not handle separated values
           * (like -F <delay>), so we have to test separately.
           */
          if (optarg) // We got an option like -F<delay>, it's OK
            {
              arg_long = strtol(optarg, &endptr, 10) ;
              if (endptr != optarg)
                options.flood_delay = arg_long ;
              else
                fprintf(stderr, "Warning! Bad flood_delay:"
                        " failing back to the default value.\n") ;
              if (optarg[0] == '+')
                options.add_flood_delay = true ;
            }

          else // We got -F alone or -F <delay>
            {
              /* If we are not at the end of the string and the next
               * optind is not an option, we have the option with a
               * separate argument. Otherwise we got the option alone,
               * but we have nothing to do since we already set the
               * default value.
               */
              if (argv[optind] != NULL && argv[optind][0] != '-')
                {
                  // Take the optind value:
                  arg_long = strtol(argv[optind], &endptr, 10) ;
                  if (endptr != argv[optind])
                    options.flood_delay = arg_long ;
                  else
                    fprintf(stderr, "Warning! Bad flood_delay:"
                            " failing back to the default value.\n") ;
                  if (argv[optind][0] == '+')
                    options.add_flood_delay = true ;
                  ++optind ;
                }
            }
          break ;
        case 'h' :
          print_usage() ;
          exit(0) ;
        case 'I' :
          strncpy(options.iface, optarg, IFNAMSIZ + 1) ;
          break ;
        case 'i' :
          strncpy(options.dest_host, optarg, MAXHOSTNAMELEN) ;
          break ;
        case 'l' :
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
          /* If -l is present, first set the listening port to its
           * default value (this can appear suboptimal when the user
           * supplies a port number, but will simplify the operations).
           */
          options.listening_port = OWL_DEFAULT_RESULT_PORT ;

          /* Facultative getopt options do not handle separated values
           * (like -l <port>), so we have to test separately.
           */
          if (optarg) // We got an option like -l<port>, it's OK
            {
              unsigned long arg_ulong = strtoul(optarg, &endptr, 10) ;
              if (endptr != optarg)
                options.listening_port = arg_ulong ;
              else
                fprintf(stderr, "Warning! Bad listening_port:"
                        " failing back to the default value.\n") ;
            }

          else // We got -l alone or -l <port>
            {
              /* If we are not at the end of the string and the next
               * optind is not an option, we have the option with a
               * separate argument. Otherwise we got the option alone,
               * but we have nothing to do since we already set the
               * default value.
               */
              if (argv[optind] != NULL && argv[optind][0] != '-')
                {
                  // Take the optind value:
                  unsigned long arg_ulong = strtoul(argv[optind],
                                                    &endptr, 10) ;
                  if (endptr != argv[optind])
                    options.listening_port = arg_ulong ;
                  else
                    fprintf(stderr, "Warning! Bad listening_port:"
                            " failing back to the default value.\n") ;
                  ++optind ;
                }
            }
#else // OWLPS_CLIENT_RECEIVES_POSITION
          fprintf(stderr, "Warning! The program was compiled without"
                  " enabling the -l option (receive the position from"
                  " the positioning server).\n") ;
#endif // OWLPS_CLIENT_RECEIVES_POSITION
          break ;
        case 'n' :
          options.nb_pkt = strtoul(optarg, NULL, 10) ;
          break ;
        case 'N' :
          options.nb_requests = strtoul(optarg, NULL, 10) ;
          break ;
        case 'p' :
          options.dest_port = strtoul(optarg, NULL, 10) ;
          break ;
        case 'q' :
          options.verbose = false ;
          break ;
        case 's' :
          options.pkt_size = strtoul(optarg, NULL, 10) ;
          break ;
        case 't' :
          arg_long = strtol(optarg, &endptr, 10) ;
          if (endptr != optarg)
            options.delay = arg_long ;
          else
            fprintf(stderr, "Warning! Bad delay:"
                    " failing back to the default value.\n") ;
          break ;
        case 'v' :
          options.verbose = true ;
          break ;
        case 'V' :
          print_version() ;
          exit(0) ;
        default :
          print_usage() ;
          exit(OWL_ERR_BAD_USAGE) ;
        }
    }
}



void check_destination_host()
{
  /* Check if we got a destination host */
  if (options.dest_host[0] == '\0')
    {
      fprintf(stderr, "Error! You must specify a destination host"
              " (-i) .\n") ;
      print_usage() ;
      exit(OWL_ERR_BAD_USAGE) ;
    }
}



/* Parses remaining arguments (possible calibration data) */
void parse_calibration_data(const int argc, char *const *argv)
{
  /* No more arguments to parse */
  if (argc - optind == 0)
    return ;

  /* Exactly 4 more arguments */
  if (argc - optind == 4)
    {
      char *endptr ;

      is_calibration_request = true ;

      options.direction = strtoul(argv[optind], &endptr, 10) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong direction!\n") ;
          goto error ;
        }
      optind++ ;

      options.x = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong X coordinate!\n") ;
          goto error ;
        }
      optind++ ;

      options.y = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong Y coordinate!\n") ;
          goto error ;
        }
      optind++ ;

      options.z = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong Z coordinate!\n") ;
          goto error ;
        }

      return ; // No error occurred
    }

  /* Bad number of arguments or parse error */
 error:
  print_usage() ;
  exit(OWL_ERR_BAD_USAGE) ;
}



void check_configuration()
{
  // Delay not specified (or bad delay):
  if (options.delay < 0)
    {
      if (options.verbose)
        fprintf(stderr,
                "Warning! delay: failing back to default value.\n") ;
      if (is_calibration_request)
        options.delay = DEFAULT_DELAY_CALIB ;
      else
        options.delay = DEFAULT_DELAY_NORMAL ;
    }

  // Number of packet not specified (or bad number)
  if (options.nb_pkt < 1)
    {
      if (options.verbose)
        fprintf(stderr,
                "Warning! nb_pkt: failing back to default value.\n") ;
      if (is_calibration_request)
        options.nb_pkt = DEFAULT_NBPKT_CALIB ;
      else
        options.nb_pkt = DEFAULT_NBPKT_NORMAL ;
    }

  // Packet size too big
  if (options.pkt_size > MAX_PKT_SIZE)
    {
      if (options.verbose)
        fprintf(stderr,
                "Warning! pkt_size cannot be greater than %d bytes:"
                " failing back to %d.\n", MAX_PKT_SIZE, MAX_PKT_SIZE) ;
      options.pkt_size = MAX_PKT_SIZE ;
    }

  // Calibration request but bad direction
  if (is_calibration_request)
    if (options.direction < OWL_DIRECTION_MIN ||
        options.direction > OWL_DIRECTION_MAX)
      {
        fprintf(stderr, "Error! \"%"PRIu8"\" is not a valid"
                " direction.\n", options.direction) ;
        exit(OWL_ERR_BAD_USAGE) ;
      }

  // Check port numbers
  if (options.dest_port < 1 || options.dest_port > 65535)
    {
      fprintf(stderr, "Warning! Bad dest_port:"
              " failing back to default value.\n") ;
      options.dest_port = OWL_DEFAULT_REQUEST_PORT ;
    }
  if (options.listening_port > 65535)
    {
      fprintf(stderr, "Warning! listening_port too high: ignored.\n") ;
      options.listening_port = 0 ;
    }

  // We want to send a calibration request AND to be located, which is
  // not allowed:
  if (is_calibration_request && options.listening_port > 0)
    {
      fprintf(stderr, "Warning! You cannot wait for a server answer when"
              " you calibrate. Option -l ignored.\n") ;
      options.listening_port = 0 ;
    }

  if (options.flood_delay >= 0)
    {
      // We want to flood AND to be located, which is not allowed:
      if (options.listening_port > 0)
        {
          fprintf(stderr, "Warning! You cannot wait for a server answer"
                  " when you flood. Option -l ignored.\n") ;
          options.listening_port = 0 ;
        }
    }
  else // Flood is unactivated
    {
      if (options.nb_requests)
        {
          fprintf(stderr, "Warning! The -N option can be used only along"
                  "  with -F. Option -N ignored.\n") ;
          options.nb_requests = 0 ;
        }
      if (options.daemon)
        {
          fprintf(stderr, "Warning! It is useless to detach from"
                  " the foreground if the flood mode is not activated"
                  " Option -D ignored.\n") ;
          options.daemon = false ;
        }
    }
}



void print_configuration()
{
  printf("Options:\n"
         "\tDaemon: %s\n"
         "\tVerbose: %s\n"
         "\tDestination host: %s\n"
         "\tDestination port: %"PRIuFAST16"\n"
         "\tInterface: %s\n"
         "\tDelay (ms): %"PRIdFAST32"\n"
         "\tNumber of packets: %"PRIuFAST16"\n"
         "\tPacket size: %"PRIuFAST16"\n"
         "\tFlood delay (ms): %"PRIdFAST32"\n"
         "\tCumulative flood delay: %s\n"
         "\tNumber of requests: %"PRIuFAST16"\n"
         "\tListening port: %"PRIuFAST16"\n"
         "\tDirection: %"PRIu8"\n"
         "\tX: %f\n"
         "\tY: %f\n"
         "\tZ: %f\n"
         ,
         OWL_BOOL_TO_STRING(options.daemon),
         OWL_BOOL_TO_STRING(options.verbose),
         options.dest_host,
         options.dest_port,
         options.iface,
         options.delay,
         options.nb_pkt,
         options.pkt_size,
         options.flood_delay,
         OWL_BOOL_TO_STRING(options.add_flood_delay),
         options.nb_requests,
         options.listening_port,
         options.direction,
         options.x,
         options.y,
         options.z
          ) ;
}



void create_socket()
{
  sockfd =
    owl_create_trx_socket(options.dest_host, options.dest_port,
                          &server, options.iface) ;
}



/*
 * Prepares a new request and sends it.
 */
void send_request()
{
  make_packet() ;
  owl_send_request(sockfd, &server, packet, packet_size,
                   options.nb_pkt, options.delay, options.verbose) ;
  free(packet) ;
}



/*
 * Creates the packet to send.
 */
void make_packet()
{
  uint_fast16_t offset ; // Index used to create the packet

  if (is_calibration_request) // Calibration packet
    {
      if (options.verbose)
        printf("\nPreparing calibration request packet...\n") ;

      packet_size =
        sizeof(uint8_t) * 2 + sizeof(owl_timestamp) + sizeof(float) * 3 +
        sizeof(uint16_t) * 2 ;
      add_padding() ;
      packet = malloc(packet_size) ;
      if (! packet)
        {
          perror("Cannot allocate memory") ;
          abort() ;
        }

      offset = initialise_common_fields(OWL_REQUEST_CALIBRATION) ;
      offset += initialise_calibration_fields(offset) ;
    }

  else // Standard packet
    {
      if (options.verbose)
        printf("\nPreparing request packet...\n") ;

      packet_size =
        sizeof(uint8_t) + sizeof(owl_timestamp) + sizeof(uint16_t) * 2 ;
      add_padding() ;
      packet = malloc(packet_size) ;
      if (! packet)
        {
          perror("Cannot allocate memory") ;
          abort() ;
        }

      offset = initialise_common_fields(OWL_REQUEST_NORMAL) ;
    }

  // Initialize padding bytes with 0xFF:
  while (offset < packet_size)
    packet[offset++] = 0xFF ;
}



/*
 * Increases the packet size to add padding.
 */
void add_padding()
{
  if (options.pkt_size > packet_size)
    packet_size = options.pkt_size ;

  if (options.verbose)
    printf("Packet size: %"PRIuFAST16"\n", packet_size) ;
}



/*
 * Initialises the fields of a normal positioning request.
 */
uint_fast16_t initialise_common_fields(const uint_fast8_t packet_type)
{
  uint_fast16_t offset = 0 ;
  uint16_t npkt ;
  owl_timestamp request_time ;
  char request_time_str[OWL_TIMESTAMP_STRLEN] ;

  // Get the current time and copy it as a string before to switch it to
  // network endianness:
  owl_timestamp_now(&request_time) ;
  owl_timestamp_to_string(&request_time, request_time_str) ;
  owl_hton_timestamp(&request_time) ;

  // Packet type:
  memset(&packet[offset++], packet_type, 1) ;

  // Number of the current packet (1 for the first):
  npkt = htons(1u) ;
  memcpy(&packet[offset], &npkt, sizeof(uint16_t)) ;
  offset += sizeof(uint16_t) ;

  // Number of packets:
  npkt = htons(options.nb_pkt) ;
  memcpy(&packet[offset], &npkt, sizeof(uint16_t)) ;
  offset += sizeof(uint16_t) ;

  // Request time:
  memcpy(&packet[offset], &request_time, sizeof(request_time)) ;
  offset += sizeof(request_time) ;

  if (options.verbose)
    printf("Packet timestamp: %s\n", request_time_str) ;

  return offset ;
}



/*
 * Initialises the calibration data fields.
 */
uint_fast16_t initialise_calibration_fields(uint_fast16_t offset)
{
  float x, y, z ;

  // Direction:
  packet[offset++] = options.direction ;

  if (options.verbose)
    printf("Direction = %d, X = %f, Y = %f, Z = %f\n",
           packet[offset - 1], options.x, options.y, options.z) ;

  // Convert the coordinates to the network endianness:
  x = owl_htonf(options.x) ;
  y = owl_htonf(options.y) ;
  z = owl_htonf(options.z) ;

  // Copy the coordinates to the packet:
  memcpy(&packet[offset], &x, sizeof(float)) ;
  offset += sizeof(float) ;
  memcpy(&packet[offset], &y, sizeof(float)) ;
  offset += sizeof(float) ;
  memcpy(&packet[offset], &z, sizeof(float)) ;
  offset += sizeof(float) ;

  return offset ;
}



#ifdef OWLPS_CLIENT_RECEIVES_POSITION
/*
 * Receives a position computed by the infrastructure.
 * Note that it is currently not guaranteed that the received result
 * correspond to the request sent.
 * Returns 0, or a non-zero value in case of error.
 */
int receive_position()
{
  owl_result *result ;

  printf("Waiting for the result from the infrastructure...\n") ;

  sockfd = owl_create_udp_listening_socket(options.listening_port) ;
  if (sockfd < 0)
    return OWL_ERR_SOCKET_CREATE ;

  result = owl_receive_position(sockfd) ;
  if (result == NULL)
    return OWL_ERR_SOCKET_RECV ;

  close(sockfd) ;
  owl_print_result(result) ;
  owl_free_result(result) ;

  return 0 ;
}
#endif // OWLPS_CLIENT_RECEIVES_POSITION



/*
 * Prints the usage message on the standard output.
 *
 * /!\ Don't forget to update the documentation when modifying something
 *     here!
 */
void print_usage()
{
  printf("Usage:\n"
         "Localisation request:\n"
         "\t%s"
         " [-v | -q]"
         " -i dest_host"
         " [-p dest_port]"
         " [-I iface]"
         " [-t delay]"
         "\n\t"
         " [-n nb_packets]"
         " [-s packet_size]"
         " [-F [[+]delay] [-N nb_requests] [-D]]"
         "\n\t"
         " [-l [port]]\n"
         "Calibration request:\n"
         "\t%s"
         " [-v | -q]"
         " -i dest_host"
         " [-p dest_port]"
         " [-I iface]"
         " [-t delay]"
         "\n\t"
         " [-n nb_packets]"
         " [-s packet_size]"
         " [-F [[+]delay] [-N nb_requests] [-D]]"
         "\n\t"
         " direction x y z\n"

         "Options:\n"
         "\t-h\t\tPrint this help.\n"
         "\t-V\t\tPrint version information.\n"
         "\t-v\t\tTurn on verbose mode (default).\n"
         "\t-q\t\tDo not print informational messages and some (less\n"
         "\t\t\timportant) warnings.\n"
         "\t-i dest_host\tName or IP address of the destination host of"
         " the\n\t\t\tlocalisation request.\n"
         "\t-p dest_port\tDestination port of the localisation request"
         " (default:\n\t\t\t%d).\n"
         "\t-t delay\tTime between each packet transmission in"
         " milliseconds\n\t\t\t(default: %d ms for a normal request,"
         " %d ms for a\n\t\t\tcalibration request).\n"
         "\t-n nb_packets\tNumber of packet transmitted for the request"
         " (default:\n\t\t\t%d for a normal request, %d for a"
         " calibration request).\n"
         "\t-s packet_size\tData size of the transmitted packets. The"
         " minimal value\n\t\t\tis the size of the request's data"
         " fields; if\n\t\t\t<packet_size> is less than this size, it is"
         " ignored.\n\t\t\tNote that this size does not take into"
         " account the\n\t\t\theaders, so the whole 802.11 frame will be"
         " bigger.\n"
         "\t-I iface\tName of the network interface used to transmit the"
         "\n\t\t\trequest (e.g. \"eth2\"). If this option is absent, the"
         "\n\t\t\tinterface is selected automatically. You must be root"
         " to\n\t\t\tuse this option.\n"
         "\t-F [delay]\t\"Flood mode\": loop indefinitely, sending a"
         " new request\n\t\t\tevery <delay> milliseconds (default:"
         " %d ms). If\n\t\t\t<delay> starts with a +, it is the time"
         " between two\n\t\t\trequests instead of the time between the"
         " start of the\n\t\t\ttransmission of two requests.\n"
         "\t-N nb_requests\tWith -F, stop after <nb_requests> requests"
         " transmitted\n\t\t\tinstead of looping indefinitely.\n"
         "\t-D\t\tDaemon mode. Useful only in flood mode.\n"
         "\t-l [port]\tWait for the computed position and display it."
         " The\n\t\t\toptional argument <port> allows to specify the"
         " listening\n\t\t\tport (default: %d). Available only if the"
         " program was\n\t\t\tcompiled with the compilation-time option"
         "\n\t\t\tOWLPS_CLIENT_RECEIVES_POSITION.\n"
         ,
         program_name,
         program_name,
         OWL_DEFAULT_REQUEST_PORT,
         DEFAULT_DELAY_NORMAL,
         DEFAULT_DELAY_CALIB,
         DEFAULT_NBPKT_NORMAL,
         DEFAULT_NBPKT_CALIB,
         DEFAULT_FLOOD_DELAY,
         OWL_DEFAULT_RESULT_PORT
         ) ;
}


void print_version()
{
  printf("This is OwlPS Client, part of the Owl Positioning System"
         " project.\n"
         "Version: %s.\n"
         "Compilation-time options:\n"
         "\tOption -l: %s.\n",
#ifdef OWLPS_VERSION
         OWLPS_VERSION
#else // OWLPS_VERSION
         "unknown version"
#endif // OWLPS_VERSION
         ,
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
         "YES"
#else // OWLPS_CLIENT_RECEIVES_POSITION
         "NO"
#endif // OWLPS_CLIENT_RECEIVES_POSITION
         ) ;
}
