#include <stdio.h>
#include <math.h>
#include <tmp/libpq-fe.h>
#include <tmp/pqthreads.h>

char *prog_name = "<>";
extern char *PQhost, *PQport;

typedef struct {
  int threadNo;
  char *dbHost;
  char *dbPort;
  char *dbName;
  char *userName;
  char *userHost;
  char *userPort;
} test_arg_t;

void *
run_test(void *arg)
{
  test_arg_t *ta = (test_arg_t *)arg;
  int n_groups, n_tuples, n_fields, i;
  PortalBuffer *pbuf;
  char query[1024];
  char *owner_id;

#define DIE(M) {printf("** Fatal error in thread#%d dbHost=%s dbPort=%s dbName=%s userName=%s\n** ",ta->threadNo,ta->dbHost,ta->dbPort,ta->dbName,ta->userName);printf(M);fflush(stdout);pthread_exit((void*)0);}
#define DIE_1(M,A) {printf("** Fatal error in thread#%d dbHost=%s dbPort=%s dbName=%s userName=%s\n** ",ta->threadNo,ta->dbHost,ta->dbPort,ta->dbName,ta->userName);printf(M,A);fflush(stdout);pthread_exit((void*)0);}
#define DIE_2(M,A1,A2) {printf("** Fatal error in thread#%d dbHost=%s dbPort=%s dbName=%s userName=%s\n** ",ta->threadNo,ta->dbHost,ta->dbPort,ta->dbName,ta->userName);printf(M,A1,A2);fflush(stdout);pthread_exit((void*)0);}

  printf("thread # %d running\n",  ta->threadNo);

  PQsetconnection(ta->userName, ta->userHost, ta->userPort);
  PQexec(" ");

  printf("thread#%d finding all models owned by user %s in %s@%s:%s...\n",
	 ta->threadNo, ta->userName, ta->dbName, ta->dbHost, ta->dbPort);

  PQexec("begin");
  sprintf(query, "retrieve portal dportal (models.id) where models.title=\"%s\"", ta->userName);
  PQexec(query);
  PQexec("fetch all in dportal");
  pbuf = PQparray("dportal");
  if (!pbuf) {
    DIE("error getting pointer to dportal");
  }
  n_groups = PQngroups(pbuf);
  if (n_groups != 1) {
    DIE_1("got back %d groups, was expecting 1", n_groups);
  }
  n_tuples = PQntuplesGroup(pbuf, 0);
  if (n_tuples != 1) {
    DIE_1("got back %d tuples, was expecting 1", n_tuples);
  }
  n_fields = PQnfieldsGroup(pbuf, 0);
  if (n_fields != 1) {
    DIE_1("got back %d fields, was expecting 1", n_fields);
  }
  owner_id = PQgetvalue(pbuf, 0, 0);
  if (!owner_id) {
    DIE_1("got back null owner id for user %s",ta->userName);
  }
  printf("thread#%d: owner model id is %s\n",ta->threadNo, owner_id);
  PQexec("close dportal");
  PQexec("end");

  printf("thread#%d switching to %s@%s:%s from %s@%s:%s\n",
	 ta->dbName, ta->dbHost, ta->dbPort,
	 ta->userName, ta->userHost,  ta->userPort);

  PQsetconnection(ta->dbName, ta->dbHost,ta->dbPort);
  PQexec(" ");

  PQexec("begin");
  sprintf(query, "retrieve portal dportal (m.all) from m in models,o in ownerLinks where o.target=\"%s\" and o.containingModel=m.id",owner_id);
  PQexec(query);
  PQexec("fetch all in dportal");
  pbuf = PQparray("dportal");
  n_groups = PQngroups(pbuf);
  if (n_groups != 1) {
    DIE_1("got back %d groups, was expecting 1", n_groups);
  }
  n_fields = PQnfieldsGroup(pbuf, 0);
  if (n_fields != 7) {
    DIE_2("tuple#%d has %d fields instead of 7",i,n_fields);
  }
  n_tuples = PQntuplesGroup(pbuf, 0);
  printf("%d tuples returned:\n",n_tuples);
  printf("id\tctime\tmtime\twtime\tstatus\ttitle\tatype\n");
  for (i = 0; i < n_tuples; i++) {
    printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", PQgetvalue(pbuf, i, 0),
	   PQgetvalue(pbuf, i, 1), PQgetvalue(pbuf, i, 2), PQgetvalue(pbuf, i, 3),
	   PQgetvalue(pbuf, i, 4), PQgetvalue(pbuf, i, 5), PQgetvalue(pbuf, i, 6));
  }
  PQexec("close dportal");
  PQexec("end");
  PQfinish();
  printf("thread#%d OK\n", ta->threadNo);
  if (ta->threadNo > 0)
    pthread_exit((void *)0);
}

main(argc, argv)
     int argc;
     char **argv;
{
  THREAD_T t1,t2,t3;
  test_arg_t test1, test2, test3;
  int i, n_tests;

  if (argc != 5) {
    printf("%s: need more arguments\nusage: %s username port# dbhost #tests\n",
	   argv[0], argv[0]);
    exit(1);
  }
  prog_name = argv[0];
  if (sscanf(argv[4], "%d", &n_tests) != 1) {
    printf("%s: last argument did not scan\nusage: %s username port# dbhost #tests\n",
	   argv[0], argv[0]);
    exit(1);
  } else if (n_tests < 0 || n_tests > 1000) {
    printf("%s: #tests out of range [%d]\nusage: %s username port# dbhost #tests\n",
	   argv[0], n_tests, argv[0]);
    exit(1);
  }

  printf("%s - single thread against multiple databases [repeated]\n\n",
	 prog_name);

  test1.threadNo = 0;
  test1.dbName = argv[1];
  test1.dbPort = argv[2];
  test1.dbHost = argv[3];
  test1.userName = argv[1];
  test1.userPort = argv[2];
  test1.userHost = argv[3];

  test2.threadNo = 0;
  test2.dbName = "ndim_public";
  test2.dbPort = "4300";
  test2.dbHost = "tika.ndim.edrc.cmu.edu";
  test2.userName = argv[1];
  test2.userPort = argv[2];
  test2.userHost = argv[3];

  test3.threadNo = 0;
  test3.dbName = "ndim_published";
  test3.dbPort = "4400";
  test3.dbHost = "tika.ndim.edrc.cmu.edu";
  test3.userName = argv[1];
  test3.userPort = argv[2];
  test3.userHost = argv[3];

  for (i =  0; i < n_tests; i++) {
    run_test((void *)&test1);
    run_test((void *)&test2);
    run_test((void *)&test3);
  }
  printf("done\n");
  exit(0);
}
