//
//  File:   hzDate.h
//
//  Legal Notice: This file is part of the HadronZoo C++ Class Library.
//
//  Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
//  Software Foundation, either version 3 of the License, or any later version.
//
//  The HadronZoo C++ Class Library 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 Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
//  Three HadronZoo data and time package provides a set of classes and methods that promote a 'rational and comprehensive' approach to dates and
//  times. Much of this package is thus asthetic.
//
//  Four classes are provided namely:-
//
//  1)  hzTime  - Based on the number of seconds since midnight (4 bytes)
//  2)  hzSDate - A short form of date based on the number of days since Jan 1st, 0000 (4 bytes)
//  3)  hzXDate - A full date and time accurate to microseconds (8 bytes)
//
#ifndef hzDate_h
#define hzDate_h
#include "hzString.h"
#define DAYS_IN_10K      3652425        //  days in 10000 years
#define DAYS_IN_2K        730485        //  days in 2000 years
#define DAYS_IN_4C        146097        //  days in 400 years
#define DAYS_IN_1C         36524        //  days in 100 years (in which the 00 year is not a leap)
#define DAYS_IN_LC         36525        //  days in 100 years (in which the 00 year is a leap)
#define DAYS_IN_4Y          1461        //  days in 4 years
#define DAYS_IN_1Y           365        //  days in 1 year
#define DAYS_IN_LY           366        //  days in 1 leap year
#define DAYS_B4_EPOCH     719528        //  days between 00000101 and 19700101
#define DAYS_EPOCH_END    744383        //  days at end of epoch (2030)
#define HOURS_B4_EPOCH  17268672        //  hours between 00000101 and 19700101
#define HOURS_EPOCH_END 17865192        //  hours at end of epoch (rounded to nearest whole day)
#define SECS_IN_DAY        86400        //  seconds in one day
#define SECS_IN_HOUR        3600        //  seconds in one day
#define DAYS_EXCEL        693959        //  Number of days with Dec 31st 1899 being day 1 (Microsoft Excel dates)
#define NULL_DOW    0xFF
#define NULL_TIME   0x80000000
#define NULL_DATE   0x80000000
enum    hzInterval
{
    //  Category:   Scheduling
    //
    //  Represents a standard interval
    DAY,            //  One day
    MONTH,          //  One calender month
    YEAR,           //  One calender year
    HOUR,           //  One hour
    MINUTE,         //  One minute
    SECOND          //  One second
} ;
enum    hzDateFmt
{
    //  Category:   Data Processing
    //
    //  Enumerated HadronZoo date presentation formats.
    FMT_DT_UNKNOWN  = 0x0000,       //  Invalid or uninitialized format
    //  Dates contrl flags
    FMT_DATE_DOW    = 0x0001,       //  Print the dow (this will appear first)
    FMT_DATE_USA    = 0x0002,       //  Where applicable, put day before month
    FMT_DATE_ABBR   = 0x0004,       //  Write words (eg dow and monthname) out in short form
    FMT_DATE_FULL   = 0x0008,       //  Write words (eg dow and monthname) out in full
    //  Date only formats
    FMT_DATE_DFLT   = 0x0010,       //  Default format YYYYMMDD
    FMT_DATE_STD    = 0x0020,       //  Standard format YYYY/MM/DD
    FMT_DATE_NORM   = 0x0040,       //  Normal format DD/MM/YYYY (UK) or MM/DD/YYYY (US)
    FMT_DATE_FORM   = 0x0080,       //  Day_of_month+monthname+YYYY (UK) or monthname+day_of_month+YYYY (US)
    //  Time only formats
    FMT_TIME_DFLT   = 0x0100,       //  Time HHMMSS
    FMT_TIME_STD    = 0x0200,       //  Time HH:MM:SS
    FMT_TIME_USEC   = 0x0400,       //  Time HH:MM:SS.uSec
    //  Timezones (always last)
    FMT_TZ_CODE     = 0x1000,       //  Timezone as code
    FMT_TZ_NUM      = 0x2000,       //  Timezone as number of hours +/- GMT
    FMT_TZ_BOTH     = 0x3000,       //  Timezone as digits plus (code in braces)
    //  Combined Date & Time formats
    FMT_DT_DFLT = FMT_DATE_DFLT + FMT_TIME_DFLT,    //  YYYYMMDD HHMMSS
    FMT_DT_STD  = FMT_DATE_STD  + FMT_TIME_STD,     //  YYYY/MM/DD HH:MM:SS
    FMT_DT_NORM = FMT_DATE_NORM + FMT_TIME_STD,     //  DD/MM/YYYY (UK) or MM/DD/YYYY (US) followed by HH:MM:SS
    FMT_DT_USEC = FMT_DATE_STD  + FMT_TIME_USEC,    //  YYYYMMDD HH:MM:SS.uSec
    FMT_DT_INET = FMT_DATE_DOW  + FMT_DATE_FULL + FMT_TIME_STD + FMT_TZ_NUM,
                                    //  Day of Week + Day_of_month+monthname+YYYY or monthname+day_of_month+YYYY + HH:MM:SS + timezone
} ;
#define FMT_DT_CONTROL      0x000F  //  Checks if there are any control settings (dow, long names, american)
#define FMT_DT_MASK_DATES   0x00F0  //  Checks if there are any control settings (dow, long names, american)
#define FMT_DT_MASK_TIMES   0x0F00  //  Checks if there are any control settings (dow, long names, american)
#define FMT_DT_MASK_TZONE   0xF000  //  Checks if there are any timezone settings
/*
**  Date calculation macros
*/
#define isleap(y)   (!(y%400)?1:!(y%100)?0:!(y%4)?1:0)
#define monlen(y,m) (m==2?isleap(y)?29:28:m==4||m==6||m==9||m==11?30:31)
/*
**  Variables for formal date presentation
*/
extern  const char* hz_daynames_abrv    [] ;    //  Day names (short)
extern  const char* hz_daynames_full    [] ;    //  Day names (full)
extern  const char* hz_monthnames_abrv  [] ;    //  Month names (short)
extern  const char* hz_monthnames_full  [] ;    //  Month names (full)
/*
**  misc non-member functions
*/
hzEcode _daysfromdate   (uint32_t& nDays, uint32_t Y, uint32_t M, uint32_t D) ;
void    _datefromdays   (uint32_t& Y, uint32_t& M, uint32_t& D, uint32_t nDays) ;
/*
**  TIME    (HHMMSS)
*/
class   hzTime
{
    //  Category:   Data
    //
    //  hzTime is a data type to represent the time of day. It has allowed range of 00:00:00 (midnight) thru to 23:59:59. The minimum
    //  unit is the second.
private:
    uint32_t    m_secs ;    //  No of seconds since midnight
public:
    hzTime  (void)  { m_secs = NULL_TIME ; }
    ~hzTime (void)  {}
    void        SysTime (void) ;
    void        SetTime (const hzTime& op) ;
    hzEcode     SetTime (uint32_t h, uint32_t m, uint32_t s) ;
    hzEcode     SetTime (uint32_t nSecs) ;
    hzEcode     SetTime (const char* pTimeStr) ;
    void        Clear   (void)  { m_secs = NULL_TIME ; }
    bool        IsNull  (void) const { return m_secs == NULL_TIME ? true : false ; }
    bool        IsSet   (void) const { return m_secs == NULL_TIME ? false : true ; }
    uint32_t    NoSecs  (void) const { return m_secs ; }
    uint32_t    Hour    (void) const { return m_secs / 3600 ; }
    uint32_t    Min     (void) const { return (m_secs / 60) % 60 ; }
    uint32_t    Sec     (void) const { return m_secs % 60 ; }
    const char* Txt     (hzDateFmt eEType = FMT_TIME_DFLT) const ;
    //  Eval operators
    const char* operator*   (void) const    { return Txt() ; }
    //  Casting operators
    operator const char*    (void) const    { return Txt() ; }
    //  Assignment Operators
    const hzTime&   operator=   (const hzTime& op)      { m_secs = op.m_secs ; return *this ; }
    const hzTime&   operator=   (uint32_t nSecs)        { SetTime(nSecs) ; return *this ; }
    const hzTime&   operator=   (const char* cpTime)    { SetTime(cpTime) ; return *this ; }
    const hzTime&   operator=   (const hzString& time)  { SetTime(*time) ; return *this ; }
    //  Arithmetic Operators
    bool    operator+=  (uint32_t nSecs) { m_secs += nSecs ; return true ; }
    bool    operator-=  (uint32_t nSecs) { m_secs -= nSecs ; return true ; }
    //  Test Operators
    bool    operator==  (const hzTime& op) const    { return (m_secs==op.m_secs) ? true : false ; }
    bool    operator!=  (const hzTime& op) const    { return (m_secs!=op.m_secs) ? true : false ; }
    bool    operator<   (const hzTime& op) const    { return (m_secs< op.m_secs) ? true : false ; }
    bool    operator<=  (const hzTime& op) const    { return (m_secs<=op.m_secs) ? true : false ; }
    bool    operator>   (const hzTime& op) const    { return (m_secs> op.m_secs) ? true : false ; }
    bool    operator>=  (const hzTime& op) const    { return (m_secs>=op.m_secs) ? true : false ; }
    bool    operator!   (void) const    { return m_secs == NULL_TIME ? true : false ; }
    //  Stream operator
    friend  std::ostream&   operator<<  (std::ostream& os, const hzTime& Time) ;
} ;
/*
**  Short date (YYYYMMDD)
*/
class   hzXDate ;
class   hzSDate
{
    //  Category:   Data
    //
    //  hzSDate is a data type capable of representing a date. It comprises a single 32 bit variable whose value is the number of days
    //  since 'the Birth of Christ' or Jan 1st, year 0.
    uint32_t    m_days ;    //  No of days since Jan 1, 0000
public:
    hzSDate     (void)  { m_days = NULL_DATE ; }
    ~hzSDate    (void)  {}
    //  Set functions
    void    SysDate     (void) ;
    hzEcode SetDate     (const hzSDate& op) ;
    hzEcode SetDate     (uint32_t Y, uint32_t M, uint32_t D) ;
    hzEcode SetDate     (uint32_t nDays) ;
    hzEcode SetDate     (const char* pDateStr) ;
    void    SetByEpoch  (uint32_t nEpochTime) ;
    //  Modify functions
    hzEcode AltYear     (int32_t x) ;
    hzEcode AltMonth    (int32_t x) ;
    hzEcode AltDay      (int32_t x) ;
    //  Get functions
    uint32_t    Year    (void) const ;
    uint32_t    Month   (void) const ;
    uint32_t    Day     (void) const ;
    uint32_t    Dow     (void) const    { return (m_days + 5) % 7 ; }
    
