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

#define DegPerRad       57.29577951308232087680
#define RadPerDeg        0.01745329251994329576

extern	double	Glon, SinGlat, CosGlat, TimeZone;

double    cosEPS = 0.91748;
double    sinEPS = 0.39778;
double    P2  = 6.283185307;


SunRise(int year, int month, int day, double LocalHour, double *LTRise, double *LTSet){

    double	UT, ym, y0, yp, SinH0;
    double	xe, ye, z1, z2, SinHS(), hour24();
    int		Rise, Set, nz;

    SinH0 = sin( -50.0/60.0 * RadPerDeg );

    UT = 1.0+TimeZone;
    *LTRise = -999.0;
    *LTSet = -999.0;
    Rise = Set = 0;
    ym = SinHS(year, month, day, UT-1.0) - SinH0;

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

	y0 = SinHS(year, month, day, UT) - SinH0;
	yp = SinHS(year, month, day, UT+1.0) - SinH0;

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

	switch(nz){

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

    }

    if (Rise){
        *LTRise -= TimeZone;
        *LTRise = hour24(*LTRise);
    } else {
        if (ym < 0.0) {
            *LTRise = -999.0;
        } else {
            *LTRise = -1.0;
        }
    }

    if (Set){
        *LTSet -= TimeZone;
        *LTSet = hour24(*LTSet);
    } else {
        if (ym < 0.0) {
            *LTSet = -999.0;
        } else {
            *LTSet = -1.0;
        }
    }

}

CivTwil(int year, int month, int day, double LocalHour, double *CTBeg, double *CTEnd){

    double	UT, ym, y0, yp, SinH0;
    double	xe, ye, z1, z2, SinHS(), hour24();
    int		Rise, Set, nz;

    SinH0 = sin( -6.0 * RadPerDeg );


    UT = 1.0+TimeZone;
    *CTBeg = -999.0;
    *CTEnd = -999.0;
    Rise = Set = 0;
    ym = SinHS(year, month, day, UT-1.0) - SinH0;

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

	y0 = SinHS(year, month, day, UT) - SinH0;
	yp = SinHS(year, month, day, UT+1.0) - SinH0;

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

	switch(nz){

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

    }

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

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

}

GoldHour(int year, int month, int day, double LocalHour, double *GHBeg, double *GHEnd){

    double	UT, ym, y0, yp, SinH0;
    double	xe, ye, z1, z2, SinHS(), hour24();
    int		Rise, Set, nz;

    SinH0 = sin( 6.0 * RadPerDeg );


    UT = 1.0+TimeZone;
    *GHBeg = -999.0;
    *GHEnd = -999.0;
    Rise = Set = 0;
    ym = SinHS(year, month, day, UT-1.0) - SinH0;

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

	y0 = SinHS(year, month, day, UT) - SinH0;
	yp = SinHS(year, month, day, UT+1.0) - SinH0;

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

	switch(nz){

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

    }

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

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

}

MoonRise(int year, int month, int day, double LocalHour, double *UTRise, double *UTSet){

    double	UT, ym, y0, yp, SinH0;
    double	xe, ye, z1, z2, SinHM(), hour24();
    int		Rise, Set, nz;

    SinH0 = sin( 8.0/60.0 * RadPerDeg );


    UT = 1.0+TimeZone;
    *UTRise = -999.0;
    *UTSet = -999.0;
    Rise = Set = 0;
    ym = SinHM(year, month, day, UT-1.0) - SinH0;

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

	y0 = SinHM(year, month, day, UT) - SinH0;
	yp = SinHM(year, month, day, 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;
    }

}


UTTohhmm(double UT, int *h, int *m){


    if (UT < 0.0) {
	*h = -1.0;
	*m = -1.0;
    } else {
        *h = (int)UT;
        *m = (int)((UT-(double)(*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;
	}
    }

}






Interp(double ym, double y0, double yp, double *xe, double *ye, double *z1, double *z2, int *nz){

    double	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);


}




double SinHS(int year, int month, int day, double UT){

    double	TU0, TU, TU2, TU3, LambdaMoon, BetaMoon, R, AGE, frac(), jd();
    double	RA_Sun, DEC_Sun, T0, gmst, lmst, Tau, epsilon;
    double	M, DL, L, SL, X, Y, Z, RHO;


    TU0 = (jd(year, month, day, 0.0) - 2451545.0)/36525.0;

    TU = (jd(year, month, day, UT+62.0/3600.0) - 2451545.0)/36525.0;
    TU2 = TU*TU;
    TU3 = TU2*TU;

    M = P2*frac(0.993133 + 99.997361*TU);
    DL = 6893.0*sin(M) + 72.0*sin(2.0*M);
    L = P2*frac(0.7859453 + M/P2 + (6191.2*TU+DL)/1296e3);
    SL = sin(L);
    X = cos(L); Y = cosEPS*SL; Z = sinEPS*SL; RHO = sqrt(1.0-Z*Z);
    DEC_Sun = atan2(Z, RHO);
    RA_Sun = (48.0/P2)*atan(Y/(X+RHO));
    if (RA_Sun < 0) RA_Sun += 24.0;

    RA_Sun = RA_Sun*15.0*RadPerDeg;

    /*
     *  Compute Greenwich Mean Sidereal Time (gmst)
     */
    UT = 24.0*frac( UT/24.0 );
/*
    gmst = 6.697374558 + 1.0027379093*UT + (8640184.812866*TU0 +(0.093104-6.2e-6*TU)*TU2)/3600.0;
*/
    gmst = 6.697374558 + 1.0*UT + (8640184.812866+(0.093104-6.2e-6*TU)*TU)*TU/3600.0;
    lmst = 24.0*frac( (gmst-Glon/15.0) / 24.0 );

    Tau = 15.0*lmst*RadPerDeg - RA_Sun;
    return( SinGlat*sin(DEC_Sun) + CosGlat*cos(DEC_Sun)*cos(Tau) );


}


double frac_plus(double x){

    x -= (int)x;
    return( (x<0) ? x+1.0 : x );

}


double SinHM(int year, int month, int day, double UT){

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

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

    /* this is more accurate, but wasteful for this -- use low res approx.
    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));
    */

    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-Glon/15.0) / 24.0 );

    Tau = 15.0*lmst*RadPerDeg - RA_Moon;
    return( SinGlat*sin(DEC_Moon) + CosGlat*cos(DEC_Moon)*cos(Tau) );


}

