/*
**********************************************************************

INPUT2.C -- Input data file interpreter for EPANET

VERSION:    2.00
DATE:       5/30/00
            9/7/00
            10/25/00
AUTHOR:     L. Rossman
            US EPA - NRMRL

This module reads and interprets the input data from file InFile.

The entry points for this module are:
   netsize()   -- called from ENopen() in EPANET.C
   readdata()  -- called from getdata() in INPUT1.C

The following utility functions are all called from INPUT3.C
   addnodeID()
   addlinkID()
   findID()
   getfloat()

**********************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include "hash.h"
#include "text.h"
#include "types.h"
#include "epanet2.h"
#include "funcs.h"
#include <math.h>
#define EXTERN extern
#include "vars.h"

#define MAXERRS 10 /* Max. input errors reported        */


/* Defined in enumstxt.h in EPANET.C */
extern char *SectTxt[]; /* Input section keywords            */

int netsize(EN_Project *pr)
/*
**--------------------------------------------------------------
**  Input:   none
**  Output:  returns error code
**  Purpose: determines number of system components
**--------------------------------------------------------------
*/
{
  parser_data_t *par = &pr->parser;
  
  char line[MAXLINE + 1]; /* Line from input data file    */
  char *tok;              /* First token of line          */
  int sect, newsect;      /* Input data sections          */
  int errcode = 0;        /* Error code                   */

  /* Initialize network component counts */
  par->MaxJuncs = 0;
  par->MaxTanks = 0;
  par->MaxPipes = 0;
  par->MaxPumps = 0;
  par->MaxValves = 0;
  par->MaxControls = 0;
  par->MaxRules = 0;
  par->MaxCurves = 0;
  sect = -1;

  /* Add a default pattern 0 */
  par->MaxPats = -1;
  addpattern(par,"");

  if (par->InFile == NULL) {
    return (0);
  }

  /* Make pass through data file counting number of each component */
  while (fgets(line, MAXLINE, par->InFile) != NULL) {
    /* Skip blank lines & those beginning with a comment */
    tok = strtok(line, SEPSTR);
    if (tok == NULL)
      continue;
    if (*tok == ';')
      continue;

    /* Check if line begins with a new section heading */
    if (tok[0] == '[') {
      newsect = findmatch(tok, SectTxt);
      if (newsect >= 0) {
        sect = newsect;
        if (sect == _END)
          break;
        continue;
      } else
        continue;
    }

    /* Add to count of current component */
    switch (sect) {
    case _JUNCTIONS:
      par->MaxJuncs++;
      break;
    case _RESERVOIRS:
    case _TANKS:
      par->MaxTanks++;
      break;
    case _PIPES:
      par->MaxPipes++;
      break;
    case _PUMPS:
      par->MaxPumps++;
      break;
    case _VALVES:
      par->MaxValves++;
      break;
    case _CONTROLS:
      par->MaxControls++;
      break;
    case _RULES:
      addrule(par,tok);
      break; /* See RULES.C */
    case _PATTERNS:
      errcode = addpattern(par, tok);
      break;
    case _CURVES:
      errcode = addcurve(par, tok);
      break;
    }
    if (errcode)
      break;
  }

  par->MaxNodes = par->MaxJuncs + par->MaxTanks;
  par->MaxLinks = par->MaxPipes + par->MaxPumps + par->MaxValves;
  if (par->MaxPats < 1)
    par->MaxPats = 1;
  if (!errcode) {
    if (par->MaxJuncs < 1)
      errcode = 223; /* Not enough nodes */
    else if (par->MaxTanks == 0)
      errcode = 224; /* No tanks */
  }
  return (errcode);
} /*  End of netsize  */

