
#include "mdb.h"
#include "scan.h"
#include "match_string.h"
#include "SDDS.h"
#include <time.h>
#include <sys/timeb.h>
#include "lanio.h"
#include <memory.h>

static char *USAGE = "hpSocketBurp  -hostname=<host> [<request-file>] <snapshot-file> \n\
        -verbose\n\
Saves (i.e. backs-up) or restores operating parameters of HP instruments.\n\
hostname              sends commands to the host specified.\n\
                      e.g. -hostname=hpvecboo or -host=hpvecsr\n\
request-file          If two filenames are present the first one is assumed\n\
                      to be the request file with column ControlName defined.\n\
snapshot-file         If two filenames are present then the second one\n\
                      is the output snapshot file. The two columns written are\n\
                      ControlName and ValueString. If only one file is\n\
                      present on the command line, then the contents of this\n\
                      file is written to the analyser. Columns ControlName and \n\
                      ValueString must be present. \n\
verbose               lists the HP-IB commands issued.\n\
Program by Louis Emery, ANL (Oct. 1994)\n";
#define BUFFER_LENGTH 512

#define CLO_VERBOSE 0
#define CLO_HOST 1
#define COMMANDLINE_OPTIONS 2
char *commandline_option[COMMANDLINE_OPTIONS] = {
  "verbose",
  "hostname"};

long verbose;

long openSocket(char *hostname, long portNumber);
char *queryInstrument(FILE *file, char *command, char *result, size_t maxLength);
long queryInstrumentBinary(FILE *file, char *command, double **array);
long commandInstrument(FILE *file, char *command);

int main(int argc, char **argv) {
  SCANNED_ARG *s_arg;
  long i, i_arg;
  long n_rows;
  char *requestfile, *snapshotfile;
  long instrument;
  FILE *instFile;
  long backup = 0; /* two modes: back-up mode (backup=1) and restore mode (backup=0)*/

  SDDS_TABLE request_page;
  SDDS_TABLE snapshot_page;
  char **ControlNames, **ValueString;
  char *command, *charBuf;
  char *hostname;

  long buffer_length;

  buffer_length = BUFFER_LENGTH;
  charBuf = (char *)malloc(buffer_length * sizeof(char));
  command = (char *)malloc(buffer_length * sizeof(char));

  argc = scanargs(&s_arg, argc, argv);
  if (argc < 2) {
    bomb(NULL, USAGE);
  }
  requestfile = NULL;
  snapshotfile = NULL;
  hostname = NULL;
  verbose = 0;
  for (i_arg = 1; i_arg < argc; i_arg++) {
    if (s_arg[i_arg].arg_type == OPTION) {
      switch (match_string(s_arg[i_arg].list[0], commandline_option, COMMANDLINE_OPTIONS, UNIQUE_MATCH)) {
      case CLO_VERBOSE:
        verbose = 1;
        break;
      case CLO_HOST:
        if (s_arg[i_arg].n_items < 2)
          SDDS_Bomb("invalid -hostName syntax");
        if (!(hostname = s_arg[i_arg].list[1]))
          bomb("No string given in option -hostname", NULL);
        break;
      default:
        bomb("unrecognized option given", USAGE);
      }
    } else {
      if (!snapshotfile) {
        snapshotfile = s_arg[i_arg].list[0];
        backup = 0;
      } else if (!requestfile) {
        requestfile = snapshotfile;
        snapshotfile = s_arg[i_arg].list[0];
        backup = 1;
      } else
        bomb("too many filenames given", USAGE);
    }
  }

  if ((instrument = openSocket(hostname, 5025)) < 0)
    return 1;

  if (!(instFile = fdopen(instrument, "r+"))) {
    fprintf(stderr, "Unable to create file * structure : %s\n",
            strerror(errno));
    return 1;
  }

  if (backup) {
    if (!SDDS_InitializeInput(&request_page, requestfile) ||
        !SDDS_InitializeOutput(&snapshot_page, SDDS_ASCII, 1, "hpSocketBurp snapshot file",
                               "snapshot file", snapshotfile))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    switch (SDDS_CheckColumn(&request_page, "ControlName", NULL, SDDS_STRING, verbose ? stdout : NULL)) {
    case SDDS_CHECK_NONEXISTENT:
    case SDDS_CHECK_WRONGTYPE:
    case SDDS_CHECK_WRONGUNITS:
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      exit(1);
      break;
    }
    if (!SDDS_TransferColumnDefinition(&snapshot_page, &request_page, "ControlName", NULL) ||
        0 > SDDS_DefineColumn(&snapshot_page, "ValueString", "ValueString", NULL, "Value in string form",
                              NULL, SDDS_STRING, 0) ||
        !SDDS_WriteLayout(&snapshot_page))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  } else {
    if (!SDDS_InitializeInput(&snapshot_page, snapshotfile))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    switch (SDDS_CheckColumn(&snapshot_page, "ControlName", NULL, SDDS_STRING, verbose ? stdout : NULL)) {
    case SDDS_CHECK_NONEXISTENT:
    case SDDS_CHECK_WRONGTYPE:
    case SDDS_CHECK_WRONGUNITS:
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      exit(1);
      break;
    }
    switch (SDDS_CheckColumn(&snapshot_page, "ValueString", NULL, SDDS_STRING, verbose ? stdout : NULL)) {
    case SDDS_CHECK_NONEXISTENT:
    case SDDS_CHECK_WRONGTYPE:
    case SDDS_CHECK_WRONGUNITS:
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      exit(1);
      break;
    }
  }

  if (backup) {
    while (0 < SDDS_ReadTable(&request_page)) {
      n_rows = SDDS_CountRowsOfInterest(&request_page);
      ValueString = (char **)malloc(n_rows * sizeof(char *));
      ControlNames = (char **)SDDS_GetColumn(&request_page, "ControlName");
      for (i = 0; i < n_rows; i++) {
        sprintf(command, "%s?", ControlNames[i]);
        if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
          printf("Problem with query command: %s.\n", command);
          exit(1);
        }
        SDDS_CopyString(&ValueString[i], charBuf);
        replace_chars(ValueString[i], "\"", "'");
      }
      if (!SDDS_StartTable(&snapshot_page, n_rows) ||
          !SDDS_SetColumn(&snapshot_page, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                          ValueString, n_rows, "ValueString") ||
          !SDDS_SetColumn(&snapshot_page, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                          ControlNames, n_rows, "ControlName") ||
          !SDDS_WriteTable(&snapshot_page))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    }
    if (!SDDS_Terminate(&request_page) ||
        !SDDS_Terminate(&snapshot_page))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  } else {
    while (0 < SDDS_ReadTable(&snapshot_page)) {
      n_rows = SDDS_CountRowsOfInterest(&snapshot_page);
      ControlNames = (char **)SDDS_GetColumn(&snapshot_page, "ControlName");
      ValueString = (char **)SDDS_GetColumn(&snapshot_page, "ValueString");
      for (i = 0; i < n_rows; i++) {
        sprintf(command, "%s %s", ControlNames[i], ValueString[i]);
        if (!commandInstrument(instFile, command)) {
          printf("Problem with command: %s.\n", command);
          exit(1);
        }
      }
    }
    if (!SDDS_Terminate(&snapshot_page))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  }
  exit(0);
}