    void        Clear   (void)          { m_days = NULL_DATE ; }
    uint32_t    NoDays  (void) const    { return m_days ; }
    uint32_t    Excel   (void) const    { return m_days > DAYS_EXCEL ? m_days - DAYS_EXCEL : 0 ; }
    bool        IsNull  (void) const    { return m_days == NULL_DATE ? true : false ; }
    bool        IsSet   (void) const    { return m_days == NULL_DATE ? false : true ; }
    const char* Txt     (hzDateFmt eEType = FMT_DATE_DFLT) const ;
    //  Eval operators
    const char* operator*   (void) const    { return Txt() ; }
    //  Casting operators
    operator const char*    (void) const    { return Txt() ; }
    //  Assignment Operators
    const hzSDate&  operator=   (const hzXDate& op) ;
    const hzSDate&  operator=   (const hzSDate& op)     { m_days = op.m_days ; return *this ; }
    const hzSDate&  operator=   (uint32_t nDays)        { SetDate(nDays) ; return *this ; }
    const hzSDate&  operator=   (const char* cpDate)    { SetDate(cpDate) ; return *this ; }
    const hzSDate&  operator=   (const hzString& S)     { SetDate(*S) ; return *this ; }
    //  Arithmetic Operators
    const hzSDate&  operator+=  (uint32_t nDays)
    {
        //  Add to this date, the supplied number of days
        m_days += nDays ;
        m_days = m_days<0 ? 0 : m_days>DAYS_IN_10K ? DAYS_IN_10K : m_days ;
        return *this ;
    }
 
