#ifndef INCLUDED_BOBCAT_DATETIME_
#define INCLUDED_BOBCAT_DATETIME_

#include <ctime>
#include <iosfwd>

namespace FBB
{

class DateTime
{
    typedef struct tm TimeStruct;

    friend  std::ostream &operator<<(std::ostream &str, 
                                          DateTime const &dt);
    friend  std::istream &operator>>(std::istream &str, DateTime &dt);

    friend bool operator==(DateTime const &left, DateTime const &right);
    friend bool operator!=(DateTime const &left, DateTime const &right);
    friend bool operator<(DateTime const &left, DateTime const &right);
    friend bool operator<=(DateTime const &left, DateTime const &right);
    friend bool operator>(DateTime const &left, DateTime const &right);
    friend bool operator>=(DateTime const &left, DateTime const &right);

    public:
        enum TimeType
        {
            LOCALTIME,
            UTC
        };
        enum Month
        {
            JANUARY,
            FEBRUARY,
            MARCH,
            APRIL,
            MAY,
            JUNE,
            JULY,
            AUGUST,
            SEPTEMBER,
            OCTOBER,
            NOVEMBER,
            DECEMBER,
        };
        enum Relative
        {
            THIS_YEAR,
            LAST,
            NEXT,
            THIS_WEEK,
        };
        enum Weekday
        {
            SUNDAY,
            MONDAY,
            TUESDAY,
            WEDNESDAY,
            THURSDAY,
            FRIDAY,
            SATURDAY,
        };
        enum TriVal
        {
            UNKNOWN = -1,
            NO,
            YES
        };
        enum TimeFields
        {
            SECONDS  = 1 << 0,
            MINUTES  = 1 << 1,
            HOURS    = 1 << 2,
            MONTHDAY = 1 << 3,
            MONTH    = 1 << 4,
            YEAR     = 1 << 5
        };

    private:
    
        TimeType    d_type; // current type of info in d_tm member
                            // (LOCALTIME (implied when using displayZone) 
                            // or UTC)
    
        time_t      d_utcSec;           // time in seconds (UTC) 
        time_t      d_displayZoneShift; // correction to UTC when displaying
                                        // times
                                        // this takes care of dst as well.

        int         d_dstShift;         // 3600 or 0: add to displayZoneShift



        TimeStruct  d_tm;               // time's elements 


        bool        d_ok;
        size_t      d_errno;

        static char const *s_month[];
        static char const *s_day[];

    public:
        explicit DateTime(TimeType type = UTC); // 1. time displayed as 
                                                //    TimeType

        // shifts in minutes

        explicit DateTime(int tzShift);         // 2. LOCALTIME: 
                                                //    UTC + 
                                                //      tzShift (= TZ + DST)

                                                // 3. specify UTC/LOCAL time in 
        DateTime(time_t time, TimeType type);   //    seconds

                                                // 4. LOCALTIME: time (UTC) +
        DateTime(time_t time, int tzShift);     //      tzShift (= TZ + DST)


        // with TimeStruct tm constructor arguments dst, day of the year,
        //  day of the week are ignored:

                                                // 5. specify tm fields as
                                                //    either UTC or LOCALTIME
                                                //    using the default 
                                                //    tzShift
        DateTime(TimeStruct const &tm, TimeType type = UTC);

                                                // 6. specify UTC tm fields
                                                // display + tzShift
        DateTime(TimeStruct const &tm, int tzShift);

                                                // 7. specify UTC/LOCAL text 
                                                //    time
        explicit DateTime(std::string const &timeStr, TimeType type = UTC);

                                                // 7dep. Deprecated. Avoid
        DateTime(std::string const &timeStr, TimeType type, int);

                                                // 8. specify UTC text time
                                                //    display + 
                                                //      displayZoneShift
        DateTime(std::string const &timeStr, int tzShift);

                                                // 8dep.: Deprecated: avoid
        DateTime(std::string const &timeStr, int displayZoneShift, int);

