package org.josast.util.date;

import java.util.GregorianCalendar;



/**
 * <b>Description : Julian day computing</b><BR>
 * 
 * <p>
 * <b>date </b>7 août 2003
 * </p>
 * 
 * <p>
 * <b><i> Source Update </i></b>
 * </p>
 * <br> date : name : comments <br>
 * V1 : mercier : create file <br>
 * V1.1 : Mercier : correction bug moi de février
 * 
 * <p>
 * This class is a set of static method to use Julian day.
 * </p>
 * 
 * <p>
 * Algorithm are based on french version of the Jean Meeus book "Calculs
 * Astronomiques à l'usage des Amateurs" editor : société Astronomique de
 * France.
 * </p>
 * 
 * <p></p>
 *
 * @author XTOPHE <a href="mailto:josast@avmdti.org">josast at
 *         avmdti.org</a>
 * @version 1.1
 */
public class JulianDay {
    //~ Static fields/initializers ----------------------------------------

    private static final double EPOCH_JAN1_00H_1900 = 2415019.5; // Jan 1.0 1900
                                                                 // = Jan 1 1900
                                                                 // 00h UTC


                                                                 // = Jan 1 1900
                                                                 // 12h UTC

                                                                 // = Jan 1 2000
  
    /** DOCUMENT ME! */
    private static final double EPOCH_JAN1_12H_1900 = 2415020.0; // Jan 1.5 1900 = Jan 1 1900 12h UTC
                                                               // 12h UTC

    //~ Constructors ------------------------------------------------------

  

   //~ Methods -----------------------------------------------------------




	// static method in the class, the constructor is not needed
	
	private JulianDay()
	{}
	
	/**
	 * Convert epoch from TLE data to julian day
	 * this method is valid from 1957 through 2056
	 * epoch is : yyddd.ddddddd
	 * where yy is year
	 * ddd.ddddddd is day of the curent year
	 * @param epoch
	 * @return
	 */
	public static double convertToJulianDay(double epoch)
	{
		double year, day, JDoE;
		/* Valid 1957 through 2056 */
		year = Math.floor(epoch*1.0E-3);
		if (year < 57)
			year += 2000;
		else
			year += 1900;
		day  = epoch - (Math.floor(epoch*1.0E-3))*1.0E3;
		JDoE = Julian_Date_of_Year(year) + day;

		return JDoE;
	}







	/**
    * Astronomical Formulae for Calculators, Jean Meeus, pages 23-25 Calculate
     * Julian Date of 0.0 Jan year
     * 
	 * @param year
	 * @return Julian day
	 */
	public static double Julian_Date_of_Year(double year)
	{
		/* Astronomical Formulae for Calculators, Jean Meeus, pages 23-25 
		   Calculate Julian Date of 0.0 Jan year */
		double A, B, JDoY;
		year = year - 1.0;
		A = Math.floor(year / 100.0);
		B  = 2.0 - A + Math.floor(A / 4.0);
		JDoY = Math.floor(365.25 * year) + Math.floor(30.6001 * 14.0) + 1720994.5 + B;
		
		return JDoY;
	}

	/**
	 * Method diffdate
	 * 
	 * This method realise the difference between 2 date.
	 * The folowing treatement is made :
	 * 		- convert the 2 date in Julian Day
	 * 		- return the difference between the 2 Julian Day
	 * .
	 * @param d1
	 * @param d2
	 * @return double
	 */
	public static double diffdate(GregorianCalendar d1, GregorianCalendar d2) {
		return (convertToJulianDay(d1) - convertToJulianDay(d2));
	}

	/**
	 * Method addJours.
	 * 
	 * This method allows to add a number of day to a date.
	 * The folowing treatement is made :
	 * 	- convert the date to Julian Day
	 * 	- add the number of day
	 *  - convert the date to GregorianCalendar
	 * 
	 * @param d1 date 
	 * @param jour number of day to add
	 * @return GregorianCalendar
	 */
	public static GregorianCalendar addDays(GregorianCalendar d1, long jour) {
		double JDd1 = convertToJulianDay(d1);
		JDd1 += jour;
		return (convertToGregorianCalendar(JDd1));

	}