int readdata(EN_Project *pr)
/*
**--------------------------------------------------------------
**  Input:   none
**  Output:  returns error code
**  Purpose: reads contents of input data file
**--------------------------------------------------------------
*/
{
  EN_Network *net = &pr->network;
  parser_data_t *par = &pr->parser;

  char line[MAXLINE + 1], /* Line from input data file       */
      wline[MAXLINE + 1]; /* Working copy of input line      */
  int sect, newsect,      /* Data sections                   */
      errcode = 0,        /* Error code                      */
      inperr, errsum;     /* Error code & total error count  */

  /* Allocate input buffer */
  par->X = (double *)calloc(MAXTOKS, sizeof(double));
  ERRCODE(MEMCHECK(par->X));

  if (!errcode) {

    /* Initialize number of network components */
    par->Ntitle = 0;
    net->Nnodes = 0;
    net->Njuncs = 0;
    net->Ntanks = 0;
    net->Nlinks = 0;
    net->Npipes = 0;
    net->Npumps = 0;
    net->Nvalves = 0;
    net->Ncontrols = 0;
    net->Nrules = 0;
    net->Ncurves = par->MaxCurves;
    net->Npats = par->MaxPats;
    par->PrevPat = NULL;
    par->PrevCurve = NULL;

    sect = -1;
    errsum = 0;

    /* Read each line from input file. */
    while (fgets(line, MAXLINE, par->InFile) != NULL) {

      /* Make copy of line and scan for tokens */
      strcpy(wline, line);
      par->Ntokens = gettokens(wline, par->Tok, MAXTOKS, par->Comment);

      /* Skip blank lines and comments */
      if (par->Ntokens == 0)
        continue;
      if (*par->Tok[0] == ';')
        continue;

      /* Check if max. length exceeded */
      if (strlen(line) >= MAXLINE) {
        char errMsg[MAXMSG+1];
        EN_geterror(214, errMsg, MAXMSG);
        sprintf(pr->Msg, "%s section: %s", errMsg, SectTxt[sect]);
        writeline(pr, pr->Msg);
        writeline(pr, line);
        errsum++;
      }

      /* Check if at start of a new input section */
      if (par->Tok[0][0] == '[') {
        newsect = findmatch(par->Tok[0], SectTxt);
        if (newsect >= 0) {
          sect = newsect;
          if (sect == _END)
            break;
          continue;
        } else {
          inperrmsg(pr, 201, sect, line);
          errsum++;
          break;
        }
      }

      /* Otherwise process next line of input in current section */
      else {
        if (sect >= 0) // for cases were no section is present on the top of the
                       // input file
        {
          inperr = newline(pr, sect, line);
          if (inperr > 0) {
            inperrmsg(pr,inperr, sect, line);
            errsum++;
          }
        } else {
          errcode = 200;
          break;
        }
      }

      /* Stop if reach end of file or max. error count */
      if (errsum == MAXERRS)
        break;
    } /* End of while */

    /* Check for errors */
    if (errsum > 0)
      errcode = 200;
  }

  /* Check for unlinked nodes */
  if (!errcode)
    errcode = unlinked(pr);

  /* Get pattern & curve data from temp. lists */
  if (!errcode)
    errcode = getpatterns(pr);
  if (!errcode)
    errcode = getcurves(pr);
  if (!errcode)
    errcode = getpumpparams(pr);

  /* Free input buffer */
  free(par->X);
  return (errcode);

} /*  End of readdata  */

int newline(EN_Project *pr, int sect, char *line)
/*
**--------------------------------------------------------------
**  Input:   sect  = current section of input file
**           *line = line read from input file
**  Output:  returns error code or 0 if no error found
**  Purpose: processes a new line of data from input file
**--------------------------------------------------------------
*/
{
  parser_data_t *par = &pr->parser;
  
  int n;
  switch (sect) {
  case _TITLE:
    if (par->Ntitle < 3) {
      n = (int)strlen(line);
      if (line[n - 1] == 10)
        line[n - 1] = ' ';
      strncpy(pr->Title[par->Ntitle], line, MAXMSG);
      par->Ntitle++;
    }
    return (0);
  case _JUNCTIONS:
    return (juncdata(pr));
  case _RESERVOIRS:
  case _TANKS:
    return (tankdata(pr));
  case _PIPES:
    return (pipedata(pr));
  case _PUMPS:
    return (pumpdata(pr));
  case _VALVES:
    return (valvedata(pr));
  case _PATTERNS:
    return (patterndata(pr));
  case _CURVES:
    return (curvedata(pr));
  case _DEMANDS:
    return (demanddata(pr));
  case _CONTROLS:
    return (controldata(pr));
  case _RULES:
    return (ruledata(pr)); /* See RULES.C */
  case _SOURCES:
    return (sourcedata(pr));
  case _EMITTERS:
    return (emitterdata(pr));
  case _QUALITY:
    return (qualdata(pr));
  case _STATUS:
    return (statusdata(pr));
  case _ROUGHNESS:
    return (0);
  case _ENERGY:
    return (energydata(pr));
  case _REACTIONS:
    return (reactdata(pr));
  case _MIXING:
    return (mixingdata(pr));
  case _REPORT:
    return (reportdata(pr));
  case _TIMES:
    return (timedata(pr));
  case _OPTIONS:
    return (optiondata(pr));

  /* Data in these sections are not used for any computations */
  case _COORDS:
    if (par->Coordflag == TRUE) {
      return (coordata(pr));
    } else
      return (0);
  case _LABELS:
    return (0);
  case _TAGS:
    return (0);
  case _VERTICES:
    return (0);
  case _BACKDROP:
    return (0);
  }
  return (201);
} /* end of newline */

