# include <ohana.h>

static double tz = 0.0;
void set_timezone (double dt) {
  tz = dt;
}

/***** convert [-]00:00:00 to 0.0000 ****/
int dms_to_ddd (double *Value, char *string) {
  
  int valid, neg, status;
  double tmp, value;
  char *p1, *p2, *px;

  valid = FALSE; 
  neg = FALSE;
  stripwhite (string);
  p1 = string;
  px = string + strlen(string);

  if (string[0] == '-') { 
    valid = TRUE; 
    neg = TRUE;
    p1 = &string[1];
  }
  if (string[0] == '+') { 
    valid = TRUE; 
    neg = FALSE;
    p1 = &string[1];
  }
  if (isdigit(string[0])) { 
    valid = TRUE;
    p1 = &string[0];
  }
  if (!valid) { return (FALSE); }

  status = 1;
  tmp = strtod (p1, &p2);
  if (p2 == p1) return (FALSE); /* entry not a number: +fred */
  value = tmp;
  if (p2 == px) goto escape;    /* entry only number: +1.0 */ 
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;    /* entry not a number: +1:fred */
  status = 2;
  value += tmp / 60.0;
  if (p2 == px) goto escape;    /* entry only number: +1:1 */
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;    /* entry not a number: +1:1:fred */
  value += tmp / 3600.0;

 escape:
  if (neg) {
    value *= -1;
  }
  *Value = value;

  return (status);
}

/**********/
int str_to_radec (double *ra, double *dec, char *str1, char *str2) {

  double Ra, Dec;

  *ra = *dec = 0;
  switch (dms_to_ddd (&Ra, str1)) {
  case 0:
    fprintf (stderr, "syntax error in RA\n");
    return (FALSE);
  case 1:
    break;
  case 2:
    Ra = Ra * 15;
    break;
  }
  switch (dms_to_ddd (&Dec, str2)) {
  case 0:
    fprintf (stderr, "syntax error in DEC\n");
    return (FALSE);
  case 1:
  case 2:
    break;
  }
  *ra = Ra;
  *dec = Dec;
  return (TRUE);
}

# define FORMAT_DAYS    1
# define FORMAT_HOURS   2
# define FORMAT_MINUTES 3
# define FORMAT_SECONDS 4
# define FORMAT_JD      5
# define FORMAT_DATE    6

/**********/
int chk_time (char *line) {

  char *p1, *p2;
  double tmp;
  int mode;

  p1 = line;
  tmp = strtod (p1, &p2);
  if (p2 == p1 + strlen (p1) - 1) {
    if (*p2 == 'd') {
      mode = FORMAT_DAYS;
    }
    if (*p2 == 'h') {
      mode = FORMAT_HOURS;
    }
    if (*p2 == 'm') {
      mode = FORMAT_MINUTES;
    }
    if (*p2 == 's') {
      mode = FORMAT_SECONDS;
    }
    if (*p2 == 'j') {
      mode = FORMAT_JD;
    }
  } else { 
    mode = FORMAT_DATE;
  }
  return (mode);
}

/**********/
int str_to_time (char *line, unsigned long *second) {
  
  struct timeval now;
  double jd;

  if (!strcasecmp (line, "NOW")) {
    gettimeofday (&now, (struct timezone *) NULL);
    *second = now.tv_sec;
    return (TRUE);
  }
    
  if (!strcasecmp (line, "TODAY")) {
    gettimeofday (&now, (struct timezone *) NULL);
    *second = 86400 * ((int)(now.tv_sec / 86400));
    return (TRUE);
  }
    
  switch (chk_time (line)) {
  case 0:
    return (FALSE);
  case FORMAT_DAYS:
    *second = strtod (line, 0) * 86400.0;
    return (TRUE);
  case FORMAT_HOURS:
    *second = strtod (line, 0) * 3600.0;
    return (TRUE);
  case FORMAT_MINUTES:
    *second = strtod (line, 0) * 60.0;
    return (TRUE);
  case FORMAT_SECONDS:
    *second = strtod (line, 0);
    return (TRUE);
  case FORMAT_JD:
    jd = strtod (line, 0);
    *second = jd_to_sec (jd);
    return (TRUE);
  case FORMAT_DATE:
    *second = date_to_sec (line);
    return (TRUE);
  }
  return (FALSE);
}