	/**
	 * Method convertToJourJulien.
	 *
	 * 	This method convert a date in Julian Day.
	 * 	
	 * 	This method call the convertToJourJulien(int annee,int mois,int jour,int heure,int minute,int seconde)
	 * 
	 * @param date 
	 * @return double
	 */
	public static double convertToJulianDay(GregorianCalendar date) {
		
		return convertToJulianDay(
			date.get(GregorianCalendar.YEAR),
			date.get(GregorianCalendar.MONTH)+1,
			date.get(GregorianCalendar.DAY_OF_MONTH),
			date.get(GregorianCalendar.HOUR_OF_DAY),
			date.get(GregorianCalendar.MINUTE),
			date.get(GregorianCalendar.SECOND));

	}
	/**
	 * Method convertToJourJulien.
	 *
	 * 	This method convert a date in Julian Day.
	 * 	
	 * 	This method call the convertToJourJulien(int annee,int mois,int jour,int heure,int minute,int seconde)
	 * 
	 * @param date 
	 * @return double
	 */
	public static double convertToJulianDay(GenericDate date) {
		return convertToJulianDay(
			date.get_year(),
			date.get_month(),
			date.get_day(),
			date.get_hour(),
			date.get_minute(),
			date.get_seconde());

	}

	/**
	 * Method convertToJourJulien.
	 * 
	 * This method concert a date in Julian Date. The algorithm from the Jean Meeus book is used.
	 * 
	 * This method as been tested with the exemple of the book. cf Unit test.
	 * 
	 * Be carrefull the input are not checked.
	 * 	
	 * 
	 * @param annee positive and negative year coold be used 
	 * @param mois 1->junary 12->december
	 * @param jour 1 to 31
	 * @param heure 0-24 
	 * @param minute 0-59
	 * @param seconde 0-59
	 * @return double julian date.
	 */
	public static double convertToJulianDay(
		int annee,
		int mois,
		int jour,
		int heure,
		int minute,
		int seconde) {
		// détection si la date est > 1582.1015
		double A = 0;
		double B = 0;
		double date = annee + mois / 100.0 + jour / 10000.0;
		double JulianDate = 0;

		double y = annee;
		double m = mois;
		double j =
			heure / 24.0
				+ ((minute / (24 * 60.0)))
				+ ((seconde / (3600.0 * 24.0)));
// MCR 15-2-2004 correction suite a un défaut détecté sur le mois de févier
// remplacement du < par <= puisque les mois de janvier et févier sont considéré comme
// les 13eme et 14eme mois.
 
		if (mois <= 2) {
			y = y - 1;
			m = m + 12;
		}

		if (date > 1582.1015) {
			A = ENT(y / 100.0);
			B = 2 - A + ENT(A / 4.0);
		} else {
			A = 0;
			B = 0;
		}

		if (y > 0) {
			JulianDate =
				ENT(365.25 * y)
					+ ENT(30.6001 * (m + 1))
					+ jour
					+ j
					+ 1720994.5
					+ B;
		} else {
			JulianDate =
				ENT(365.25 * y - 0.75)
					+ ENT(30.6001 * (m + 1))
					+ jour
					+ j
					+ 1720994.5
					+ B;
		}

		return (JulianDate);
	}
	
	/**
	 * 
	 * This method concert a date in Julian Date Modified with origin 1/1/2000 0H. 
	 * 
	 * The time scale used here is a "day number" from 2000 Jan 0.0 TDT, which is the same as 1999 Dec 31.0 TDT, i.e. precisely at midnight TDT at the start of the last day of this century. With the modest accuracy we strive for here, one can usually disregard the difference between TDT (formerly canned ET) and UT.
	 * 
	 * @param annee
	 * @param mois
	 * @param jour
	 * @param heure
	 * @param minute
	 * @param seconde
	 * @return
	 */
	public static double convertToJulianDay2000(int annee,
	int mois,
	int jour,
	int heure,
	int minute,
	int seconde)
	{
		return (convertToJulianDay(annee,mois,jour,heure,minute,seconde)- 2451543.5d);
	}