int getpumpparams(EN_Project *pr)
/*
**-------------------------------------------------------------
**  Input:   none
**  Output:  returns error code
**  Purpose: computes & checks pump curve parameters
**--------------------------------------------------------------
*/
{
  int i, j = 0, k, m, n = 0;
  double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0;
  char errMsg[MAXMSG+1];
  Spump *pump;
  Slink *link;
  Scurve *curve;
  
  EN_Network *net = &pr->network;
  
  for (i = 1; i <= net->Npumps; i++) {
    pump = &net->Pump[i];
    k = pump->Link;
    link = &net->Link[k];
    if (pump->Ptype == CONST_HP) { /* Constant Hp pump */
      pump->H0 = 0.0;
      pump->R = -8.814 * link->Km;
      pump->N = -1.0;
      pump->Hmax = BIG; /* No head limit      */
      pump->Qmax = BIG; /* No flow limit      */
      pump->Q0 = 1.0;   /* Init. flow = 1 cfs */
      continue;
    }
    else if (pump->Ptype == NOCURVE) { /* Pump curve specified */
      j = pump->Hcurve; /* Get index of head curve */
      if (j == 0) {       /* Error: No head curve */
        EN_geterror(226, errMsg, MAXMSG);
        sprintf(pr->Msg, "%s link: %s", errMsg, link->ID);
        writeline(pr, pr->Msg);
        return (200);
      }
      curve = &net->Curve[j];
      curve->Type = P_CURVE;
      n = curve->Npts;
      if (n == 1) {  /* Only a single h-q point supplied so use generic */
        pump->Ptype = POWER_FUNC; /* power function curve.   */
        q1 = curve->X[0];
        h1 = curve->Y[0];
        h0 = 1.33334 * h1;
        q2 = 2.0 * q1;
        h2 = 0.0;
      } else if (n == 3 && curve->X[0] == 0.0) /* 3 h-q points supplied with */
      {                                /* shutoff head so use fitted */
        pump->Ptype = POWER_FUNC;    /* power function curve.      */
        h0 = curve->Y[0];
        q1 = curve->X[1];
        h1 = curve->Y[1];
        q2 = curve->X[2];
        h2 = curve->Y[2];
      } 
      else { // use a custom curve, referenced by ID
        pump->Ptype = CUSTOM; /* Else use custom pump curve.*/
        // at this point, j is set to that curve's index.
      }
      
      /* Compute shape factors & limits of power function pump curves */
      if (pump->Ptype == POWER_FUNC) {
        if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) { /* Error: Invalid curve */
          EN_geterror(227, errMsg, MAXMSG);
          sprintf(pr->Msg, "%s link: %s", errMsg, link->ID);
          writeline(pr, pr->Msg);
          return (200);
        } else {
          pump->H0 = -a;
          pump->R = -b;
          pump->N = c;
          pump->Q0 = q1;
          pump->Qmax = pow((-a / b), (1.0 / c));
          pump->Hmax = h0;
        }
      }
    }

    /* Assign limits to custom pump curves */
    if (pump->Ptype == CUSTOM) {
      curve = &net->Curve[j];
      for (m = 1; m < n; m++) {
        if (curve->Y[m] >= curve->Y[m - 1]) { /* Error: Invalid curve */
          EN_geterror(227, errMsg, MAXMSG);
          sprintf(pr->Msg, "%s link: %s", errMsg, link->ID);
          writeline(pr, pr->Msg);
          return (200);
        }
      }
      pump->Qmax = curve->X[n - 1];
      pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0;
      pump->Hmax = curve->Y[0];
    }
  } /* Next pump */
  return (0);
}

