#include<time.h>
#include<math.h>
#include<stdio.h>

#define BASE_YEAR 1900

/* constant string arrays of the days of the week and the months of the year */
const char *days[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
const char *months[] = {"January","February","March","April","May","June","July",
                  "August","September","October","November","December"};

/* prototypes of functions defined below */
void display_time(char *, struct tm *);	
double julian_date(struct tm *, int);			  
double sidereal_time(double, struct tm *, double);
				  
int main(void)
{
  double julian, sidereal;
  float longitude;
  double sid_hr, sid_mn, sid_sc;
  char c;
  time_t timer;
 
  /* Print header */
  printf("\n");
  printf(" ***********************************************************************************\n");
  printf(" * This program displays the computer's local time, the Greenwich Mean Time (GMT), *\n");
  printf(" * the Julian date, and local sidereal time (LST).  The only input required is the *\n");
  printf(" * the user's longitude.  This must be entered in degrees and decimal degrees      *\n");
  printf(" * (dd.ddd) east or west the prime meridian.  For locations east of the prime      *\n");
  printf(" * meridian, the value entered should be positive (+).  For locations west of the  *\n");
  printf(" * prime meridian, the value should be negative (-).                               *\n");
  printf(" ***********************************************************************************\n");
  printf("\n");
  
  /* Prompt user for longitude in decimal degrees */
  printf(" Enter your longitude: ");
  scanf("%f",&longitude);
  
  do 
  {
    /* Display the user's longitude */
    printf("\n Longitude:  \t%0.2f ",fabs(longitude));
    if (longitude < 0.0)
	  printf("West\n");
    else
      printf("East\n");
    /* Load time struct with current data. Show local time and GMT */
    timer=time(NULL);
    display_time(" Local:      ",localtime(&timer));
    display_time(" GMT:        ",gmtime(&timer));
  
    /* Call Julian date function and display result */
    julian = julian_date(gmtime(&timer),0);
    printf(" Julian Date:\t%0.6f\n",julian); 
  
    /* Call Julian date function for present day at 0h GMT then call sidereal time function */
    julian = julian_date(gmtime(&timer),1);
    sidereal = sidereal_time(julian,gmtime(&timer),longitude);
  
    /* Split sidereal time into hours, minutes, and seconds then display */
    sid_mn = modf(sidereal,&sid_hr) * 60;
    sid_sc = modf(sid_mn,&sid_mn) * 60;
    modf(sid_sc,&sid_sc);  
    printf(" LST:        \t%02.0f:%02.0f:%02.0f\n",sid_hr,sid_mn,sid_sc);

    /* Read to end of line or end of buf to flush the input stream */
    do {
      c = fgetc(stdin);
    } while ((c != '\n') && (c != EOF));
  
    /* Wait for user to press ENTER to end or 'r' to repeat */
    printf("\n\n Press ENTER to exit or 'R' plus ENTER to repeat: ");
    scanf("%c",&c);
  } while ((c == 'r') || (c == 'R'));
  
  return 0;
}

/* Displays the time and date contained in the data of the struct that 'time' points to */
void display_time(char *str, struct tm *time)
{
  printf("%s\t%s, %02d %s",str,*(days+(time->tm_wday)),time->tm_mday,*(months+(time->tm_mon)));
  printf(" %d",time->tm_year + BASE_YEAR);
  printf(" %02d:%02d:%02d\n",time->tm_hour,time->tm_min,time->tm_sec);
}

/* Calculate the Julian date for the date and time contained in the struct that 'time' points to.         */
/* For 't_zero' = 0, show the exact Julian date.  For 't_zero' = 1, show the Julian date at prior 0h GMT. */
double julian_date(struct tm *time, int t_zero)
{
  int A,B;
  int year = time->tm_year + BASE_YEAR;
  int month = time->tm_mon + 1;
  double julian_day, DD;
  
  if (!t_zero)
    DD = time->tm_mday + ((time->tm_hour + time->tm_min/60.0 + time->tm_sec/3600.0)/24.0);
  else
    DD = time->tm_mday;
	
  if (month < 2)
  {
	year -= 1;
	month += 12;
  }
	
  A = year/100;
  B = 2 - A + A/4;
	
  julian_day = floor(365.25 * year) + floor(30.6001*(month + 1.0)) + DD + 1720994.5 + B;

  /* Return 'julian_day' in decimal format */
  return julian_day;
}

/* Calculate and return the sidereal time given inputs of the Julian date at prior 0h GMT, */
/* current GMT as given in struct that 'time' points to, and the user-inputed longitude.   */
double sidereal_time(double julian, struct tm *time, double lngtd)
{
  double T, UT;
  double local_sidereal;
	
  /* Calculate decimal equivalent of current GMT */
  UT = time->tm_hour;
  UT += time->tm_min/60.0 + time->tm_sec/3600.0;
	
  T = (julian - 2415020.0) / 36525.0;
  local_sidereal = 6.6460656 + 2400.051262 * T + 0.00002581 * T * T;
  
  /* Normalize 'local_sidereal between 0 and 23 */
  while (local_sidereal > 24)
  {
    local_sidereal -= 24;
  }

  local_sidereal += 1.002737908 * UT;
  local_sidereal += lngtd / 15.0;

  /* Normalize 'local_sidereal' between 0 and 23 */
  if (local_sidereal < 0)
  {
	local_sidereal += 24;
  }
  else if (local_sidereal > 24)
  {
    local_sidereal -= 24;
  }

  /* Return LST in decimal format */
  return local_sidereal;
}