#ifndef MARS_MTime
#define MARS_MTime

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// MTime                                                                   //
//                                                                         //
// A generalized MARS time stamp                                           //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#ifndef MARS_MParContainer
#include "MParContainer.h"
#endif

#ifndef ROOT_TTime
#include <TTime.h>
#endif

struct timeval;

class MTime : public MParContainer
{
    friend bool operator==(MTime &t1, MTime &t2);
    friend bool operator!=(MTime &t1, MTime &t2);
    friend bool operator< (MTime &t1, MTime &t2);
    friend bool operator> (MTime &t1, MTime &t2);
    //friend bool operator<=(MTime &t1, MTime &t2);
    //friend bool operator>=(MTime &t1, MTime &t2);

private:
     UInt_t fMjd;     // [d]  Day in the century        (Day of sun rise)
     TTime  fTime;    // [ms] Time of Day               (-11h<=x<13h)
     UInt_t fNanoSec; // [ns] NanoSec part of TimeOfDay (<1000000)

     ULong_t GetTime24() const
     {
         const ULong_t hor = 3600000; // One Hour in milliseconds
         return (Long_t)fTime<0 ? (Long_t)fTime+24*hor : (ULong_t)fTime;
     }

public:
    MTime(const char *name=NULL, const char *title=NULL)
    {
        fName = name ? name : ClassName();
        fTitle = title;

        Reset();
    }

    MTime(MTime& t) { *this = t; }

    void Reset() { fMjd=0; fTime=0; fNanoSec=0; }

    void operator=(MTime &t)
    {
        fMjd     = t.fMjd;
        fTime    = t.fTime;
        fNanoSec = t.fNanoSec;
    }

    void Print(Option_t *t=NULL) const;

    void Now();

    Double_t GetMjd() const
    {
        return fMjd+((Long_t)fTime+fNanoSec/1e6)/1000;
    }

    void SetSystemTimer(const struct timeval &tv);

    Bool_t Set(UInt_t mjd, ULong_t ms, UInt_t ns=0)
    {
        const ULong_t hor = 3600000; // One Hour in milliseconds
        if (ms>24*hor-1 || ns>999999)
            return kFALSE;

        const Bool_t am = ms < hor*13;

        fMjd     = am ? mjd : mjd + 1;
        fTime    = (Long_t)(am ? ms  : ms - hor*24);
        fNanoSec = ns%1000000;

        return kTRUE;
    }

    Bool_t Set(UShort_t y, Byte_t m, Byte_t d, Byte_t h, Byte_t min, Byte_t s, UShort_t ms, UInt_t ns=0);

    void SetCT1Time(UInt_t mjd, UInt_t t1, UInt_t t0)
    {
        // int   isecs_since_midday; // seconds passed since midday before sunset (JD of run start)
        // int   isecfrac_200ns;     // fractional part of isecs_since_midday
        // fTime->SetTime(isecfrac_200ns, isecs_since_midday);
        const ULong_t hor = 3600000; // One Hour in milliseconds

        fNanoSec         = (200*t1)%1000000;
        const ULong_t ms = (200*t1)/1000000 + t0+12*hor;

        fTime = (Long_t)(ms<13*hor ? ms : ms-24*hor);

        fMjd = mjd+1;
    }
    /*
    void SetSystemTime(const TTime &tm)
    {
        fTime = (ULong_t)tm%(24*hor);
    }
     */
    operator double() const //[s]
    {
        return fMjd*24*3600+((Long_t)fTime+fNanoSec*1e6)*1000;
    }
    double operator()() const //[s]
    {
        return operator double();
    }

    void GetDate(UShort_t &y, Byte_t &m, Byte_t &d) const;
    void GetTime(Byte_t &h, Byte_t &m, Byte_t &s, UShort_t &ms) const // Time of Day returned in the range [0h, 24h)
    {
        Long_t tm = GetTime24();
        ms  = tm%1000;
        tm /= 1000;
        s   = tm%60;
        tm /= 60;
        m   = tm%60;
        tm /= 60;
        h   = tm;
    }
    Long_t GetTime() const // Time of Day returned in the range [-11h, 13h)
    { return (Long_t)fTime; }

    void GetTime(Byte_t &h, Byte_t &m, Byte_t &s) const
    {
        UShort_t ms;
        GetTime(h, m, s, ms);
    }

    ClassDef(MTime, 3)	//A generalized MARS time stamp
};

inline bool operator<(MTime &t1, MTime &t2)
{
    if (t1.fMjd<t2.fMjd)
        return true;
    if (t1.fMjd==t2.fMjd && t1.fTime<t2.fTime)
        return true;
    if (t1.fMjd==t2.fMjd && t1.fTime==t2.fTime && t1.fNanoSec<t2.fNanoSec)
        return true;
    return false;
}
inline bool operator>(MTime &t1, MTime &t2)
{
    if (t1.fMjd>t2.fMjd)
        return true;
    if (t1.fMjd==t2.fMjd && t1.fTime>t2.fTime)
        return true;
    if (t1.fMjd==t2.fMjd && t1.fTime==t2.fTime && t1.fNanoSec>t2.fNanoSec)
        return true;
    return false;
}
inline bool operator<=(MTime &t1, MTime &t2)
{
    return !(t1>t2);
}
inline bool operator>=(MTime &t1, MTime &t2)
{
    return !(t1<t2);
}
inline bool operator==(MTime &t1, MTime &t2)
{
    return t1.fNanoSec==t2.fNanoSec && t1.fTime==t2.fTime && t1.fMjd==t2.fMjd;
}
inline bool operator!=(MTime &t1, MTime &t2)
{
    return t1.fNanoSec!=t2.fNanoSec || t1.fTime!=t2.fTime || t1.fMjd!=t2.fMjd;
}
#endif