int addnodeID(EN_Network *net, int n, char *id)
/*
**-------------------------------------------------------------
**  Input:   n = node index
**           id = ID label
**  Output:  returns 0 if ID already in use, 1 if not
**  Purpose: adds a node ID to the Node Hash Table
**--------------------------------------------------------------
*/
{
  if (findnode(net,id)) {
    return (0); /* see EPANET.C */
  }
  strncpy(net->Node[n].ID, id, MAXID);
  ENHashTableInsert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */
  return (1);
}

int addlinkID(EN_Network *net, int n, char *id)
/*
**-------------------------------------------------------------
**  Input:   n = link index
**           id = ID label
**  Output:  returns 0 if ID already in use, 1 if not
**  Purpose: adds a link ID to the Link Hash Table
**--------------------------------------------------------------
*/
{
  if (findlink(net,id)) {
    return (0); /* see EPANET.C */
  }
  strncpy(net->Link[n].ID, id, MAXID);
  ENHashTableInsert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */
  return (1);
}

int addpattern(parser_data_t *par, char *id)
/*
**-------------------------------------------------------------
**  Input:   id = pattern ID label
**  Output:  returns error code
**  Purpose: adds a new pattern to the database
**--------------------------------------------------------------
*/
{
  
  STmplist *p;

  /* Check if ID is same as last one processed */
  if (par->Patlist != NULL && strcmp(id, par->Patlist->ID) == 0) {
    return (0);
  }

  /* Check that pattern was not already created */
  if (findID(id, par->Patlist) == NULL) {

    /* Update pattern count & create new list element */
    (par->MaxPats)++;
    p = (STmplist *)malloc(sizeof(STmplist));
    if (p == NULL)
      return (101);

    /* Initialize list element properties */
    else {
      p->i = par->MaxPats;
      strncpy(p->ID, id, MAXID);
      p->x = NULL;
      p->y = NULL;
      p->next = par->Patlist;
      par->Patlist = p;
    }
  }
  return (0);
}

int addcurve(parser_data_t *par, char *id)
/*
**-------------------------------------------------------------
**  Input:   id = curve ID label
**  Output:  returns error code
**  Purpose: adds a new curve to the database
**--------------------------------------------------------------
*/
{
  STmplist *c;

  /* Check if ID is same as last one processed */
  if (par->Curvelist != NULL && strcmp(id, par->Curvelist->ID) == 0)
    return (0);

  /* Check that curve was not already created */
  if (findID(id, par->Curvelist) == NULL) {

    /* Update curve count & create new list element */
    (par->MaxCurves)++;
    c = (STmplist *)malloc(sizeof(STmplist));
    if (c == NULL)
      return (101);

    /* Initialize list element properties */
    else {
      c->i = par->MaxCurves;
      strncpy(c->ID, id, MAXID);
      c->x = NULL;
      c->y = NULL;
      c->next = par->Curvelist;
      par->Curvelist = c;
    }
  }
  return (0);
}

STmplist *findID(char *id, STmplist *list)
/*
**-------------------------------------------------------------
**  Input:   id = ID label
**           list = pointer to head of a temporary list
**  Output:  returns list item with requested ID label
**  Purpose: searches for item in temporary list
**-------------------------------------------------------------
*/
{
  STmplist *item;
  for (item = list; item != NULL; item = item->next) {
    if (strcmp(item->ID, id) == 0) {
      return (item);
    }
  }
  return (NULL);
}