    const hzSDate&  operator-=  (uint32_t nDays)
    {
        //  Subtract from this date, the supplied number of days
        m_days -= nDays ;
        m_days = m_days<0 ? 0 : m_days>DAYS_IN_10K ? DAYS_IN_10K : m_days ;
        return *this ;
    }
    //  Test Operators
    bool    operator== (const hzXDate& op) const ;
    bool    operator== (const hzSDate& op) const    { return (m_days==op.m_days) ? true : false ; }
    bool    operator!= (const hzSDate& op) const    { return (m_days!=op.m_days) ? true : false ; }
    bool    operator<  (const hzSDate& op) const    { return (m_days< op.m_days) ? true : false ; }
    bool    operator<= (const hzSDate& op) const    { return (m_days<=op.m_days) ? true : false ; }
    bool    operator>  (const hzSDate& op) const    { return (m_days> op.m_days) ? true : false ; }
    bool    operator>= (const hzSDate& op) const    { return (m_days>=op.m_days) ? true : false ; }
    bool    operator!   (void) const    { return m_days == NULL_TIME ? true : false ; }
    //  Stream operator
    friend  std::ostream&   operator<<  (std::ostream& os, const hzSDate& d) ;
} ;
/*
**  Full date and time
*/
class   hzXDate
{
    //  Category:   Data
    //
    //  A full date and time accurate to microseconds (8 bytes)
private:
    uint32_t    m_hour ;        //  no of hours since midnight 0000/01/01
    uint32_t    m_usec ;        //  no of microseconds since start of hour
    void    altsec  (int32_t nounits) ;
    void    altmin  (int32_t nounits) ;
    void    althour (int32_t nounits) ;
    void    altday  (int32_t nounits) ;
    void    altmon  (int32_t nounits) ;
    void    altyear (int32_t nounits) ;
public:
    hzXDate     (void)  { m_hour = 0 ; m_usec = 0 ; }
    ~hzXDate    (void)  {}
    bool        IsNull  (void) const    { return !m_hour && !m_usec ? true : false ; }
    bool        IsSet   (void) const    { return m_hour || m_usec ? true : false ; }
    uint32_t    Year    (void) const ;
    uint32_t    Month   (void) const ;
    uint32_t    Day     (void) const ;
    uint64_t    AsVal   (void) const ;
    uint32_t    Hour    (void) const    { return m_hour%24 ; }
    uint32_t    Min     (void) const    { return m_usec/60000000 ; }
    uint32_t    Sec     (void) const    { return (m_usec/1000000)%60 ; }
    uint32_t    uSec    (void) const    { return m_usec%1000000 ; }
    uint32_t    Dow     (void) const    { return ((m_hour/24)+5)%7 ; }
    void    Clear   (void)  { m_hour = 0 ; m_usec = 0 ; }
    void    SysDateTime (void) ;
    hzEcode SetDate     (const hzXDate& op) { m_hour = op.m_hour ; m_usec = op.m_usec ; return E_OK ; }
    hzEcode SetDate     (hzSDate& D)        { m_hour %= 24 ; m_hour += (D.NoDays() * 24) ; return E_OK ; }
    hzEcode SetDate     (uint32_t Y, uint32_t M, uint32_t D) ;
    hzEcode SetDate     (uint32_t nDays) ;
    hzEcode SetDate     (uint32_t nDays, uint32_t nSecs) ;
    hzEcode SetDate     (uint64_t xdVal) ;
    hzEcode SetDate     (const char* pDateStr) ;
    hzEcode SetTime     (hzTime& T) { m_hour -= m_hour%24 ; m_hour += T.Hour() ; m_usec = (T.NoSecs()/24) * 1000000 ; return E_OK ; }
    hzEcode SetTime     (const char* cpTimeStr) ;
    hzEcode SetTime     (uint32_t h, uint32_t m, uint32_t s) ;
    hzEcode SetTime     (uint32_t nSecs) ;
    hzEcode SetDateTime (const char* pDateStr) ;
    void    SetByEpoch  (uint32_t nEpochTime) ;
    void    SetByEpoch  (uint32_t nEpochTime, uint32_t usec) ;
    void    altdate     (hzInterval unit, int32_t nounits) ;
    uint32_t    DaysInYear  (void) const ;
    uint32_t    NoDays      (void) const    { return m_hour/24 ; }
    uint32_t    NoSecs      (void) const    { return ((m_hour%24) * 3600) + (m_usec/1000000) ; }
    uint32_t    Excel       (void) const    { return (m_hour/24) > DAYS_EXCEL ? (m_hour/24) - DAYS_EXCEL : 0 ; }
    hzSDate     Date    (void) const ;      //  Return the date part only
    hzTime      Time    (void) const ;      //  Return the time part only
    uint32_t    AsEpoch (void) const ;      //  Return the time & date as an epoch
    const char* Txt     (hzDateFmt eEType = FMT_DT_DFLT) const ;
    //  Eval operators
    const char* operator*   (void) const    { return Txt() ; }
    //  Casting operators
    operator const char*    (void) const    { return Txt() ; }
    //  Assignment Operators
    const hzXDate&  operator=   (const hzXDate& op) { m_hour = op.m_hour ; m_usec = op.m_usec ; return *this ; }
    const hzXDate&  operator=   (const hzSDate& op) { m_hour = op.Day() * 24 ; m_usec = 0 ; return *this ; }
    const hzXDate&  operator=   (uint64_t xdVal)    { SetDate(xdVal) ; return *this ; }
    const hzXDate&  operator=   (const hzString& S) { SetDate(*S) ; return *this ; }
    const hzXDate&  operator=   (const char* s)     { SetDate(s) ; return *this ; }
    //  Arithmentic Operators
    const hzXDate&  operator+=  (uint32_t nDays)
    {
        //  Add to this date the supplied number of days
        m_hour += (nDays * 24) ;
        m_hour = m_hour < 0 ? 0 : m_hour > (DAYS_IN_10K * 24) ? (DAYS_IN_10K * 24) : m_hour ;
        return *this ;
    }
 
