/**************************************************************

trajd.c (C-Munipack project)
Originates from original Munipack by Filip Hroch.

 trajd.f -- translated by f2c (version 19950110).
   You must link the resulting object file with the libraries:
	-lf2c -lm   (in that order)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

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

#include "cmpack_common.h"
#include "comfun.h"
#include "trajd.h"

/* Inacuracy in singularity tests */
#define TOL		0.000001
        
/* Angle of earth's axis */
#define EPS		((23+26/60.0)/180*M_PI)
        
/* Converts citizen date to Julian date */
double cmpack_encodejd(const CmpackDateTime *dt)
{
    double jd1;
    int before, d1, d2;

	/* Check date and time */
	if (dt->date.day<=0 || dt->date.year<=0 || dt->date.month<=0) 
		return 0;

    /* Compute Julian date from input citizen year, month and day. */
    /* Tested for YEAR>0 except 1582-10-07/15 */
    if (dt->date.year > 1582) {
	    before = 0;
    } else if (dt->date.year < 1582) {
	    before = 1;
    } else
	if (dt->date.month > 10) {
	    before = 0;
	} else if (dt->date.month < 10) {
	    before = 1;
	} else
	if (dt->date.day >= 15) {
		before = 0;
	} else {
	    before = 1;
	}
    if (dt->date.month <= 2) {
		d1 = (int)(365.25*(dt->date.year-1));
		d2 = (int)(30.6001*(dt->date.month+13));
	} else {
		d1 = (int)(365.25*(dt->date.year));
		d2 = (int)(30.6001*(dt->date.month+1));
	}
	jd1 = 1720994.5 + d1 + d2 + dt->date.day;
    jd1 += 1.0*dt->time.hour/24;
    jd1 += 1.0*dt->time.minute/1440.0;
    jd1 += 1.0*dt->time.second/86400.0;
	jd1 += 1.0*dt->time.milisecond/86400000.0;

    if (before) {
	    if (dt->date.year<0) 
			return jd1-1;
	     else 
			return jd1;
	} else {
		return jd1 + 2 - (dt->date.year/100) + (dt->date.year/400);
	}
}

int cmpack_decodejd(double jd, CmpackDateTime *dt)
{
    int time, z, e, c, a, b, x;

	/* Check Julian date */
	memset(dt, 0, sizeof(CmpackDateTime));
	if (jd<=0.0) 
		return CMPACK_ERR_INVALID_DATE;

    /* Compute citizen date: year, month and day from input Julian date. */
    /* Only for JD>0! Tested for YEAR>0 except 1582-10-07/15. */
    z = (int)(jd+0.5);
    if (z>=2299163) {
	    int alpha = (int)((z-1867216.25)/36524.25);
	    a = z + 1 + alpha - (alpha/4);
    } else {
	    a = z;
    }
    b = a + 1524;
    c = (int)((b-122.1)/365.25);
    x = (int)(365.25*c);
    e = (int)((b-x)/30.6001);
    dt->date.day   = b-x-(int)(30.6001*e);
    dt->date.month = (e<=13 ? e-1 : e-13);
    dt->date.year  = (dt->date.month>=3 ? c-4716 : c-4715);

    /* Cas v milisekundach */
    time = (int)((jd+0.5-z)*86400000.0+0.5);
    dt->time.hour   = (time/3600000);
    dt->time.minute = (time/60000)%60;
    dt->time.second = (time/1000)%60;
    dt->time.milisecond = time%1000;
	return 0;
}