int unlinked(EN_Project *pr)
/*
**--------------------------------------------------------------
** Input:   none
** Output:  returns error code if any unlinked junctions found
** Purpose: checks for unlinked junctions in network
**
** NOTE: unlinked tanks have no effect on computations.
**--------------------------------------------------------------
*/
{
  EN_Network *net = &pr->network;
  int *marked;
  int i, err, errcode;
  char errMsg[MAXMSG+1];
  
  errcode = 0;
  err = 0;
  marked = (int *)calloc(net->Nnodes + 1, sizeof(int));
  ERRCODE(MEMCHECK(marked));
  if (!errcode) {
    memset(marked, 0, (net->Nnodes + 1) * sizeof(int));
    for (i = 1; i <= net->Nlinks; i++) /* Mark end nodes of each link */
    {
      marked[net->Link[i].N1]++;
      marked[net->Link[i].N2]++;
    }
    for (i = 1; i <= net->Njuncs; i++) /* Check each junction  */
    {
      if (marked[i] == 0) /* If not marked then error */
      {
        err++;
        EN_geterror(233, errMsg, MAXMSG);
        sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[i].ID);
        writeline(pr, pr->Msg);
      }
      if (err >= MAXERRS)
        break;
    }
    if (err > 0)
      errcode = 200;
  }
  free(marked);
  return (errcode);
} /* End of unlinked */

int getpatterns(EN_Project *pr)
/*
**-----------------------------------------------------------
**  Input:   none
**  Output:  returns error code
**  Purpose: retrieves pattern data from temporary linked list
**-------------------------------------------------------------
*/
{
  int i, j;
  SFloatlist *f;
  STmplist *pat;
  
  EN_Network *net = &pr->network;
  hydraulics_t *hyd = &pr->hydraulics;
  parser_data_t *par = &pr->parser;

  /* Start at head of list */
  pat = par->Patlist;

  /* Traverse list of patterns */
  while (pat != NULL) {

    /* Get index of current pattern in Pattern array */
    i = pat->i;

    /* Check if this is the default pattern */
    if (strcmp(pat->ID, par->DefPatID) == 0) {
      hyd->DefPat = i;
    }
    if (i >= 0 && i <= par->MaxPats) {
      /* Save pattern ID */
      
      Spattern *pattern = &net->Pattern[i];
      
      strcpy(pattern->ID, pat->ID);

      /* Give pattern a length of at least 1 */
      if (pattern->Length == 0)
        pattern->Length = 1;
      pattern->F = (double *)calloc(pattern->Length, sizeof(double));
      if (pattern->F == NULL)
        return (101);

      /* Start at head of pattern multiplier list */
      /* (which holds multipliers in reverse order)*/
      f = pat->x;
      j = pattern->Length - 1;

      /* Use at least one multiplier equal to 1.0 */
      if (f == NULL)
        pattern->F[0] = 1.0;

      /* Traverse list, storing multipliers in Pattern array */
      else
        while (f != NULL && j >= 0) {
          pattern->F[j] = f->value;
          f = f->next;
          j--;
        }
    }
    pat = pat->next;
  }
  return (0);
}

int getcurves(EN_Project *pr)
/*
**-----------------------------------------------------------
**  Input:   none
**  Output:  returns error code
**  Purpose: retrieves curve data from temporary linked list
**-----------------------------------------------------------
*/
{
  EN_Network *net = &pr->network;
  parser_data_t *par = &pr->parser;
  
  int i, j;
  double x;
  SFloatlist *fx, *fy;
  STmplist *c;

  /* Start at head of curve list */
  c = par->Curvelist;

  /* Traverse list of curves */
  while (c != NULL) {
    i = c->i;
    if (i >= 1 && i <= par->MaxCurves) {
      Scurve *curve = &net->Curve[i];
      
      /* Save curve ID */
      strcpy(curve->ID, c->ID);

      /* Check that curve has data points */
      if (curve->Npts <= 0) {
        char errMsg[MAXMSG+1];
        EN_geterror(230, errMsg, MAXMSG);
        sprintf(pr->Msg, "%s curve: %s", errMsg, curve->ID);
        writeline(pr, pr->Msg);
        return (200);
      }

      /* Allocate memory for curve data */
      curve->X = (double *)calloc(curve->Npts, sizeof(double));
      curve->Y = (double *)calloc(curve->Npts, sizeof(double));
      if (curve->X == NULL || curve->Y == NULL)
        return (101);

      /* Traverse list of x,y data */
      x = BIG;
      fx = c->x;
      fy = c->y;
      j = curve->Npts - 1;
      while (fx != NULL && fy != NULL && j >= 0) {

        /* Check that x data is in ascending order */
        if (fx->value >= x) {
          char errMsg[MAXMSG+1];
          EN_geterror(230, errMsg, MAXMSG);
          sprintf(pr->Msg, "%s node: %s", errMsg, curve->ID);
          writeline(pr, pr->Msg);
          return (200);
        }
        x = fx->value;

        /* Save x,y data in Curve structure */
        curve->X[j] = fx->value;
        fx = fx->next;
        curve->Y[j] = fy->value;
        fy = fy->next;
        j--;
      }
    }
    c = c->next;
  }
  return (0);
}

