/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2022  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. 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 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_UTC_TIME_H
#define _GCTL_UTC_TIME_H

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <sys/time.h>
#include <unistd.h>

namespace gctl
{
    enum time_unit
    {
        Millisecond,
        Second,
        Mintue,
        Hour,
        Day,
        Month,
        Year,
    };

    // return a string of current time stamp.
    std::string time_stamp();

    // Function returns true if the given year is a leap year and returns false otherwise.
    bool is_leap_year(int year);

    // Compute the month and day corresponding to the Julian day within the given year.
    void month_and_day(int year, int julian_day, int &month, int &day);

    // Compute the Julian day corresponding to the month and day within the given year.
    void julian_day(int year, int month, int day, int &julian_day);

    /**
     * @brief Convert a decimal year to year, month and day integrals
     * 
     * @param deci_year decimal year
     * @param year returned year number
     * @param month returned month number
     * @param day retured day number
     */
    void decimal_year2date(double deci_year, int &year, int &month, int &day);

    // This struct holds variables and functions for time calculation.
    struct UTC_TIME
    {
        int year, month, day, julday;
        int hour, mint, sec, msec;

        UTC_TIME();
        UTC_TIME(int y, int m, int d, int h, int mt, int s, int ms);
        UTC_TIME(int y, int jd, int h, int mt, int s, int ms);
        UTC_TIME(std::string t_str); // <year>-<month>-<day>T<hour>:<minute>:<sec>.<msec>
        void set_time(int y, int m, int d, int h, int mt, int s, int ms);
        void set_time(int y, int jd, int h, int mt, int s, int ms);
        void set_time(std::string t_str); // <year>-<month>-<day>T<hour>:<minute>:<sec>.<msec>
        void set_rdseed_time(std::string t_str); // <year>,<jday>,<hour>:<minute>:<sec>.<msec>
        void set_to_now(); // set to current UTC time
        void add_duration(double s);
        void round_up_to(time_unit tu);
        double diff_sec(const UTC_TIME &t) const;
        std::string time_str(bool symbolic = false) const;
        std::string rdseed_time_str() const;
    };

    static UTC_TIME UTC_START(1900, 1, 1, 0, 0, 0, 0);

    bool operator==(const UTC_TIME &ta, const UTC_TIME &tb);
    bool operator!=(const UTC_TIME &ta, const UTC_TIME &tb);
    bool operator<(const UTC_TIME &ta, const UTC_TIME &tb);
    bool operator<=(const UTC_TIME &ta, const UTC_TIME &tb);
};

#endif // _GCTL_UTC_TIME_H