/*************************************************************
 * File:             stardate.c
 * Author:           Jay Windley  (modified by Yves Perrenoud)
 * Created:           1 Jan 92    (modified 19 Dec 94)
 * Copyright:        (c)  1 Jan 92 by Jay Windley
 * Description:      prints the current stardate to stdout
 *
 *************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <time.h>

#define	FIT(x,y) while((x)<0)(x)+=(y);while((x)>(y))(x)-=(y)

#define MAXPRECISION       7
#define OUTPUTFORMAT       "%%%03d.%df"

/*---------------------------------------------------------------------*
 *TAG (                           julian_day                           );
 *---------------------------------------------------------------------*
 * Returns the New Epoch Julian Day for the given Gregorian calendar
 * day.
 */
double julian_day (month, day, year)
     int month, day, year;
{
  int loc_month;
  int loc_year;
  int a, b, c, d;
  
  loc_month = month;
  loc_year = year;
  
  /* correct for March-based B.C. year */
  if (year < 0) loc_year ++;
  if (month < 3) {
    loc_month = month + 12;
    loc_year --;
  }
  
  /* set up A and B for Julian to Gregorian calendar change */
  if (year < 1582) b = 0;
  else if (year == 1582) {
    if (month < 10) b = 0;
    else if (month == 10) {
      if (day < 15) b = 0;
      else {
	a = loc_year / 100;
	b = 2 - a + (a/4);
      }
    } else {
      a = loc_year / 100;
      b = 2 - a + (a/4);
    }
  } else {
    a = loc_year / 100;
    b = 2 - a + (a/4);		/* century is set up */
  }
  
  /* fudge for year and month */
  c = (int) (365.25 * loc_year) - 694025;
  d = (int) (30.6001 * (loc_month + 1));
  
  return (double) (b + c + d + day) - .5;
}

/*---------------------------------------------------------------------*
 *TAG (                            sidereal                            );
 *---------------------------------------------------------------------*
 * Returns Greenwich Mean Sidereal Time for the given Greenwich Mean
 * Solar Time on the given NE Julian Day.
 */
double sidereal (gmt, jd, gyear)
     double gmt;		/* Greenwich Mean Time, decimal	*/
     double jd;			/* Julian Day */
     int gyear;			/* calendar year */
{
  register double dayfudge, yrfudge, jhund;
  double initjd, hourfudge, time0, lst;
  
  /* find Julian century at start of year */
  initjd = julian_day (1, 0, gyear);
  jhund = initjd / 36525.0;
  
  /* apply polynomial correction */
  dayfudge = 6.6460656 + (0.051262 + (jhund * 0.00002581)) * jhund;
  yrfudge = 2400.0 * (jhund - ( (double) (gyear - 1900) / 100));
  hourfudge = 24.0 - dayfudge - yrfudge;
  time0 = (jd - initjd) * 0.0657098 - hourfudge;
  
  /* adjust to modulo 24 hours */
  lst = (gmt * 1.002737908) + time0;
  FIT (lst, 24.0);
  return lst;
}

/*---------------------------------------------------------------------*
 *TAG (                              main                              );
 *---------------------------------------------------------------------*
 * Behaves as expected.
 */
void main (argc, argv)
     int argc;
     char *argv[];
{
  int timezone=0,i;
  int jd, precis;
  double gmt, gmst;
  time_t t;
  struct tm *tm;
  char buf[16];
  char fmt[16];

/* Parsing below rewritten by Yves Perrenoud based on the original code */

 	if (argc > 1)
 	{
		for (i=1;i <= (argc-1);i++)
		{
			if (argv[i][0] == '-')
			{
				switch (argv[i][1])
				{
					case 'd' :	if (sscanf (&argv[i][2], "%d", &precis) < 1)
								{
									fprintf (stderr, "%s: invalid precision specification\n", argv[0]);
									exit (1);
								}
								if (precis < 1) precis = 1;
								if (precis > MAXPRECISION) precis = MAXPRECISION;
								break;
					case 't' :	if (sscanf (&argv[i][2], "%d", &timezone) < 1)
								{
									fprintf (stderr, "%s: invalid timezone specification\n", argv[0]);
									exit (1);
								}
								if (timezone < -12) timezone = -12;
								if (timezone > 12) timezone = 12;
								break;
					case 'h' :  puts("options:");
								puts("  -d<prec> : precision from 1 to 7");
								puts("  -t<tz>   : timezone from -12 to 12");
								exit(0);
								break;
					default  : puts("Uknown type of OPTION"); exit(1);
				}
			}
		}
	} else precis = 1;


/* timezone modifications made by Yves Perrenoud for Amiga compatibility */

  t = time (NULL);
  tm = localtime (&t);
  jd = julian_day (tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
  gmt = (double) tm->tm_hour - timezone +
    tm->tm_min / 60.0 +
    tm->tm_sec / 3600.0;
  gmst = sidereal (gmt, (double) jd, tm->tm_year);
  sprintf (fmt, OUTPUTFORMAT, precis + 6, precis);
  sprintf (buf, fmt, (double) jd + gmst / 24.0);
  puts (buf);
}