int findmatch(char *line, char *keyword[])
/*
**--------------------------------------------------------------
**  Input:   *line      = line from input file
**           *keyword[] = list of NULL terminated keywords
**  Output:  returns index of matching keyword or
**           -1 if no match found
**  Purpose: determines which keyword appears on input line
**--------------------------------------------------------------
*/
{
  int i = 0;
  while (keyword[i] != NULL) {
    if (match(line, keyword[i]))
      return (i);
    i++;
  }
  return (-1);
} /* end of findmatch */

int match(const char *str, const char *substr)
/*
**--------------------------------------------------------------
**  Input:   *str    = string being searched
**           *substr = substring being searched for
**  Output:  returns 1 if substr found in str, 0 if not
**  Purpose: sees if substr matches any part of str
**
**      (Not case sensitive)
**--------------------------------------------------------------
*/
{
  int i, j;

  /*** Updated 9/7/00 ***/
  /* Fail if substring is empty */
  if (!substr[0])
    return (0);

  /* Skip leading blanks of str. */
  for (i = 0; str[i]; i++)
    if (str[i] != ' ')
      break;

  /* Check if substr matches remainder of str. */
  for (i = i, j = 0; substr[j]; i++, j++)
    if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j]))
      return (0);
  return (1);
} /* end of match */

int  gettokens(char *s, char** Tok, int maxToks, char *comment)
/*
 **--------------------------------------------------------------
 **  Input:   *s = string to be tokenized
 **  Output:  returns number of tokens in s
 **  Purpose: scans string for tokens, saving pointers to them
 **           in module global variable Tok[]
 **
 ** Tokens can be separated by the characters listed in SEPSTR
 ** (spaces, tabs, newline, carriage return) which is defined
 ** in TYPES.H. Text between quotes is treated as a single token.
 **--------------------------------------------------------------
 */
{
  int  m, n;
  size_t len;
  char *c, *c2;
  
  // clear comment
  comment[0] = '\0';
  
  /* Begin with no tokens */
  for (n=0; n<maxToks; n++) { 
    Tok[n] = NULL;
  }
  n = 0;
  
  /* Truncate s at start of comment */
  c = strchr(s,';');
  if (c) {
    c2 = c+1;
    if (c2) {
      // there is a comment here, after the semi-colon.
      len = strlen(c2);
      if (len > 0) {
        len = strcspn(c2, "\n\r");
        len = MIN(len, MAXMSG);
        strncpy(comment, c2, len);
        comment[MIN(len,MAXMSG)] = '\0';
      }
    }
    *c = '\0';
  }
  len = (int)strlen(s);
  
  /* Scan s for tokens until nothing left */
  while (len > 0 && n < MAXTOKS)
  {
    m = (int)strcspn(s,SEPSTR);          /* Find token length */
    len -= m+1;                     /* Update length of s */
    if (m == 0) s++;                /* No token found */
    else
    {
      if (*s == '"')               /* Token begins with quote */
      {
        s++;                      /* Start token after quote */
        m = (int)strcspn(s,"\"\n\r");  /* Find end quote (or EOL) */
      }                            
      s[m] = '\0';                 /* Null-terminate the token */
      Tok[n] = s;                  /* Save pointer to token */
      n++;                         /* Update token count */
      s += m+1;                    /* Begin next token */
    }
  }
  return(n);
}  

