//-----------------------------------------------------------------------------
// Copyright © 2004,2006 - 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 <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libh/time.h>
#include <libh/string.h>


//-----------------------------------------------------------------------------
// program	ctime
//
// purpose	Convert times from and to various formats.
//
// command	ctime [-option timevalue] [+format]
//
// options	-e	time is given in Earth day
//		-E	time is given in Earth time
//		-l	time is given in Lilian day
//		-x	time is given in eXchange day
//		-J	time is given in Julian Date
//		-s	time is given in system time_t
//		-m	time is given in system timems_t
//		-u	time is given in system timeus_t
//		-n	time is given in system timens_t
//		-j	time is given in Julian calendar + HMS
//		-g	time is given in Gregorian calendar + HMS
//
// format	The calendar input for Gregorian or Julian is:
//		    YYYY[AD|BC]/MM/DD_HH:MM:SS.fraction
//		where the separators can be any special character.
//-----------------------------------------------------------------------------
int
main (
    int		argc
    ,
    char * *	argv
)
{
    static const char	date_format	[] =
	"%g"
	"etime =\t\t%E\n"
	"eday =\t\t%e\n"
	"lday =\t\t%L\n"
	"xday =\t\t%X\n"
	"juliandate =\t%J\n"
	"gregorian =\t%Y/%m/%d (%A %1d %B %1Z)\n"
	"%j"
	"julian =\t%Y/%m/%d (%A %1d %B %1Z)\n"
	"%g"
	"dayofyear =\t%D\n"
	"dayofweek =\t%w (%A, %a)\n"
	"monthofyear = \t%1m (%B, %b)";

    static const char	time_format	[] =
	"%g"
	"etime =\t\t%E\n"
	"eday =\t\t%e\n"
	"lday =\t\t%L\n"
	"xday =\t\t%X\n"
	"juliandate =\t%J\n"
	"gregorian =\t%Y/%m/%d_%H:%M:%S.%U (%A %1d %B %1Z)\n"
	"%j"
	"julian =\t%Y/%m/%d_%H:%M:%S.%U (%A %1d %B %1Z)\n"
	"%g"
	"dayofyear =\t%D\n"
	"dayofweek =\t%w (%A, %a)\n"
	"monthofyear = \t%1m (%B, %b)";
		
    const char *	def_format	;
    const char *	format		;

    etime_t		etime		;
    eday_t		eday		;

    int			cal_type	;


    //--------------------------------------------------------------
    // Default is to display the current time in the default locale.
    //--------------------------------------------------------------
    setlocale( LC_TIME, "" );
    etime = current_etime();
    eday = etime_to_eday( etime );
    cal_type = ECAL_TYPE_GREGORIAN;
    def_format = time_format;
    format = NULL;

    //--------------------------------------------------------------
    // Check for input type option.  Convert input for simple types.
    //--------------------------------------------------------------
    while ( ++ argv, -- argc > 0 && * argv ) {
	if ( argv[0][0] == '-' ) {
	    int opt_ch = argv[0][1];
	    switch ( opt_ch ) {
	    case 'h':
		goto help;
	    case 'e':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		eday = str_expr_to_ul( * argv, NULL, 0 );
		etime = eday_to_etime( eday );
		def_format = date_format;
		break;
	    case 'E':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = str_expr_to_ull( * argv, NULL, 0 );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 'l':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = lday_to_etime( str_expr_to_ul( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = date_format;
		break;
	    case 'x':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = xday_to_etime( str_expr_to_ul( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = date_format;
		break;
	    case 'J':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = jd_to_etime( str_expr_to_d( * argv, NULL ) );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 's':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = time_to_etime( str_expr_to_ul( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 'm':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = timems_to_etime( str_expr_to_ull( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 'u':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = timeus_to_etime( str_expr_to_ull( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 'n':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = timens_to_etime( str_expr_to_ull( * argv, NULL, 0 ) );
		eday = etime_to_eday( etime );
		def_format = time_format;
		break;
	    case 'g':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = str_gregorian_to_etime( * argv );
		eday = etime_to_eday( etime );
		break;
	    case 'j':
		if ( ++ argv, -- argc <= 0 || ! * argv ) goto missing_value;
		etime = str_julian_to_etime( * argv );
		eday = etime_to_eday( etime );
		break;
	    default:
		goto unknown_option;
	    }
	}
	else if ( argv[0][0] == '+' ) {
	    format = 1 + * argv;
	}
	else if ( strcasecmp( argv[0], "now" ) != 0 ) {
	    goto bad_argument;
	}
    }

    //---------------------------------------------------
    // Convert to requested or default format and output.
    //---------------------------------------------------
    if ( ! format ) format = def_format;
    etime_to_printf( format, etime );
    putchar( '\n' );
    fflush( stdout );
    return 0;

    //-----------
    // Show help.
    //-----------

 bad_argument:
    fprintf( stderr, "\nUnrecognized argument \"%s\"\n", argv[0] );
    goto help;

 missing_value:
    fprintf( stderr, "\nMissing value for option \"%s\"\n", argv[-1] );
    goto help;

 unknown_option:
    fprintf( stderr, "\nUnknown option \"%s\"\n", argv[0] );
    goto help;

 help:
    fputs( "\n"
	   "ctime  [-option timevalue] [+format]\n"
	   "\n"
	   "options:\n"
	   "\t-e\ttime is given in Earth day\n"
	   "\t-E\ttime is given in Earth time\n"
	   "\t-l\ttime is given in Lilian day\n"
	   "\t-x\ttime is given in eXchange day\n"
	   "\t-J\ttime is given in Julian Date\n"
	   "\t-s\ttime is given in system time_t\n"
	   "\t-m\ttime is given in system timems_t\n"
	   "\t-u\ttime is given in system timeus_t\n"
	   "\t-n\ttime is given in system timens_t\n"
	   "\t-g\ttime is given in Gregorian calendar + HMS\n"
	   "\t-j\ttime is given in Julian calendar + HMS\n"
	   "\n"
	   "(default is Gregorian calendar)\n"
	   "\n"
	   "Calendar format is: yyyy.mm.dd.hh.mm.ss.fraction\n"
	   "where the dot can be any special character.\n"
	   "\n",
	   stderr );
    return 1;
}

