/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/time.h>

#include <assert.h>

static size_t build_message(char *message, uint32_t s, uint32_t us) {
  char const *msg_prefix = "(4:post2:q1(4:post0:8:";
  char const *msg_suffix = "0:)0:)";
  size_t prefix_len = strlen(msg_prefix);
  size_t suffix_len = strlen(msg_suffix);
  uint32_t v;
  size_t total_len = 0;

  memcpy(message + total_len, msg_prefix, prefix_len);
  total_len += prefix_len;
  v = htonl(s);
  memcpy(message + total_len, &v, sizeof(uint32_t));
  total_len += sizeof(uint32_t);
  v = htonl(us);
  memcpy(message + total_len, &v, sizeof(uint32_t));
  total_len += sizeof(uint32_t);
  memcpy(message + total_len, msg_suffix, suffix_len);
  total_len += suffix_len;

  /*
  printf("%d<<", total_len);
  fwrite(message, total_len, 1, stdout);
  printf(">>\n");
  */
  return total_len;
}

int main(int argc, char *argv[]) {
  int fd = socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in s;
  FILE *f;
  struct timeval start_time;
  long bytecount = 0;
  int i;
  unsigned long hz_limit = 1000000;
  unsigned long msgcount = 10000000;

  assert(sizeof(uint32_t) == 4);

  if (argc < 2) {
    fprintf(stderr, "Usage: test1 <serverhostname> [<hz_limit> [<msgcount>]]\n");
    exit(1);
  }

  if (argc > 2) {
    hz_limit = strtoul(argv[2], NULL, 0);
  }
  printf("hz_limit = %lu\n", hz_limit);

  if (argc > 3) {
    msgcount = strtoul(argv[3], NULL, 0);
  }
  printf("msgcount = %lu\n", msgcount);

  {
    struct hostent *h = gethostbyname(argv[1]);
    if (h == NULL) {
      fprintf(stderr, "serverhostname lookup: %d\n", h_errno);
      exit(1);
    }
    s.sin_family = AF_INET;
    s.sin_addr.s_addr = * (uint32_t *) h->h_addr_list[0];
    s.sin_port = htons(5671);
  }

  if (connect(fd, (struct sockaddr *) &s, sizeof(s)) < 0) return 1;

  {
    int i = 1;
    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i));
  }

  f = fdopen(fd, "a+");

  fprintf(f, "(9:subscribe5:test30:0:5:test35:login)");
  fflush(f);

  usleep(100000);
  {
    char buf[4096];
    size_t n = read(fd, buf, sizeof(buf));
    printf("Received: <<%.*s>>\n", (int) n, buf);
  }

  gettimeofday(&start_time, NULL);

  for (i = 0; i < msgcount; i++) {
    char message[1024];
    size_t msglen;
    while (1) {
      struct timeval now;
      double delta;
      gettimeofday(&now, NULL);
      delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0;
      if (i / delta <= hz_limit) break;
      fflush(f);
      usleep(1000);
    }
    if ((i % (hz_limit / 4)) == 0) {
      struct timeval now;
      gettimeofday(&now, NULL);
      msglen = build_message(message, now.tv_sec, now.tv_usec);
    } else {
      msglen = build_message(message, 0, 0);
    }
    fwrite(message, msglen, 1, f);
    bytecount += msglen;
    if ((bytecount % 100000) < msglen) {
      struct timeval now;
      double delta;
      gettimeofday(&now, NULL);
      delta = (now.tv_sec - start_time.tv_sec) + (now.tv_usec - start_time.tv_usec) / 1000000.0;
      printf("So far sent %ld bytes in %g seconds = %g bytes/sec and %g msgs/sec\n",
	     bytecount,
	     delta,
	     bytecount / delta,
	     bytecount / (delta * msglen));
      fflush(stdout);
    }
  }

  fprintf(f, "(11:unsubscribe5:test3)");
  fflush(f);

  fclose(f);

  return 0;
}