double hour(char *time, char *units)
/*
**---------------------------------------------------------
**  Input:   *time  = string containing a time value
**           *units = string containing time units
**  Output:  returns numerical value of time in hours,
**           or -1 if an error occurs
**  Purpose: converts time from units to hours
**---------------------------------------------------------
*/
{
  int n;
  double y[3];
  char *s;

  /* Separate clock time into hrs, min, sec. */
  for (n = 0; n < 3; n++)
    y[n] = 0.0;
  n = 0;
  s = strtok(time, ":");
  while (s != NULL && n <= 3) {
    if (!getfloat(s, &y[n]))
      return (-1.0);
    s = strtok(NULL, ":");
    n++;
  }

  /* If decimal time with units attached then convert to hours. */
  if (n == 1) {
    /*if (units[0] == '\0')       return(y[0]);*/
    if (strlen(units) == 0)
      return (y[0]);
    if (match(units, w_SECONDS))
      return (y[0] / 3600.0);
    if (match(units, w_MINUTES))
      return (y[0] / 60.0);
    if (match(units, w_HOURS))
      return (y[0]);
    if (match(units, w_DAYS))
      return (y[0] * 24.0);
  }

  /* Convert hh:mm:ss format to decimal hours */
  if (n > 1)
    y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0;

  /* If am/pm attached then adjust hour accordingly */
  /* (12 am is midnight, 12 pm is noon) */
  if (units[0] == '\0')
    return (y[0]);
  if (match(units, w_AM)) {
    if (y[0] >= 13.0)
      return (-1.0);
    if (y[0] >= 12.0)
      return (y[0] - 12.0);
    else
      return (y[0]);
  }
  if (match(units, w_PM)) {
    if (y[0] >= 13.0)
      return (-1.0);
    if (y[0] >= 12.0)
      return (y[0]);
    else
      return (y[0] + 12.0);
  }
  return (-1.0);
} /* end of hour */

int getfloat(char *s, double *y)
/*
**-----------------------------------------------------------
**  Input:   *s = character string
**  Output:  *y = floating point number
**           returns 1 if conversion successful, 0 if not
**  Purpose: converts string to floating point number
**-----------------------------------------------------------
*/
{
  char *endptr;
  *y = (double)strtod(s, &endptr);
  if (*endptr > 0)
    return (0);
  return (1);
}

int setreport(EN_Project *pr, char *s)
/*
**-----------------------------------------------------------
**  Input:   *s = report format command
**  Output:  none
**  Returns: error code
**  Purpose: processes a report formatting command
**           issued by the ENsetreport function
**-----------------------------------------------------------
*/
{
  parser_data_t *par = &pr->parser;
  par->Ntokens = gettokens(s, par->Tok, MAXTOKS, par->Comment);
  return (reportdata(pr));
}

void inperrmsg(EN_Project *pr, int err, int sect, char *line)
/*
**-------------------------------------------------------------
**  Input:   err     = error code
**           sect    = input data section
**           *line   = line from input file
**  Output:  none
**  Purpose: displays input error message
**-------------------------------------------------------------
*/
{
  parser_data_t *par = &pr->parser;
  
  char errStr[MAXMSG + 1];
  char id[MAXMSG + 1];
  
  EN_geterror(err, errStr, MAXMSG);
  
  /* get text for error message */
  sprintf(pr->Msg, "%s - section: %s", errStr, SectTxt[sect]);
  
  // append ID?
  /* Retrieve ID label of object with input error */
  /* (No ID used for CONTROLS or REPORT sections).*/
  switch (sect) {
    case _CONTROLS:
    case _REPORT:
      // don't append
      break;
    case _ENERGY:
      sprintf(id, " id: %s", par->Tok[1]);
      break;
    default:
      sprintf(id, " id: %s", par->Tok[0]);
      break;
  }
    
  strcat(pr->Msg, id);
  writeline(pr, pr->Msg);

  /* Echo input line for syntax errors, and   */
  /* errors in CONTROLS and OPTIONS sections. */
  if (sect == _CONTROLS || err == 201 || err == 213)
    writeline(pr, line);
  else
    writeline(pr, "");
}

/********************** END OF INPUT2.C ************************/
