/*
 * Ticker-tape client.
 *
 * g++ -g -I/usr/local/include/sfs -I/usr/local/include ticker-client.C ticker.C -L/usr/local/lib -L/usr/local/lib/sfs -lasync -larpc -ldmalloc -o ticker-client
 */

#include "amisc.h"
#include "async.h"
#include "arpc.h"
#include "ticker.h"
#include "dns.h"

class TC {
public:
  TC();
  void start_one(char *host, int port, str msg);

private:
  int to_do;
  int n_finished;
  int n_ok;

  int fd;
  ptr<axprt> x;
  ptr<aclnt> c;

  void start_cb(int port, str msg, ptr<hostent>, int err);
  void submit_call(struct sockaddr_in *sin, str msg);
  void submit_done(submit_result *res, struct sockaddr_in *sin, clnt_stat err);
  void done_one(int ok);
};

TC::TC()
{
  to_do = 0;
  n_finished = 0;
  n_ok = 0;

  // Create just one UDP socket.
  // We'll be making all RPCs, and receiving all replies,
  // on the one socket.
  fd = inetsocket(SOCK_DGRAM);
  if(fd < 0){
    fprintf(stderr, "ticker-client: inetsocket failed: %s\n",
            strerror(errno));
    exit(1);
  }
  x = axprt_dgram::alloc(fd);
  c = aclnt::alloc(x, ticker_prog_1);
}

// We've completely finished one of the RPCs;
// decide whether it's time to exit the program.
void
TC::done_one(int ok)
{
  n_finished += 1;
  if(ok)
    n_ok += 1;
  if(n_ok >= to_do){
    exit(0);
  }
  if(n_finished >= to_do){
    exit(1);
  }
}

// Start processing one of the sets of command-line
// arguments by looking up the server's host name.
void
TC::start_one(char *host, int port, str msg)
{
  to_do++;
  dns_hostbyname(host, wrap(this, &TC::start_cb, port, msg), true, true);
}

// The DNS lookup for the server's host name completed,
// so start the RPC.
// Note we have to dynamically allocate the sockaddr_in,
// because c->call() keeps a copy of the pointer for
// retransmissions.
void
TC::start_cb(int port, str msg, ptr<hostent> h, int err)
{
  if(h){
    struct sockaddr_in *sin = new sockaddr_in;
    bzero(sin, sizeof(*sin));
    sin->sin_family = AF_INET;
    sin->sin_port = htons(port);
    sin->sin_addr = *(in_addr*)h->h_addr;
    submit_call(sin, msg);
  } else {
    fprintf(stderr, "ticker-client: could not look up host name\n");
    done_one(0);
  }
}

// We just got the reply to a submit RPC, or a timeout.
void
TC::submit_done(submit_result *res, struct sockaddr_in *sin, clnt_stat err)
{
  if(err || res->ok != 1){
    fprintf(stderr, "ticker-client: RPC failed: err=%d ok=%d errno=%d\n",
            err, res->ok, errno);
    done_one(0);
  } else {
    done_one(1);
  }
  delete res;
  delete sin;
}

// Give the servers a chance to run.
void
force_context_switch()
{
  int pid, status;

  pid = fork();
  if(pid == 0){
    exit(0);
  } else if(pid > 0){
    waitpid(pid, &status, 0);
  }
}

// Send a submit RPC to a server.
void
TC::submit_call(struct sockaddr_in *sin, str msg)
{
  submit_args arg;
  submit_result *res = new submit_result;

  arg.msg = msg;

  c->call(TICKER_SUBMIT, &arg, res,
          wrap(this, &TC::submit_done, res, sin),
          (AUTH *) 0,
          (xdrproc_t) 0, (xdrproc_t) 0,
          (u_int32_t) 0, (u_int32_t) 0,
          (struct sockaddr *) sin);

  force_context_switch();
}

void
usage()
{
  fprintf(stderr, "Usage: ticker-client host1 port1 tag1 ...\n");
  fprintf(stderr, "       ticker-client -r # host1 port ...\n");
  exit(1);
}

main(int argc, char **argv)
{
  int i = 1;
  int n = 1;
  int iter, seq = 0;
  
  if(argc < 4)
    usage();

  TC *tc = new TC();

  if(strcmp(argv[1], "-r") == 0){
    // Generate n RPCs, quickly.
    n = atoi(argv[2]);
    i += 2;
    for(iter = 0; iter < n; iter++){
      int j;
      for(j = i; j+1 < argc; j += 2){
        char buf[64];
        sprintf(buf, "t-%d", seq++);
        tc->start_one(argv[j], atoi(argv[j+1]), buf);
      }
    }
  } else {
    // Just send the RPCs indicated on the command line.
    int j;
    for(j = i; j+2 < argc; j += 3){
      tc->start_one(argv[j], atoi(argv[j+1]), argv[j+2]);
    }
  }

  amain();
}