/* String to date */
int cmpack_strtodate(const char *datestr, CmpackDate *date)
{
	int y, m = 1, d = 1;
	const char *ptr;
	char *endptr;
	
	memset(date, 0, sizeof(CmpackDate));
	
	if (!datestr)
		return CMPACK_ERR_INVALID_DATE;
		
	ptr = datestr;
	y = strtol(ptr, &endptr, 10);
	if (y<1600 || y>=2600 || endptr==ptr)
		return CMPACK_ERR_INVALID_DATE;
	ptr = endptr + strspn(endptr, "-./: \t,+");
	if (*ptr!='\0') {
		m = strtol(ptr, &endptr, 10); 
		if (m<1 || m>12 || endptr==ptr)
			return CMPACK_ERR_INVALID_DATE;
		ptr = endptr + strspn(endptr, "-./: \t,+");
	}
	if (*ptr!='\0') {
		d = strtol(ptr, &endptr, 10); 
		if (d<1 || d>31 || endptr==ptr)
			return CMPACK_ERR_INVALID_DATE;
	}
	/* Valid date */
	date->year = y;
	date->month = m;
	date->day = d;
	return CMPACK_ERR_OK;
}

/* String to time */
int cmpack_strtotime(const char *timestr, CmpackTime *time)
{
	int h = 0, m = 0, x = 0;
	const char *ptr;
	char *endptr;

	memset(time, 0, sizeof(CmpackTime));
	
	if (!timestr)
		return CMPACK_ERR_INVALID_DATE;

	ptr = timestr;
	if (*ptr!='\0') {
		h = strtol(ptr, &endptr, 10);
		if (h<0 || h>=24 || endptr==ptr)
			return CMPACK_ERR_INVALID_DATE;
		ptr = endptr + strspn(endptr, "-./: \t,+");
	}
	if (*ptr!='\0') {
		m = strtol(ptr, &endptr, 10);
		if (m<0 || m>=60 || endptr==ptr)
			return CMPACK_ERR_INVALID_DATE;
		ptr = endptr + strspn(endptr, "-./: \t,+");
	}
	if (*ptr!='\0') {
	    x = (int)(1000.0*strtod(ptr, &endptr)+0.5);
	    if (x<0 || x>=60000 || endptr==ptr)
	    	return CMPACK_ERR_INVALID_DATE;
    }
    /* Valid time */
    time->hour = h;
    time->minute = m;
    time->second = x/1000;
    time->milisecond = x%1000;
    return CMPACK_ERR_OK;
}

/* Date to string */
int cmpack_datetostr(const CmpackDate *date, char *buf, int buflen)
{
	buf[0] = '\0';
	if (date->year<1970 || date->year>9999 || date->month<1 || date->month>12 || date->day<1 || date->day>31)
		return CMPACK_ERR_INVALID_DATE;
	if (buflen<11)
		return CMPACK_ERR_BUFFER_TOO_SMALL;
	sprintf(buf, "%04d-%02d-%02d", date->year, date->month, date->day);
	return 0;
}

/* Time to string */
int cmpack_timetostr(const CmpackTime *time, char *buf, int buflen)
{
	buf[0] = '\0';
	if (time->hour<0 || time->hour>24 || time->minute<0 || time->minute>60 || time->second<0 || time->second>60 || time->milisecond<0 || time->milisecond>1000)
		return CMPACK_ERR_INVALID_DATE;
	if (buflen<13)
		return CMPACK_ERR_BUFFER_TOO_SMALL;
	sprintf(buf, "%02d:%02d:%02d.%03d", time->hour, time->minute, time->second, time->milisecond);
	return 0;
}

/* Computes Greenwich mean sidereal time in hours */
double cmpack_siderealtime(double jd)
{
	double jd0, t3, s0, s1;

	/* JD at 0h UT */
	jd0 = floor(jd) + 0.5;
	/* Julian centuries */
	t3 = (jd0 - 2451545.0)/36525.0;
	/* Mean sidereal time for 0h UT */
	s0 = 6.697374558 + 2400.05133691*t3 + 0.0000258622*t3*t3 - 0.0000000017*t3*t3*t3;
	/* Mean sidereal time */
	s1 = s0/24.0 + 1.0027379093*(jd-jd0);
	return 24.0*(s1 - floor(s1));
}