	/**
	 * Method convertToGregorianCalendar.
	 *
	 * This method convert an Julian date to a Gregorian Calendar date.
	 * 
	 * @param JulianDate
	 * @return GregorianCalendar
	 */
	public static GregorianCalendar convertToGregorianCalendar(double JulianDate) {
		double Z = ENT(JulianDate + 0.5);
		double A = 0;
		if (Z < 2299161) {
			A = Z;
		} else {
			double alpha = ENT((Z - 1867216.25) / 36524.35);
			A = Z + 1 + alpha - ENT(alpha / 4);
		}

		double F = JulianDate + 0.5 - Z;

		double B = A + 1524;
		double C = ENT((B - 122.1) / 365.25);
		double D = ENT(365.25 * C);
		double E = ENT((B - D) / 30.6001);
		double tempjour = (B - D - ENT(30.6001 * E) + F);

		int jour= (int) ENT(tempjour);
		double temp = tempjour - jour;

		double ensecondes = 24 * 60 * 60 * temp;

		int heure = (int) ENT(ensecondes / 3600);
		int minute = (int) ENT((ensecondes - (3600 * heure)) / 60.0);
		int secondes = (int) ensecondes - ((3600 * heure) + (60 * minute));
		int mois = (int) E - 1;

		if (E > 13.5) {
			mois = (int) E - 13;
		}

		int annee = (int) C - 4716;
		if (mois < 2.5) {
			annee = (int) C - 4715;
		}
		System.err.println(""+annee+"" +mois+""+ jour);
		return (
				
			new GregorianCalendar(annee, mois-1, jour, heure, minute, secondes));
	}

	/**
	 * Method ENT.
	 * 
	 * @param valeur
	 * @return double
	 */
	private static double ENT(double valeur) {
		long l = (long) valeur;
		return (l * 1.0);
	}

	/**
	 * Method convertToGenericDate.
	 * @param JulianDate
	 * @return GenericDate
	 */
	public static GenericDate convertToGenericDate(double JulianDate) {
		double Z = ENT(JulianDate + 0.5);
		double A = 0;
		if (Z < 2299161) {
			A = Z;
		} else {
			double alpha = ENT((Z - 1867216.25) / 36524.35);
			A = Z + 1 + alpha - ENT(alpha / 4);
		}

		double F = JulianDate + 0.5 - Z;

		double B = A + 1524;
		double C = ENT((B - 122.1) / 365.25);
		double D = ENT(365.25 * C);
		double E = ENT((B - D) / 30.6001);
		double tempjour = (B - D - ENT(30.6001 * E) + F);

		int jour  = (int) ENT(tempjour);
		double temp = tempjour - jour;

		double ensecondes = 24 * 60 * 60 * temp;

		int heure = (int) ENT(ensecondes / 3600);
		int minute = (int) ENT((ensecondes - (3600 * heure)) / 60.0);
		int secondes = (int) ensecondes - ((3600 * heure) + (60 * minute));
		int mois = (int) E - 1;

		if (E > 13.5) {
			mois = (int) E - 13;
		}

		int annee = (int) C - 4716;
		if (mois < 2.5) {
			annee = (int) C - 4715;
		}

		return (new GenericDate(annee, mois, jour, heure, minute, secondes));

	}
	 /**
     * DOCUMENT ME!
     *
     * @param m_Date julian day
     *
     * @return number of day from 1/1/1900 OH
     */
    public static double FromJan1_00h_1900(double m_Date) {
        return m_Date - EPOCH_JAN1_00H_1900;
    }

	
	 /**
     * DOCUMENT ME!
     *
     * @param m_Date julian day
     *
     * @return number of day from 1/1/1900 12H
     */
    public static double FromJan1_12h_1900(double m_Date) {
        return m_Date - EPOCH_JAN1_12H_1900;
    }

    /**
     * DOCUMENT ME!
     *
     * @param m_Date julian day
     *
     * @return number of day from 1/1/2000 12H
     */
    public static double FromJan1_12h_2000(double m_Date) {
        return m_Date - EPOCH_JAN1_12H_2000;
    }


    /** DOCUMENT ME! */
    private static final double EPOCH_JAN1_12H_2000 = 2451545.0; // Jan 1.5 2000 = Jan 1 2000 12h UTC

	public static double getFromJan1_12h_2000(double date) {
		 return date - EPOCH_JAN1_12H_2000;
	}

    //~ Constructors ------------------------------------------------------

    // static method in the class, the constructor is not needed
 
}