long openSocket(char *hostname, long portNumber) {
  struct hostent *hostPtr;

  struct sockaddr_in peeraddr_in;
  long s;

  memset(&peeraddr_in, 0, sizeof(struct sockaddr_in));
  /*    {
        long address;
        address = inet_addr((char*)hostname);
        hostPtr=gethostbyaddr((char*)&address,4,0);
        }
  */
  hostPtr = gethostbyname((char *)hostname);
  if (hostPtr == NULL) {
    fprintf(stderr, "unable to resolve hostname '%s'\n", hostname);
    return (-1);
  }
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s == -1) {
    fprintf(stderr, "unable to create socket to '%s': %s\n",
            hostname, strerror(errno));
    return (-1);
  }

  memcpy(&peeraddr_in.sin_addr.s_addr, hostPtr->h_addr, hostPtr->h_length);
  peeraddr_in.sin_family = AF_INET;
  peeraddr_in.sin_port = htons(portNumber);
  if (connect(s, (const struct sockaddr *)&peeraddr_in, sizeof(struct sockaddr_in)) == -1) {
    fprintf(stderr, "unable to create socket to '%s': %s\n",
            hostname, strerror(errno));
    return (-1);
  }
  return s;
}

char *queryInstrument(FILE *file, char *command, char *result, size_t maxLength) {
  long length;

  if (verbose) {
    printf("%30s  ", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0)
    return NULL;
  fflush(file);
  rewind(file);

  if (fgets(result, maxLength, file) == NULL)
    return NULL;
  fflush(file);
  rewind(file);

  length = strlen(result);
  if (result[length - 1] == '\n')
    result[length - 1] = '\0';
  if (verbose) {
    if (length < 100) {
      printf("%s\n", result);
    } else {
      printf("Results has more than 100 characters\n");
    }
    fflush(stdout);
  }
  return (result);
}

/* query the instrument for an array of binary doubles */
/* also returns the number of points */
long queryInstrumentBinary(FILE *file, char *command, double **array) {
  long ndigits, nitems, idigits;

  if (verbose) {
    printf("%s\n", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0)
    return 0;
  fflush(file);

  getc(file);
  ndigits = getc(file) - '0';
  nitems = 0;
  for (idigits = 0; idigits < ndigits; idigits++) {
    nitems = getc(file) - '0' + 10 * nitems;
  }
  nitems /= 8;

  if (!*array) {
    *array = (double *)malloc(nitems * sizeof(double));
  } else {
    *array = (double *)realloc(*array, nitems * sizeof(double));
  }
  if (fread(*array, 8, nitems, file) != nitems) {
    printf("something wrong with binary read of array.\n");
    return 0;
  }

  fflush(file);

  return (nitems);
}

long commandInstrument(FILE *file, char *command) {
  if (verbose) {
    printf("%s\n", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0)
    return 0;
  fflush(file);

  return 1;
}
