/*========================================================================
 * Copyright (C) GemTalk Systems 1986-2014.  All Rights Reserved.
 *
 *  Name - gsstat.c - a sample program for the gcsi interface
 *
 * $Id: gsstat.cc 32752 2014-02-28 22:11:10Z stever $
 *
 *========================================================================
 */
#include "flag.ht"

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "shrpcstats.ht"
#include "gcsierr.ht"
#include "gcsi.hf"

class GsCacheConnection {
public:
  char errBuf[200];
  char cacheName[200];
  char stoneName[200];
  int interval;
  int linesPrinted;

  BoolType initFromArgs(int argc, char *argv[]);
  BoolType printStats(void);
  void printBannerIfNeeded(void);
  void printBanner(void);
  void serviceLoop(void);
  BoolType attachCache(void);

private:
  BoolType setCacheName(const char *nameFromUser);

};

BoolType GsCacheConnection::attachCache(void)
{
  int result = 0;
  if (cacheName[0] != '\0') { // we have a fully qualified cache name from the user
    result = GcsiAttachSharedCache(cacheName, errBuf, sizeof(errBuf));
  }
  else { // user only specifed the stone name
    result = GcsiAttachSharedCacheForStone(stoneName, errBuf, sizeof(errBuf));
  }
  return result == GCSI_SUCCESS;
}

BoolType GsCacheConnection::setCacheName(const char *nameFromUser)
{
  strncpy(stoneName, nameFromUser, sizeof(stoneName));
  const char *tilde = strchr(nameFromUser, '~');
  if (tilde != NULL) { // this is a full cache name
    stoneName[tilde - nameFromUser] = '\0';
    strncpy(cacheName, nameFromUser, sizeof(cacheName));
  }
  else { 
   // nameFromUser is only the stonename, which is all we need in GS64 3.x
  }

  return TRUE;
}

BoolType GsCacheConnection::initFromArgs(int argc, char *argv[])
{
  memset(this, 0, sizeof(*this));
  linesPrinted = -1;
  GcsiInit();

  if (!setCacheName(argv[1]))
    return FALSE;

  if (argc == 2) // print 1 sample only
    interval = -1;
  else {
    interval = atoi(argv[2]);
    if (interval < 1)
      return FALSE;
  }
  return TRUE;
}

void GsCacheConnection::printBanner(void)
{
  printf("\n");
  printf("Sess     CR    PNR        PD       DNR      FP OCS     FF\n");
  printf("---------------------------------------------------------\n");
}


void GsCacheConnection::printBannerIfNeeded(void)
{
  if (linesPrinted != 20 && linesPrinted != -1)
    return;
  
  printBanner();
  linesPrinted = 0;
}


BoolType GsCacheConnection::printStats(void)
{
  GcsiResultSType stnStats;
  int numStats = 2;
  unsigned int numCr = 0; // commit records
  unsigned int pnr = 0; // pages need reclaiming
  uint64_t pd = 0; // possible dead
  uint64_t dnr = 0; // dead not reclaimed
  unsigned int sess = 0; // number of sessions on stone
  unsigned int fp = 0; // free pages in stone
  int oldCr = 0; // oldest CR session


  unsigned int mask = SHRPC_STONE;
  int result = GcsiAllStatsForMask(mask, &stnStats, &numStats);
  BoolType hasStone = TRUE;

  switch (result) {
  case GCSI_SUCCESS:
    break;

  case GCSI_ERR_NO_STONE:
    hasStone = FALSE;
    break;
 
  default:
    return FALSE;
    break;
  }

  if (hasStone) {
    ShrPcStnStatSType *s = &stnStats.stats.u.stone;
    sess = s->totalSessionsCount;
    numCr = s->numberOfCommitRecords;
    pnr = s->pagesNeedReclaimingSize;
    pd = s->possibleDeadObjs;
    dnr = s->deadNotReclaimedObjs;
    fp = s->freePages;
    oldCr = s->oldestCrSession;
    if (oldCr == 0)
      oldCr = s->oldestCrSessNotInTrans;
  }

  // now get the free frame couont
  unsigned int ff = 0;
  result = GcsiShrPcMonStatAtOffset(offsetof(ShrPcMonStatSType, numberOfFreeFrames), &ff);
  if (result != GCSI_SUCCESS)
    return FALSE;


 if (hasStone) 
   printf("%4u %6u %6u %9lu %9lu %7u %3u %6u\n", sess, numCr, pnr, pd, dnr, fp, oldCr, ff);
 else // remote shared cache, no stone stats available
   printf("   ?      ?      ?         ?         ?        ?    ? %6u\n", ff);

 fflush(stdout);
 ++linesPrinted;
 return TRUE;
}

void GsCacheConnection::serviceLoop(void)
{
  int sleepMs = interval * 1000;
  for (;;) {
    printBannerIfNeeded();
    if (!printStats()) {
      printf("  *** cache shutdown detected.  Goodbye. ***\n");
      break;
    }
    if (poll(NULL, 0, sleepMs) == -1)
      break; // probably a ctrl-C signal, die now
  }
}

static void usage(char *argv[])
{
  fprintf(stderr, "Usage: %s <cache or stone name> <interval>\n", argv[0]);
  exit(1);
}

int main(int argc, char *argv[])
{
  int i;
  int exitCode = 0;
  if (argc < 2 || argc > 3) 
    usage(argv); // does not return

  GsCacheConnection conn;
  if (!conn.initFromArgs(argc, argv))
    usage(argv); // does not return

  if (!conn.attachCache()) {
    printf("Attach to cache named %s failed\n", 
	   conn.cacheName);
    exit(1);
  }

  if (conn.interval == -1) {
    conn.printBanner();
    conn.printStats();
    exit(0);
  }

  conn.serviceLoop();
  exit(0);
}