/* Transforms equatorial coordinates to horizontal system */
void cmpack_azalt(double jd, double ra, double dec, double lon, double lat, double *az, double *alt)
{
	double lmst, har, x, y;
	
	/* Local mean sidereal time (in hours) */
	lmst = cmpack_siderealtime(jd) + (lon/15.0);
	/* Hour angle (in radians) */
	har = (lmst-ra)/12.0*M_PI;
    dec = dec/180.0*M_PI;
	lat = lat/180.0*M_PI;
    /* Compute azimuth */
    x = cos(dec)*sin(har);
    y = cos(dec)*cos(har)*sin(lat) - sin(dec)*cos(lat);
  	x = 0.75 - (atan2(y, x)/(2.0*M_PI));
   	if (az) *az = 360.0*(x-floor(x));
	/* Compute altitude */
    if (alt) *alt = asin(sin(dec)*sin(lat) + cos(dec)*cos(har)*cos(lat))/M_PI*180.0;
}

/* Prevede souradnice ra,de (rovnikove) na la, be (ekliptikalni) */
void cmpack_rdtolb(double ra, double de, double *la, double *be)
{
  /* Prevod na radiany */
  ra = ra/12.0*M_PI;
  de = de/180.0*M_PI;
  /* Do intervalu 0..2pi */
  while (ra<0.0)   ra += 2*M_PI;
  while (ra>=2*M_PI) ra -= 2*M_PI;
  /* Vylouceni singularit */
  if (de>M_PI/2-TOL) {
    *la=M_PI/2;
    *be=M_PI/2-EPS;
  } else
  if (de<-M_PI/2+TOL) {
    *la=3*M_PI/2;
    *be=EPS-M_PI/2;
  } else
  if (fabs(ra-M_PI/2)<TOL) {
    *la=M_PI/2;
    *be=de-EPS;
  } else
  if (fabs(ra-3*M_PI/2)<TOL) {
    *la=3*M_PI/2;
    *be=de+EPS;
  } else {
    /* Normalni vypocet */
    *be=asin(cos(EPS)*sin(de)-sin(EPS)*cos(de)*sin(ra));
    *la=atan((sin(EPS)*sin(de)+cos(EPS)*cos(de)*sin(ra))/(cos(de)*cos(ra)));
    /* Do spravneho kvadrantu */
    if (cos(ra)<0.0) *la += M_PI;
  }
}

void cmpack_sun(double jd, double *sun_ls, double *sun_rs)
/* Vypocet ekl. delky slunce a vzdalenosti zeme - slunce */
{
 double t,vt,ls,ms,m5,m4,m2,lm;

 t =jd-2451545.0;
 vt=1+(t/36525.0);
 ls=(0.779072 + 0.00273790931*t)*2*M_PI;
 ms=(0.993126 + 0.00273777850*t)*2*M_PI;
 m5=(0.056531 + 0.00023080893*t)*2*M_PI;
 m4=(0.053856 + 0.00145561327*t)*2*M_PI;
 m2=(0.140023 + 0.00445036173*t)*2*M_PI;
 lm=(0.606434 + 0.03660110129*t)*2*M_PI;
 ls=ls*3600*180/M_PI+6910*sin(ms)
       +  72*sin(2*ms)
       -17*vt*sin(ms)
       -   7*cos(ms-m5)
       +   6*sin(lm-ls)
       +   5*sin(4*ms+8*m4+3*m5)
       -   5*cos(2*ms-2*m2)
       -   4*sin(ms-m2)
       +   4*cos(4*ms-8*m4+3*m5)
       +   3*sin(2*ms-2*m2)
       -   3*sin(m5)
       -   3*sin(2*ms-2*m5);
 /* Ekl. delka slunce v radianech */
 *sun_ls=ls/3600/180*M_PI;
 /* vzdalenost slunce v AU */
 *sun_rs=1.00014-0.01675*cos(ms)-0.00014*cos(2*ms);
}