/**********/
int str_to_dtime (char *line, double *second) {
  
  switch (chk_time (line)) {
  case 0:
  case FORMAT_JD:
  case FORMAT_DATE:
    return (FALSE);
  case FORMAT_DAYS:
    *second = strtod (line, 0) * 86400.0;
    return (TRUE);
  case FORMAT_HOURS:
    *second = strtod (line, 0) * 3600.0;
    return (TRUE);
  case FORMAT_MINUTES:
    *second = strtod (line, 0) * 60.0;
    return (TRUE);
  case FORMAT_SECONDS:
    *second = strtod (line, 0);
    return (TRUE);
  }
  return (FALSE);
}

/**********/
double sec_to_jd (unsigned long second) {

  double jd;
  
  jd = second/86400.0 + 2440587.5;
  return (jd);
}

/**********/
unsigned long int jd_to_sec (double jd) {

  unsigned long int second;

  second = (jd - 2440587.5)*86400;
  return (second);
}

/**********/
char *sec_to_date (unsigned long second) {
  
  struct tm *gmt;
  char *line;
  
  ALLOCATE (line, char, 64);
  second -= 3600*tz;
  gmt   = gmtime (&second);
  sprintf (line, "%4d/%02d/%02d,%02d:%02d:%02d", 1900+gmt[0].tm_year, gmt[0].tm_mon+1, gmt[0].tm_mday, gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
  return (line);

}

/***** date in format yyyy/mm/dd,hh:mm:ss *****/
unsigned long date_to_sec (char *date) {
  
  unsigned long second;
  double tmp, jd;
  struct tm now;
  char *p1, *p2, *px;
  
  p1 = date;
  px = date + strlen(date);
  bzero (&now, sizeof(now));

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  now.tm_year = tmp;
  if (now.tm_year > 1000) now.tm_year -= 1900;
  if (now.tm_year <   50) now.tm_year += 100;
  if (p2 == px) goto escape;  
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  now.tm_mon = tmp - 1; /* mon runs from 0 - 11 */
  if (p2 == px) goto escape;  
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  now.tm_mday = tmp;
  if (p2 == px) goto escape;  
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  p1 = p2 + 1;
  now.tm_hour = tmp;
  if (p2 == px) goto escape;  

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  now.tm_min = tmp;
  if (p2 == px) goto escape;  
  p1 = p2 + 1;

  tmp = strtod (p1, &p2);
  if (p2 == p1) goto escape;
  now.tm_sec = tmp;
  if (p2 == px) goto escape;  
  p1 = p2 + 1;

 escape:
  jd = now.tm_mday - 32075 + (int)(1461*(1900 + now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
  
  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;

  second += 3600*tz;

  return (second);
}


/* times may be in forms as:
   20040200450s (N seconds since 1970.0)
   2440900.232j (julian date)
   99/02/23,03:22:18 (date string)
   (separators may be anything except space, +, -)
   99:02:15:12:23:30
   99:02:15:12h23m30s
   */


/* fseek with timeout - 0.5 sec */
int Fseek (FILE *f, long offset, int whence) {

  int status, k;

  status = fseek (f, offset, whence);
  if (status == -1) {
    for (k = 0; (k < 10) && ((status = fseek (f, 0, SEEK_SET)) == -1); k++) usleep (50000);
    if (status == -1) {
      return (0);
    }
  }
  return (1);
}

/* return values:
   0 - no trange arguments
   1 - arguments ok
   2 - arguments bad
*/
int get_trange_arguments (int *argc, char **argv, unsigned long **Tstart, unsigned long **Tstop, int *ntimes) {

  int Na, N, Ntimes;
  double trange;
  unsigned long tmp, *tstart, *tstop;

  /* allocate space for returned lists */
  Ntimes = 10;
  ALLOCATE (tstart, unsigned long, Ntimes);
  ALLOCATE (tstop,  unsigned long, Ntimes);

  for (N = 0; ; N++) {

    /* find next -trange arg */
    Na = get_argument (*argc, argv, "-trange");
    if (Na == 0) {
      *ntimes = N;
      *Tstart = tstart;
      *Tstop  = tstop;
      return (TRUE);
    }
    remove_argument (Na, argc, argv);
    
    /* tstart */
    if (!str_to_time (argv[Na], &tstart[N])) { 
      return (FALSE);
    }

    /* interpret second value */
    remove_argument (Na, argc, argv);
    if (str_to_dtime (argv[Na], &trange)) { 
      if (trange < 0) {
	tstop[N]  = tstart[N];
	tstart[N] = tstop[N] + trange;
      } else {
	tstop[N]  = tstart[N] + trange;
      }
      remove_argument (Na, argc, argv);
      goto goodvalue;
    }
    if (str_to_time (argv[Na], &tstop[N])) { 
      if (tstart[N] > tstop[N]) {
	tmp     = tstart[N];
	tstart[N] = tstop[N];
	tstop[N]  = tmp;
      }
      remove_argument (Na, argc, argv);
      goto goodvalue;
    }
    return (FALSE); /* syntax error in 2nd value */

  goodvalue:
    if (N == Ntimes - 1) {
      Ntimes += 10;
      REALLOCATE (tstart, unsigned long, Ntimes);
      REALLOCATE (tstop,  unsigned long, Ntimes);
    }
  }
}
  
void make_backup (char *filename) {

  int status, cmode;
  struct stat filestat;
  char line [256];

  status = stat (filename, &filestat);
  if (status == 0) { /* file exists, make backup copy */
    sprintf (line, "cp %s %s~", filename, filename);
    status = system (line);
    if (status) {
      fprintf (stderr, "ERROR: unable to create %s~, exiting\n", filename);
      exit (0);
    }
    cmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
    sprintf (line, "%s~", filename);
    chmod (line, cmode);
  }
}


int get_filter_arguments (int *argc, char **argv, int **Filt, int *Nfilt) {

  int i, N, Na, NF;
  int *filt;

  /* allocate space for returned lists */
  NF = 10;
  ALLOCATE (filt, int, NF);

  for (N = 0; ; N++) {

    /* find next -trange arg */
    Na = get_argument (*argc, argv, "-filter");
    if (Na == 0) {
      *Nfilt = N;
      *Filt = filt;
      return (TRUE);
    }
    if (Na > *argc - 2) return (FALSE); /* -filter F */
    remove_argument (Na, argc, argv);
    
    for (i = 0; i < strlen (argv[Na]); i++) { if (isspace (argv[Na][i])) argv[Na][i] = '.'; }
    for (i = 0; i < NFILTER; i++) {
      if (!strcasecmp (argv[Na], filtername[i])) {
	filt[N] = filternum[i];
      }
    }
    if (filt[N] == FILTER_NONE) return (FALSE);
    remove_argument (Na, argc, argv);

    if (N == NF - 1) {
      NF += 10;
      REALLOCATE (filt, int, NF);
    }
  }
}
  
   
int get_version (int argc, char **argv, char *version) {

  int N;
  char *p, *q, *line;

  if (get_argument (argc, argv, "-version")) {

    N = strlen (version) + 2;
    line = (char *) malloc (N);
    bzero (line, N);

    p = strstr (version, "$Revision: ");
    if (p != (char *) NULL) 
      p += strlen ("$Revision: ");
    else
      p = version;

    q = strstr (p, "$");
    if (q != (char *) NULL) 
      N = q - p; 
    else
      N = strlen (p);

    strncpy (line, p, N);

    fprintf (stderr, "%s\n", line);
    exit (2);
  }
}  