    const hzXDate&  operator-=  (uint32_t nDays)
    {
        //  Subtract from this date the supplied number of days
        m_hour -= (nDays * 24) ;
        m_hour = m_hour < 0 ? 0 : m_hour > (DAYS_IN_10K * 24) ? (DAYS_IN_10K * 24) : m_hour ;
        return *this ;
    }
    //  Test Operators
    bool    operator==  (const hzXDate& op) const ;
    bool    operator!=  (const hzXDate& op) const ;
    bool    operator<   (const hzXDate& op) const ;
    bool    operator<=  (const hzXDate& op) const ;
    bool    operator>   (const hzXDate& op) const ;
    bool    operator>=  (const hzXDate& op) const ;
    bool    operator!   (void) const    { return !m_hour && !m_usec ? true : false ; }
    //  Static functions
    static  int32_t datecmp (hzXDate& a, hzXDate& b) ;
    //  Stream operator
    friend  std::ostream&   operator<<  (std::ostream& os, const hzXDate& Date) ;
} ;
//  Externals
extern  const hzSDate   _hz_null_hzSDate ;      //  Null short date
extern  const hzXDate   _hz_null_hzXDate ;      //  Null full date
extern  const hzTime    _hz_null_hzTime ;       //  Null time
/*
**  Prototypes
*/
hzDateFmt   Str2DateFmt     (const hzString& dtFmt) ;
const char* DateFmt2Txt     (hzDateFmt dtFmt) ;
//  Return real time in microseconds/nanoseconds
uint64_t    RealtimeMicro   (void) ;
uint64_t    RealtimeNano    (void) ;
#endif  //  hzDate_h