/******************************************************************************/
/*                                                                            */
/* JulianDay.cpp                                                              */
/* Elementfunktionen der Klasse CJulianDay                                    */
/*                                                                            */
/******************************************************************************/

//#include "StdAfx.h"			// Precompiled Header
#include "JulianDay.h"		// eigene Klassendefinition
#include <math.h>			// mathematische Bibliothek
#include <stdio.h>			// sprintf etc.
#include <string.h>			// strcat etc.

CJulianDay::CJulianDay()
{
	// Variablen initialisieren
	SetJD(0,0.0);
}

const double CJulianDay::dSecsPerDay = 86400.0;

// wandelt aktuellen JulianDayFraction gemäß Konvention
void CJulianDay::Normalize(void)
{
	// auf Überlauf prüfen
	if(JulianDay.dFraction >= 1.0)
	{
		// Number anpassen
		JulianDay.lNumber += (long)JulianDay.dFraction;
		// Fraction anpassen
		JulianDay.dFraction -= (double)(long)JulianDay.dFraction;
	}
 	// auf Unterlauf prüfen
	else if(JulianDay.dFraction < 0.0)
	{
		// Number anpassen
		JulianDay.lNumber += (long)JulianDay.dFraction - 1;
		// Fraction anpassen
		JulianDay.dFraction += -(double)(long)JulianDay.dFraction + 1.0;
	}
}

CJulianDay::CJulianDay(long lNumber,double dFraction)
{
	// Variablen initialisieren
	SetJD(lNumber,dFraction);
}

CJulianDay::CJulianDay(JDStruct JD)
{
	// Variablen initialisieren
	SetJD(JD);
}

CJulianDay::CJulianDay(double dCompleteJD)
{
	// Variablen initialisieren
	SetJD((long)dCompleteJD,dCompleteJD - (double)(long)dCompleteJD);
}

CJulianDay::CJulianDay(USHORT usDay,USHORT usMonth,long lYear,USHORT usHour,USHORT usMinute,double dSecond)
{
	// UTC nach JulianDay umrechnen
	SetJD(usDay,usMonth,lYear,usHour,usMinute,dSecond);
}

CJulianDay::~CJulianDay()
{
}

// prüft, ob der JulianDay gleich 0 0.0 ist
BOOL CJulianDay::IsEmpty(void)
{
	if(!JulianDay.lNumber)
		if(JulianDay.dFraction == 0.0)
			return TRUE;
	return FALSE;
}

void CJulianDay::SetJD(long lNumber,double dFraction)
{
	// Variablen setzen
	JulianDay.lNumber = lNumber;
	JulianDay.dFraction = dFraction;
	// und Konventionen anpassen
	Normalize();
}

void CJulianDay::SetJD(JDStruct JD)
{
	// Variablen setzen
	JulianDay = JD;
	// und Konventionen anpassen
	Normalize();
}

JDStruct CJulianDay::GetJD(void) const
{
	// JulianDay zurückgeben
	return JulianDay;
}

void CJulianDay::GetJD(long* plNumber,double* pdFraction) const
{
	// JulianDay zurückgeben
	*plNumber = JulianDay.lNumber;
	*pdFraction = JulianDay.dFraction;
}

ULONG CJulianDay::GetNumber(void) const
{
	// JulianDay-Number zurückgeben
	return JulianDay.lNumber;
}

double CJulianDay::GetFraction(void) const
{
	// JulianDay-Fraction zurückgeben
	return JulianDay.dFraction;
}

CJulianDay CJulianDay::operator=(const CJulianDay& CJD)
{
	// JulianDay setzen
	JulianDay = CJD.GetJD();
	// eigenes Objekt zurückgeben
	return *this;
}

CJulianDay CJulianDay::operator=(JDStruct JD)
{
	// JulianDay setzen
	JulianDay = JD;
	// eigenes Objekt zurückgeben
	return *this;
}

