//-----------------------------------------------------------------------------
// Copyright © 2004 - Philip Howard - All rights reserved
//
// 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.
//-----------------------------------------------------------------------------
// package	libh/time
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------
// reference	Explanatory Supplement to the Astronomical Almanac,
//		P. Kenneth Seidelmann, editor  [ISBN 0-935702-68-7]
//		http://shop.bn.com/bookSearch/isbnInquiry.asp?isbn=0935702687
//-----------------------------------------------------------------------------

#include <ctype.h>

#include "time_lib.h"


__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	ymd_gregorian_to_eday
//
// purpose	Convert a Gregorian calendar date to an Earth Day value.
//
// arguments	1 (long) Gregorian calendar year (0 = 1 BC, 1 = 1 AD)
//		2 (int) Gregorian calendar month (1-12)
//		3 (int) Gregorian calendar date (1-31)
//
// returns	(eday_t) resultant Earth Day value
//
// note		The calculation is for the proleptic Gregorian calendar.
//		There is no accomodation for selection between Gregorian
//		and Julian calendars.  Since different areas of the world
//		switched calendars at different dates, the calling program
//		will have to determine on its own what is the appropriate
//		calendar context and call ymd_gregorian_to_eday() or ymd_julian_to_eday().
//-----------------------------------------------------------------------------
eday_t
ymd_gregorian_to_eday (
    long	arg_year
    ,
    int		arg_month
    ,
    int		arg_date
    )
__PROTO_END__
{
    //--------------------------------------------------------------
    // Define a lookup table indexed by the year in a 400 year cycle
    // yielding the number of days in the cycle to that year.
    //--------------------------------------------------------------
    static int year_to_day[400] = {
	     0,   366,   731,  1096,  1461,  1827,  2192,  2557,  2922,  3288,
	  3653,  4018,  4383,  4749,  5114,  5479,  5844,  6210,  6575,  6940,
	  7305,  7671,  8036,  8401,  8766,  9132,  9497,  9862, 10227, 10593,
	 10958, 11323, 11688, 12054, 12419, 12784, 13149, 13515, 13880, 14245,
	 14610, 14976, 15341, 15706, 16071, 16437, 16802, 17167, 17532, 17898,
	 18263, 18628, 18993, 19359, 19724, 20089, 20454, 20820, 21185, 21550,
	 21915, 22281, 22646, 23011, 23376, 23742, 24107, 24472, 24837, 25203,
	 25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855,
	 29220, 29586, 29951, 30316, 30681, 31047, 31412, 31777, 32142, 32508,
	 32873, 33238, 33603, 33969, 34334, 34699, 35064, 35430, 35795, 36160,
	 36525, 36890, 37255, 37620, 37985, 38351, 38716, 39081, 39446, 39812,
	 40177, 40542, 40907, 41273, 41638, 42003, 42368, 42734, 43099, 43464,
	 43829, 44195, 44560, 44925, 45290, 45656, 46021, 46386, 46751, 47117,
	 47482, 47847, 48212, 48578, 48943, 49308, 49673, 50039, 50404, 50769,
	 51134, 51500, 51865, 52230, 52595, 52961, 53326, 53691, 54056, 54422,
	 54787, 55152, 55517, 55883, 56248, 56613, 56978, 57344, 57709, 58074,
	 58439, 58805, 59170, 59535, 59900, 60266, 60631, 60996, 61361, 61727,
	 62092, 62457, 62822, 63188, 63553, 63918, 64283, 64649, 65014, 65379,
	 65744, 66110, 66475, 66840, 67205, 67571, 67936, 68301, 68666, 69032,
	 69397, 69762, 70127, 70493, 70858, 71223, 71588, 71954, 72319, 72684,
	 73049, 73414, 73779, 74144, 74509, 74875, 75240, 75605, 75970, 76336,
	 76701, 77066, 77431, 77797, 78162, 78527, 78892, 79258, 79623, 79988,
	 80353, 80719, 81084, 81449, 81814, 82180, 82545, 82910, 83275, 83641,
	 84006, 84371, 84736, 85102, 85467, 85832, 86197, 86563, 86928, 87293,
	 87658, 88024, 88389, 88754, 89119, 89485, 89850, 90215, 90580, 90946,
	 91311, 91676, 92041, 92407, 92772, 93137, 93502, 93868, 94233, 94598,
	 94963, 95329, 95694, 96059, 96424, 96790, 97155, 97520, 97885, 98251,
	 98616, 98981, 99346, 99712,100077,100442,100807,101173,101538,101903,
	102268,102634,102999,103364,103729,104095,104460,104825,105190,105556,
	105921,106286,106651,107017,107382,107747,108112,108478,108843,109208,
	109573,109938,110303,110668,111033,111399,111764,112129,112494,112860,
	113225,113590,113955,114321,114686,115051,115416,115782,116147,116512,
	116877,117243,117608,117973,118338,118704,119069,119434,119799,120165,
	120530,120895,121260,121626,121991,122356,122721,123087,123452,123817,
	124182,124548,124913,125278,125643,126009,126374,126739,127104,127470,
	127835,128200,128565,128931,129296,129661,130026,130392,130757,131122,
	131487,131853,132218,132583,132948,133314,133679,134044,134409,134775,
	135140,135505,135870,136236,136601,136966,137331,137697,138062,138427,
	138792,139158,139523,139888,140253,140619,140984,141349,141714,142080,
	142445,142810,143175,143541,143906,144271,144636,145002,145367,145732
    };

    //----------------------------------------------------------
    // Define a lookup table indexed by the year offset in a 400
    // year cycle yielding an indicator if this is a leap year.
    //----------------------------------------------------------
    static char year_is_leapyear[400] = {
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
	1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0
    };

    //-----------------------------------------------------
    // Define a lookup table indexed by the month offset in
    // a year yielding the number of days to that month.
    //-----------------------------------------------------
    static int month_to_day[12] = {
	0,31,59,90,120,151,181,212,243,273,304,334
    };

    //-----------------------------------------------------
    // Define a lookup table indexed by the month offset in
    // a year yielding the number of days in that month.
    //-----------------------------------------------------
    static int days_in_month[12] = {
	31,28,31,30,31,30,31,31,30,31,30,31
    };

    //------------------------
    // Define other variables.
    //------------------------
    long		cycle		;
    long		days		;
    int			year		;
    int			month		;
    int			leapyear	;


    //-----------------------------------------------------------
    // Split year into cycle number and year offset within cycle.
    //-----------------------------------------------------------
    cycle = quo_ni( arg_year, 400 );
    year = arg_year - cycle * 400;
    leapyear = year_is_leapyear[ year ];

    //------------------------------
    // Get month into range 0 to 11.
    //------------------------------
    month = arg_month - 1;
    if ( arg_month < 1 ) month = 0;
    else if ( arg_month > 12 ) month = 11;

    //------------------------------------
    // Get days into range for this month.
    //------------------------------------
    days = days_in_month[ month ] + ( leapyear && month == 1 );
    if ( arg_date < 1 ) days = 1;
    else if ( arg_date < days ) days = arg_date;
    -- days;

    //--------------------------------
    // Add days in year to this month.
    //--------------------------------
    days += month_to_day[ month ];
    if ( leapyear && month > 1 ) ++ days;

    //--------------------------------------------------------
    // Add days in cycle to this year, days to cycle from year
    // zero, adjust to epoch, and return this number of days.
    //--------------------------------------------------------
    return days + year_to_day[ year ] + cycle * 146097 + EDAY_1_JAN_0;
}

