#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#define MAXSTR  5000
#define MAXPARS 200

#include "nicklib.h"  
#include "getpars.h" 

/** 
 a very simple keyword parameter cracker 
 comments with line starting #  
 syntax:  keyword param(s)   
 not designed to read large databases (needs hash code for that)
 */
void
subcolon (char *ss);
void
stripcomment (char *str);
int
findpname (phandle *pp, char *parname);
int
indxstring (char **ppars, int npars, char *ww);

static int parchange = 0;
static int debug = NO;

phandle *
openpars (char *fname)
/* constructor */
{
  phandle *pp;
  FILE *ff;

  char line[MAXSTR + 1];
  char str[MAXSTR];
  char ww[MAXSTR];
  char rest[MAXSTR];

  char * ppars[MAXPARS];
  char * pdata[MAXPARS];

  int npars = 0, i;
  int len, plen, t;

  pp = (phandle *) malloc (sizeof(phandle));
  ff = pp->fx = fopen (fname, "r");
  if (ff == NULL)
    {
      perror ("Can't open file\n");
      fatalx ("can't open %s\n", fname);
    }

  line[MAXSTR] = '\0'; // defensive programmming
  while (fgets (line, MAXSTR, ff) != NULL)
    {

      len = strlen (line);

      if (isspace(line[len - 1]))
        line[len - 1] = '\0';

      if (first_word (line, ww, rest) > 0)
        {

          if (ww[0] == '#')
            continue;
          /*AT: 12/2/04: Adding check to make sure that the parameter name ends in : */
          plen = strlen (ww);
          if (ww[plen - 1] != ':')
            printf (
                "**warning: dubious parameter, please check the parameter name %s\n",
                ww);
          t = indxstring (ppars, npars, ww);
          if (t >= 0)
            fatalx ("duplicate parameter: %s\n", ww);
          ppars[npars] = strdup (ww);

          striptrail (rest, ' '); /* no trailing blanks */
          stripcomment (rest);
          pdata[npars] = strdup (rest);
          ++npars;

          if (debug)
            printf ("param %d %s %s\n", npars, ppars[npars - 1],
                    pdata[npars - 1]);

        }
    }
  pp->numpars = npars;

  if (npars > 0)
    {
      ZALLOC(pp->ppars, npars, char *);
      ZALLOC(pp->pdata, npars, char *);
    }
  else
    {
      fprintf (stderr, "***warning: no parameters in %s\n", fname);
    }

  for (i = 0; i < npars; i++)
    {
      pp->ppars[i] = strdup (ppars[i]);
      pp->pdata[i] = strdup (pdata[i]);

      /*  printf("zz: %d %s %s\n",i,ppars[i],pp->ppars[i]) ;*/

    }

  for (i = 0; i < npars; i++)
    {
      free (ppars[i]);
      free (pdata[i]);
    }

  return pp;

}
void
closepars (phandle *pp)
/* destructor */
{
  int n, i;

  fclose (pp->fx);
  n = pp->numpars;
  for (i = 0; i < n; i++)
    {
      free (pp->ppars[i]);
      free (pp->pdata[i]);
    }

  free (pp->ppars);
  free (pp->pdata);

  free (pp);
  pp = NULL;

}
void
stripcomment (char *str)
{
  int i, len;

  len = strlen (str);

  for (i = 0; i < len; i++)
    {
      if (str[i] == '#')
        {
          str[i] = '\0';
          return;
        }
    }
}

#define MAXFIELD 1000 

int
getstring (phandle *pp, char *parname, char **strng)
{

  char *field[MAXFIELD];
  int n, kode;

  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  n = splitup (pp->pdata[kode], field, MAXFIELD);
  *strng = strdup (field[0]);
  freeup (field, n);
  return 1;
}

int
getint (phandle *pp, char *parname, int *kret)
{

  char *field[MAXFIELD];
  char str[MAXSTR];
  int n, kode;

  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  n = splitup (pp->pdata[kode], field, MAXFIELD);
  strcpy (str, field[0]);
  freeup (field, n);
  if (strcmp (str, "YES") == 0)
    {
      *kret = YES;
      return 1;
    }
  if (strcmp (str, "NO") == 0)
    {
      *kret = NO;
      return 1;
    }

  *kret = atoi (str);
  return 1;
}

int
getints (phandle *pp, char *parname, int *aint, int nint)
{
  char *field[MAXFIELD];
  int n, kode, i;
  char str[MAXSTR];

  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;

  strcpy (str, pp->pdata[kode]);
  subcolon (str);
  n = splitup (str, field, MAXFIELD);

  if (nint < n)
    fatalx ("(getints) wrong number of ints in line\n");

  for (i = 0; i < n; i++)
    {
      aint[i] = atoi (field[i]);
    }
  freeup (field, n);
  return n;
}
int
getintss (phandle *pp, char *parname, int *aint, int *xint)
/* white space separated */
{
  char *field[MAXFIELD];
  int n, kode, i;
  char str[MAXSTR];

  *xint = 0;
  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  strcpy (str, pp->pdata[kode]);
  subcolon (str);
  n = splitup (str, field, MAXFIELD);
  for (i = 0; i < n; i++)
    {
      aint[i] = atoi (field[i]);
    }
  freeup (field, n);
  *xint = n;
  return 1;
}