        DateTime &operator+=(time_t seconds);           // 1.
        DateTime &operator+=(TimeStruct const &tm);     // 2. 

        DateTime &operator-=(time_t seconds);           // opsubis1.f
        DateTime &operator-=(TimeStruct const &tm);

        operator bool() const;                          // opbool.f

        bool setDay(int day);           // 'int' values may be negative or 
        bool setHours(int hours);       // postive.
        bool setMinutes(int minutes);   
        bool setMonth(Month month, Relative where = THIS_YEAR);
        bool setMonth(int month);       // set month, using 0: january this yr
                                        // correct for overflows.
        bool setSeconds(int seconds);
        bool setTime(time_t time);      // utc time
        bool setWeekday(Weekday weekday, Relative where = NEXT);
        bool setYear(size_t year);
        bool setFields(TimeStruct const &tmStruct, int fields);
        void setValid();                                            // .f

        int displayZoneShift() const;                               // .f
        TriVal dst() const;                                         // .f
        size_t error() const;                                       // .f
        size_t hours() const;                                       // .f
        size_t minutes() const;                                     // .f
        Month month() const;                                        // .f
        size_t monthDayNr() const;                                  // .f
        std::string rfc2822() const;
        std::string rfc3339() const;
        size_t seconds() const;                                     // .f
        time_t time() const;                                        // .f
        TimeStruct const *timeStruct() const;                       // .f
        bool valid() const;                                         // .f
        Weekday weekday() const;                                    // .f
        size_t year() const;                                        // .f
        size_t yearDay() const;             // starts counting at 0    .f
        size_t yearDayNr() const;           // starts counting at 1    .f
        size_t weekNr() const;              // starts counting at 1

        
        DateTime utc() const;                                       // .f
        DateTime localTime() const;                                 // .f
        DateTime to(TimeType type) const;

        DateTime timeZoneShift(int displayZoneShift) const;         // .f

    private:
            // used by constructors
        void d_tm2d_tm(int displayZoneShift);   // cons 6, 8
        void d_tm2timeType();                   // cons 5, 7

        void d_tm2utcSec();
        void displayShift2d_tm();

        bool updateTime(TimeStruct &tm);

        void setDisplayZone(time_t displayZoneShift);
        void parse(std::istream &in);
        void parseFromDayName(std::istream &in);
        std::ostream &timeStr(std::ostream &out) const;

        time_t timeStruct2utcSec(TimeStruct *ts);           // sets d_ok
        void utcSec2timeStruct(TimeStruct *ts,              // sets d_ok    .f
                                            time_t time);    
        
        int dstCorrection(bool *ok) const;
        int dstCorrection();

        time_t defaultDisplayZoneShift() const;     // seconds

        static int zoneShiftSeconds(int shiftMinutes);              // .f
                                                // shifts multiples of 30'
                                                // at most +/-12 hours away
};

#include "displayzoneshift.f"
#include "dst.f"
#include "error.f"
#include "hours.f"
#include "localtime.f"
#include "minutes.f"
#include "month.f"
#include "monthdaynr.f"
#include "opbool.f"
#include "opsubis1.f"
#include "seconds.f"
#include "setvalid.f"
#include "time.f"
#include "timestruct.f"
#include "timezoneshift.f"
#include "utc.f"
#include "valid.f"
#include "weekday.f"
#include "year.f"
#include "yearday.f"
#include "yeardaynr.f"

// Free functions

#include "opadd1.f"     // DateTime + time_t
#include "opadd2.f"     // DateTime + tm
#include "opeq.f"       // DateTime == DateTime
#include "opgeq.f"      // DateTime >= DateTime
#include "opgreater.f"  // DateTime > DateTime
#include "opleq.f"      // DateTime <= DateTime
#include "opless.f"     // DateTime < DateTime
#include "opneq.f"      // DateTime != DateTime
#include "opsub1.f"     // DateTime - time_t
#include "opsub2.f"     // DateTime - tm

}   // FBB

#endif
