#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;

LOCK_T done_lock;
COND_T done_cond;
int n_done = 0;

#define N_THREADS 3

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 [" THREAD_PRINTF_FMT "]\n",  ta->threadNo,
	 THREAD_PRINTF_ARG(pthread_self()));

  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->threadNo,
	 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("thread#%d %d tuples returned:\n",ta->threadNo,n_tuples);
  printf("thread#%d\ttuple#\tid\tctime\tmtime\twtime\tstatus\ttitle\tatype\n",ta->threadNo);
  for (i = 0; i < n_tuples; i++) {
    printf("thread#%d\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", ta->threadNo, i,
	   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);

  GET_LOCK(done_lock);
  n_done++;
  REL_LOCK(done_lock);
  SIGNAL_COND(done_cond);

  pthread_exit((void *)0);
}

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

  setbuf(stdout, (char *)0);
  setbuf(stderr, (char *)0);

  if (argc != 4) {
    printf("%s: need more arguments\nusage: %s username port# dbhost\n",
	   argv[0], argv[0]);
    exit(1);
  }
  prog_name = argv[0];

  INIT_LOCK(done_lock);
  INIT_COND(done_cond);

  printf("%s - multiple simultaneous threads against multiple databases\n\n",
	 prog_name);

  test1.threadNo = 1;
  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 = 2;
  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 = 3;
  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];

  FORK_THREAD(t1, run_test, (void *)&test1);
  FORK_THREAD(t2, run_test, (void *)&test2);
  FORK_THREAD(t3, run_test, (void *)&test3);
  printf("three threads forked.\n");

  while ((k_stat = WAIT_COND(done_cond, done_lock)) == 0) {
    if (n_done == N_THREADS)
      break;
    REL_LOCK(done_lock);
  }
  if (k_stat != 0) {
    perror("WAIT_COND");
    exit(1);
  }
  printf("%s FINISHED\n", prog_name);
  exit(0);
}
