/* (C) Mike Henderson <mghenderson@lanl.gov>. 
 *
 *  I've converted to glib types, removed unused variables, and piped the whole
 *  thing through indent.
 *
 *  josh buhl <jbuhl@users.sourceforge.net> 
 */

#include "Moon.h"
#include "MoonRise.h"

void
UTTohhmm(gdouble UT, gint * h, gint * m)
{
	if (UT < 0.0) {
		*h = -1.0;
		*m = -1.0;
	} else {
		*h = (gint) UT;
		*m = (gint) ((UT - (gdouble) (*h)) * 60.0 + 0.5);
		if (*m == 60) {
			/* 
			 *  If it was 23:60 this should become 24:00
			 *  I prefer this designation to 00:00. So dont reset h to 0 when it goes above 24.
			 */
			*h += 1;
			*m = 0;
		}
	}

}

static gint
Interp(gdouble ym, gdouble y0, gdouble yp, gdouble * xe, gdouble * ye,
       gdouble * z1, gdouble * z2, gint * nz)
{

	gdouble a, b, c, d, dx;

	*nz = 0;
	a = 0.5 * (ym + yp) - y0;
	b = 0.5 * (yp - ym);
	c = y0;
	*xe = -b / (2.0 * a);
	*ye = (a * (*xe) + b) * (*xe) + c;
	d = b * b - 4.0 * a * c;

	if (d >= 0) {
		dx = 0.5 * sqrt(d) / fabs(a);
		*z1 = *xe - dx;
		*z2 = *xe + dx;
		if (fabs(*z1) <= 1.0)
			*nz += 1;
		if (fabs(*z2) <= 1.0)
			*nz += 1;
		if (*z1 < -1.0)
			*z1 = *z2;
	}

	return (0);
}


static gdouble SinH(CTrans * c, gdouble UT)
{

	gdouble TU, TU2, TU3, LambdaMoon, BetaMoon, R, AGE, frac(), jd();
	gdouble RA_Moon, DEC_Moon, gmst, lmst, Tau, epsilon, Moon();
	gdouble angle2pi();

	TU = (jd(c->year, c->month, c->day, UT) - 2451545.0) / 36525.0;

	/* this is more accurate, but expensive. */
	TU2 = TU * TU;
	TU3 = TU2 * TU;
	Moon(TU, &LambdaMoon, &BetaMoon, &R, &AGE);
	LambdaMoon *= RadPerDeg;
	BetaMoon *= RadPerDeg;
	epsilon =
	    (23.43929167 - 0.013004166 * TU - 1.6666667e-7 * TU2 -
	     5.0277777778e-7 * TU3) * RadPerDeg;
	RA_Moon =
	    angle2pi(atan2
		     (sin(LambdaMoon) * cos(epsilon) -
		      tan(BetaMoon) * sin(epsilon), cos(LambdaMoon)));
	DEC_Moon =
	    asin(sin(BetaMoon) * cos(epsilon) +
		 cos(BetaMoon) * sin(epsilon) * sin(LambdaMoon));


	/* This is less accurate, but computationally less expensive */
/*      MiniMoon(TU, &RA_Moon, &DEC_Moon); */
/*      RA_Moon *= 15.0*RadPerDeg; */
/*      DEC_Moon *= RadPerDeg; */

	/*
	 *  Compute Greenwich Mean Sidereal Time (gmst)
	 */
	UT = 24.0 * frac(UT / 24.0);
	/* this is for the ephemeris meridian??? 
	   gmst = 6.697374558 + 1.0027379093*UT + (8640184.812866+(0.093104-6.2e-6*TU)*TU)*TU/3600.0;
	 */
	gmst =
	    UT + 6.697374558 + (8640184.812866 +
				(0.093104 -
				 6.2e-6 * TU) * TU) * TU / 3600.0;
	lmst = 24.0 * frac((gmst - c->Glon / 15.0) / 24.0);

	Tau = 15.0 * lmst * RadPerDeg - RA_Moon;

	return (c->SinGlat * sin(DEC_Moon) +
		c->CosGlat * cos(DEC_Moon) * cos(Tau));
}


void
MoonRise(CTrans * c, gdouble * UTRise, gdouble * UTSet)
{

	gdouble UT, ym, y0, yp, SinH0;
	gdouble xe, ye, z1, z2;
	gint Rise, Set, nz, TimeZone;

	SinH0 = sin(8.0 / 60.0 * RadPerDeg);

	/* report rise and set times in LST */
	TimeZone = c->UT - c->LST;

	UT = 1.0 + TimeZone;
	*UTRise = -999.0;
	*UTSet = -999.0;
	Rise = Set = 0;

	ym = SinH(c, UT - 1.0) - SinH0;

	while ((UT <= 24.0 + TimeZone)) {

		y0 = SinH(c, UT) - SinH0;
		yp = SinH(c, UT + 1.0) - SinH0;

		Interp(ym, y0, yp, &xe, &ye, &z1, &z2, &nz);

		switch (nz) {

		case 0:
			break;
		case 1:
			if (ym < 0.0) {
				*UTRise = UT + z1;
				Rise = 1;
			} else {
				*UTSet = UT + z1;
				Set = 1;
			}
			break;
		case 2:
			if (ye < 0.0) {
				*UTRise = UT + z2;
				*UTSet = UT + z1;
			} else {
				*UTRise = UT + z1;
				*UTSet = UT + z2;
			}
			Rise = 1;
			Set = 1;
			break;
		}
		ym = yp;
		UT += 2.0;

	}

	if (Rise) {
		*UTRise -= TimeZone;
		*UTRise = hour24(*UTRise);
	} else {
		*UTRise = -999.0;
	}

	if (Set) {
		*UTSet -= TimeZone;
		*UTSet = hour24(*UTSet);
	} else {
		*UTSet = -999.0;
	}

}