CJulianDay CJulianDay::operator+(const CJulianDay& CJD)
{
	// neues JulianDay-Objekt erzeugen
	CJulianDay NewJD(JulianDay.lNumber + CJD.JulianDay.lNumber,JulianDay.dFraction + CJD.JulianDay.dFraction);
	// und Konventionen anpassen
	NewJD.Normalize();
	// neues Objekt zurückgeben
	return NewJD;
}

CJulianDay CJulianDay::operator+(JDStruct JD)
{
	// neues JulianDay-Objekt erzeugen
	CJulianDay NewJD(JulianDay.lNumber + JD.lNumber,JulianDay.dFraction + JD.dFraction);
	// und Konventionen anpassen
	NewJD.Normalize();
	// neues Objekt zurückgeben
	return NewJD;
}

CJulianDay CJulianDay::operator+=(const CJulianDay& CJD)
{
	// Addition durchführen
	JulianDay.lNumber += CJD.JulianDay.lNumber;
	JulianDay.dFraction += CJD.JulianDay.dFraction;
	// und Konventionen anpassen
	Normalize();
	// eigenes Objekt zurückgeben
	return *this;
}

CJulianDay CJulianDay::operator+=(JDStruct JD)
{
	// Addition durchführen
	JulianDay.lNumber += JD.lNumber;
	JulianDay.dFraction += JD.dFraction;
	// und Konventionen anpassen
	Normalize();
	// eigenes Objekt zurückgeben
	return *this;
}

CJulianDay CJulianDay::operator-(const CJulianDay& CJD)
{
	// neues JulianDay-Objekt erzeugen
	CJulianDay NewJD(JulianDay.lNumber - CJD.JulianDay.lNumber,JulianDay.dFraction - CJD.JulianDay.dFraction);
	// und Konventionen anpassen
	NewJD.Normalize();
	// neues Objekt zurückgeben
	return NewJD;
}

CJulianDay CJulianDay::operator-(JDStruct JD)
{
	// neues JulianDay-Objekt erzeugen
	CJulianDay NewJD(JulianDay.lNumber - JD.lNumber,JulianDay.dFraction - JD.dFraction);
	// und Konventionen anpassen
	NewJD.Normalize();
	// neues Objekt zurückgeben
	return NewJD;
}

CJulianDay CJulianDay::operator-=(const CJulianDay& CJD)
{
	// Addition durchführen
	JulianDay.lNumber -= CJD.JulianDay.lNumber;
	JulianDay.dFraction -= CJD.JulianDay.dFraction;
	// und Konventionen anpassen
	Normalize();
	// eigenes Objekt zurückgeben
	return *this;
}

CJulianDay CJulianDay::operator-=(JDStruct JD)
{
	// Addition durchführen
	JulianDay.lNumber -= JD.lNumber;
	JulianDay.dFraction -= JD.dFraction;
	// und Konventionen anpassen
	Normalize();
	// eigenes Objekt zurückgeben
	return *this;
}