int
getdbls (phandle *pp, char *parname, double *dbl, int ndbl)
{
  char *field[MAXFIELD];
  int n, kode, i;
  char str[MAXSTR];

  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  strcpy (str, pp->pdata[kode]);
  subcolon (str);
  n = splitup (str, field, MAXFIELD);
  if (ndbl != n)
    fatalx ("(getdbls) wrong number of dbls in line\n");
  for (i = 0; i < n; i++)
    {
      dbl[i] = atof (field[i]);
    }
  freeup (field, n);
  return 1;
}
int
getdblss (phandle *pp, char *parname, double *dbl, int *ndbl)
/* separated no check */
{
  char *field[MAXFIELD];
  int n, kode, i;
  char str[MAXSTR];

  *ndbl = 0;
  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  strcpy (str, pp->pdata[kode]);
  subcolon (str);
  n = splitup (str, field, MAXFIELD);
  for (i = 0; i < n; i++)
    {
      dbl[i] = atof (field[i]);
    }
  freeup (field, n);
  *ndbl = n;
  return 1;
}

void
subcolon (char *ss)
/* substitute ' ' for ':' or ',' */
{
  int i, l;
  l = strlen (ss);
  for (i = 0; i < l; i++)
    {
      if (ss[i] == ':')
        ss[i] = ' ';
      if (ss[i] == ',')
        ss[i] = ' ';
    }
}

int
getdbl (phandle *pp, char *parname, double *dbl)
{
  char *field[MAXFIELD];
  int n, kode;

  kode = findpname (pp, parname);
  if (kode < 0)
    return kode;
  n = splitup (pp->pdata[kode], field, MAXFIELD);
  *dbl = atof (field[0]);
  freeup (field, n);
  return 1;
}

int
findpname (phandle *pp, char *parname)
{
  int k;

  for (k = 0; k < pp->numpars; k++)
    {
      if (strcmp (parname, pp->ppars[k]) == 0)
        return k;
    }
  return -1;
}
void
writepars (phandle *pp)
{
  int k;
  if (pp == NULL)
    fatalx ("(writepars) phandle not open\n");
  for (k = 0; k < pp->numpars; k++)
    {
      printf ("%s %s\n", pp->ppars[k], pp->pdata[k]);
    }
}
void
fwritepars (FILE *fff, phandle *pp)
{
  int k;
  if (pp == NULL)
    fatalx ("(writepars) phandle not open\n");
  for (k = 0; k < pp->numpars; k++)
    {
      fprintf (fff, "%s %s\n", pp->ppars[k], pp->pdata[k]);
    }
}

void
dostrsub (phandle *pp)
{
  /** 
   massage phandle data structure  
   to do string substitutions of 
   UPPER case paramewters
   */

  int n, nu, nl;
  int *isupp, *islow, *slenm, *indx;
  int i, j, k, l, ind;
  int nchange = 0;
  char *inx, *outx;
  n = pp->numpars;

  ZALLOC(isupp, n, int);
  ZALLOC(islow, n, int);
  ZALLOC(slenm, n, int);
  ZALLOC(indx, n, int);

  nu = nl = 0;
  for (k = 0; k < pp->numpars; k++)
    {
      if (upstring (pp->ppars[k]))
        {
          isupp[nu] = k;
          l = strlen (pp->ppars[k]);
          slenm[nu] = -(10000 * l - k);

          /** 
           after sorting longest first then order in file
           ensures sort is stable
           */

          ++nu;
        }
      else
        {
          islow[nl] = k;
          ++nl;
        }
    }
  if (nu == 0)
    return;
  isortit (slenm, indx, nu);
  for (i = 0; i < nu; i++)
    {
      ind = indx[i];
      k = isupp[ind];
      inx = strdup (pp->ppars[k]);
      l = strlen (inx);
      if (inx[l - 1] == ':')
        inx[l - 1] = '\0';
      outx = strdup (pp->pdata[k]);
      for (j = 0; j < nl; j++)
        {
          k = islow[j];
          nchange += substring (&(pp->pdata[k]), inx, outx);
          if (nchange > 0)
            break;
        }
      free (inx);
      free (outx);
    }
  free (isupp);
  free (islow);
  free (slenm);
  free (indx);

  parchange += nchange;
  if (parchange > 10000)
    fatalx ("(getpars) dostrsub looping\n");
  if (nchange > 0)
    dostrsub (pp);

}

int
upstring (char *ss)
/* 
 YES if at least one upper case character 
 and no lower case  
 */
{
  int nupper = 0;
  int i;
  for (i = 0; i < strlen (ss); i++)

    {
      if (islower(ss[i]))
        return NO;
      if (isupper(ss[i]))
        ++nupper;
    }
  if (nupper > 0)
    return YES;
  return NO;

}