BOOL CJulianDay::operator>(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber > CJD.JulianDay.lNumber)
		return TRUE;
	else if(JulianDay.lNumber < CJD.JulianDay.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction > CJD.JulianDay.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator>(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber > JD.lNumber)
		return TRUE;
	else if(JulianDay.lNumber < JD.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction > JD.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator>=(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber > CJD.JulianDay.lNumber)
		return TRUE;
	else if(JulianDay.lNumber < CJD.JulianDay.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction >= CJD.JulianDay.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator>=(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber > JD.lNumber)
		return TRUE;
	else if(JulianDay.lNumber < JD.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction >= JD.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator<(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber < CJD.JulianDay.lNumber)
		return TRUE;
	else if(JulianDay.lNumber > CJD.JulianDay.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction < CJD.JulianDay.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator<(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber < JD.lNumber)
		return TRUE;
	else if(JulianDay.lNumber > JD.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction < JD.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator<=(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber < CJD.JulianDay.lNumber)
		return TRUE;
	else if(JulianDay.lNumber > CJD.JulianDay.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction <= CJD.JulianDay.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator<=(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber < JD.lNumber)
		return TRUE;
	else if(JulianDay.lNumber > JD.lNumber)
		return FALSE;
	// JulianDay-Fractions vergleichen
	if(JulianDay.dFraction <= JD.dFraction)
		return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator==(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber == CJD.JulianDay.lNumber)
		// JulianDay-Fractions vergleichen
		if(JulianDay.dFraction == CJD.JulianDay.dFraction)
			return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator==(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber == JD.lNumber)
		// JulianDay-Fractions vergleichen
		if(JulianDay.dFraction == JD.dFraction)
			return TRUE;
	return FALSE;
}

BOOL CJulianDay::operator!=(const CJulianDay& CJD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber == CJD.JulianDay.lNumber)
		// JulianDay-Fractions vergleichen
		if(JulianDay.dFraction == CJD.JulianDay.dFraction)
			return FALSE;
	return TRUE;
}

BOOL CJulianDay::operator!=(JDStruct JD)
{
	// JulianDay-Numbers vergleichen
	if(JulianDay.lNumber == JD.lNumber)
		// JulianDay-Fractions vergleichen
		if(JulianDay.dFraction == JD.dFraction)
			return FALSE;
	return TRUE;
}

CJulianDay::operator JDStruct()
{
	return JulianDay;
}

CJulianDay::operator double()
{
	return (double)JulianDay.lNumber + JulianDay.dFraction;
}

void CJulianDay::AddSeconds(const double dSecs)
{
	// Sekunden hinzuaddieren
	JulianDay.dFraction += dSecs / dSecsPerDay;
	// und Konventionen anpassen
	Normalize();
}

// bestimmt den (vollständigen) Julian Day des übergebenen Datums und Uhrzeit (UTC)
/*#pragma warning (disable: 4244) // disable warning 'conversion from double to unsigned long possible loss of data'
void CJulianDay::SetJD(USHORT usDay,USHORT usMonth,long lYear,USHORT usHour,USHORT usMinute,double dSecond)
{
	int f,g,A;
	// JulianDay nach 'Fundamental Astronomy' by H. Karttunen, P.Kröger et.al.'
	if(usMonth >= 3)
    {
		f = lYear;
		g = usMonth;
    }
	else
    {
		f = lYear - 1;
		g = usMonth + 12;
    }
	A = 2 - (int)((double)f / 100.0) + (int)((double)f / 400.0);
	JulianDay.lNumber = (long)((int)(365.25 * f)) + (double)((int)(30.6001 * (g + 1))) + (double)usDay + (double)A + 1720994;
	JulianDay.dFraction = 0.5;
	// Uhrzeit mitberücksichtigen 
	JulianDay.dFraction += ((double)usHour / 24.0) + ((double)usMinute / 1440.0) + (dSecond / 86400.0);
	// eventuellen Übertrag berücksichtigen
	if(JulianDay.dFraction >= 1.0)
	{
		(JulianDay.lNumber)++;
		JulianDay.dFraction -= 1.0;
	}
}
#pragma warning (default: 4244)*/

// bestimmt den (vollständigen) Julian Day des übergebenen Datums und Uhrzeit (UTC)
// JulianDay nach JulianTime for Java von Dieter Egger 'http://alpha.fesg.tu-muenchen.de/dieter/java/methods.html'
void CJulianDay::SetJD(USHORT usDay,USHORT usMonth,long lYear,USHORT usHour,USHORT usMinute,double dSecond)
{
	BOOL reform;
	long a,b=0,c,d;
	double x1;

	if(usMonth < 3)
	{
		lYear--;
		usMonth +=12;
	}
	reform = (lYear == 1582) && ((usMonth == 10) && (usDay > 15) || (usMonth > 10)) || (lYear > 1582);
	if(reform)
	{
		a = lYear / 100;
		c = a / 4;
		b = 2 - a + c;
	}
	x1 = 365.25 * lYear;
	if(lYear < 0)
		x1 -= 0.75;
	c = (long)x1;
	d = (long)(30.6001 * (usMonth + 1));
	JulianDay.lNumber = c + d + usDay + 1720994;
	if(reform)
		JulianDay.lNumber += b;
	JulianDay.dFraction = 0.5;
	// Uhrzeit mitberücksichtigen 
	JulianDay.dFraction += ((double)usHour / 24.0) + ((double)usMinute / 1440.0) + (dSecond / 86400.0);
	// JulianDay gemäß Konventionen normieren
	Normalize();
}

void CJulianDay::SetJD(const UTCStruct UTC)
{
	// JulianDay setzen
	SetJD(UTC.usDay,UTC.usMonth,UTC.lYear,UTC.usHour,UTC.usMinute,UTC.dSecond);
}

// rechnet den aktuellen JulianDay in UTC-Kalenderzeit um
// Originalroutine stammt aus XEphem-V3.1
#pragma warning (disable: 4244) // disable warning 'conversion from double to unsigned short possible loss of data'
UTCStruct CJulianDay::GetUTC(void) const
{
	// lokale Konstanten
	const ULONG ulMJDOffset = 2415020;		// modified Julian Day gemäß XEphem-V3.1-Konventionen (entspricht 0.5 Jan 1900)
	// dynamische Variablen
	double dMJD0h;							// modified JulianDay der vorangehenden Mitternacht
	double d,f,i,a,b,ce,g;					// Variablen der XEphem-V3.1-Routine
	UTCStruct UTC;							// Rückgabewert;
	double dHoursOfDay;						// vergangene Stunden seit Mitternacht;

	// modified Julian Day (gemäß XEphem-Konventionen) der letzten vorangehenden Mitternacht bestimmen
	if(JulianDay.dFraction >= 0.5)
		dMJD0h = ((double)JulianDay.lNumber - (double)ulMJDOffset) + 0.5;
	else
		dMJD0h = ((double)JulianDay.lNumber - (double)ulMJDOffset) - 0.5;
	// Beginn der original XEphem-Routine
	d = dMJD0h + 0.5;
	i = floor(d);
	f = d - i;
	if(f == 1)
	{
		f = 0;
		i += 1;
	}
	if(i > -115860.0)
	{
		a = floor((i / 36524.25) + 0.9983573) + 14;
		i += 1 + a - floor(a / 4.0);
	}
	b = floor((i / 365.25) + 0.802601);
	ce = i - floor((365.25 * b) + 0.750001) + 416;
	g = floor(ce / 30.6001);
	UTC.usDay = ce - floor(30.6001 * g) + f;
	if(g > 13.5)
		UTC.usMonth = g - 13;
	else
		UTC.usMonth = g - 1;
	if(UTC.usMonth < 2.5)
		UTC.lYear = b + 1900;
	else
		UTC.lYear = b + 1899;
	if(UTC.lYear < 1)
		UTC.lYear -= 1;
	// vergangene Stunden seit letzter Mitternacht bestimmen
	if(JulianDay.dFraction >= 0.5)
		// es ist zwischen 0h und 12h
		dHoursOfDay = (JulianDay.dFraction - 0.5) * 24.0;
	else
		// es ist zwischen 12h und 24h
		dHoursOfDay = (JulianDay.dFraction + 0.5) * 24.0;
	// ganze Stunden absplitten
	UTC.usHour = (USHORT)(dHoursOfDay);
	// ganze Minuten absplitten
	UTC.usMinute = (USHORT)((dHoursOfDay - (double)UTC.usHour) * 60.0);
	// verbleidende Sekunden bestimmen
	UTC.dSecond = ((dHoursOfDay - (double)UTC.usHour) * 60.0 - (double)UTC.usMinute) * 60.0;
	// Sekunden auf eine 1 nsec genau auf- bzw. abrunden (entspricht ungefähr der 13.Nachkommastelle des Fraction of Days und somit ungefähr der Rechengenauigkeit einer double-precision-Variable im Bereich zwischen 0 und <1)
	UTC.dSecond = floor(UTC.dSecond * 1.0E9 + 0.5) / 1.0E9;
	// UTC-Zeit zurückgeben
	return UTC;
}
#pragma warning (default: 4244)

// liefert die verstrichenen Sekunden seit JD 0 0.0 zurück
double CJulianDay::ConvertToSeconds(void) const
{
	double dSeconds;
	// ganze Tage in Sekunden umwandeln
	dSeconds = JulianDay.lNumber * dSecsPerDay;
	// und Tagesbruchteil hinzuaddieren
	dSeconds += JulianDay.dFraction * dSecsPerDay;
	// auf Nanosekunden runden
	dSeconds = floor(dSeconds * 1E9 + 0.5) / 1E9;
	// Wert zurückgeben
	return dSeconds;
}

// liefert den aktuellen JulianDay als String zurück
char* CJulianDay::GetUTCString(char* szOutputString,int nMaxChars)
{
	UTCStruct UTC;

	// Speicherplatz prüfen
	if(nMaxChars < 38)
		// Fehler melden
		return NULL;
	// JulianDay in UTC umwandeln
	UTC = GetUTC();
	// und in String schreiben
	sprintf(szOutputString,"%02u.%02u.%04ld %02u:%02u:%019.16f",UTC.usDay,UTC.usMonth,UTC.lYear,UTC.usHour,UTC.usMinute,UTC.dSecond);
	// rechtseitige Nullen entfernen
	while(szOutputString[strlen(szOutputString) - 1] == '0')
	{
		szOutputString[strlen(szOutputString) - 1] = (char)0;
	}
	// aber mindestens eine Null
	if(szOutputString[strlen(szOutputString) - 1] == '.')
		// Null hinzufügen
		strcat(szOutputString,"0");
	// Pointer zurückgeben
	return szOutputString;
}

// liefert den Wochentag als Integer zurück
// Originalroutine stammt aus XEphem
// given an mjd, set *dow to 0..6 according to which day of the week it falls
// on (0=sunday).
// return 0 if ok else -1 if can't figure it out.
short CJulianDay::GetDayOfWeek(void) const
{
	// lokale Konstanten
	const ULONG ulMJDOffset = 2415020;		// modified Julian Day gemäß XEphem-V3.1-Konventionen (entspricht 0.5 Jan 1900)
	double dMJD0h;			// modified JulianDay nach XEphem-Konventionen
	short sDayOfWeek;

	// modified Julian Day (gemäß XEphem-Konventionen) der letzten vorangehenden Mitternacht bestimmen
	if(JulianDay.dFraction >= 0.5)
		dMJD0h = ((double)JulianDay.lNumber - (double)ulMJDOffset) + 0.5;
	else
		dMJD0h = ((double)JulianDay.lNumber - (double)ulMJDOffset) - 0.5;
	// Beginn der Original XEphem-Routine
    /* cal_mjd() uses Gregorian dates on or after Oct 15, 1582.
     * (Pope Gregory XIII dropped 10 days, Oct 5..14, and improved the leap-
     * year algorithm). however, Great Britian and the colonies did not
     * adopt it until Sept 14, 1752 (they dropped 11 days, Sept 3-13,
     * due to additional accumulated error). leap years before 1752 thus
     * can not easily be accounted for from the cal_mjd() number...
     */
	if(dMJD0h < -53798.5)
	{
		/* pre sept 14, 1752 too hard to correct |:-S */
		return (-1);
	}
	sDayOfWeek = ((long)floor(dMJD0h-.5) + 1) % 7;/* 1/1/1900 (mjd 0.5) is a Monday*/
	if(sDayOfWeek < 0)
		sDayOfWeek += 7;
	return (sDayOfWeek);
}

// liefert den Wochentag als ASCII-String zurück
char* CJulianDay::GetDayOfWeekString(void) const
{
	static const char *szDay[] = { "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" , "" };
	short sDayOfWeek;
	char* szDoW;

	// Wochentag bestimmen lassen
	sDayOfWeek = GetDayOfWeek();
	// auf Error-return prüfen
	if(sDayOfWeek == -1)
		// Error-String setzen
		sDayOfWeek = 7;
	szDoW = (char*)&(szDay[sDayOfWeek][0]); 
	// und in String umwandeln
	return szDoW;
}
