/**
 *  @file   TimeCnv.h
 *  @brief  時間関係(ファイル時間、年月日時分秒構造体等)の変換関係.
 *  @author tenk* (Masashi Kitamura)
 *  @note
 *  -   Win の SYSTEMTIME と C言語の struct tm 用のラッパー関数.
 *  -   SYSTEMTIME または struct tm に入っている値はローカル時間であることを
 *      前提にしている.
 *  -   Public Domain Software
 */

#ifndef TIMECNV_H
#define TIMECNV_H

#if defined _WIN32

#include <windows.h>
#ifndef __cplusplus
#define inline __inline
#endif

typedef SYSTEMTIME          TimeCnv_Tm;         ///< 年月日時分秒のようにメンバー化された構造体.
typedef unsigned __int64    TimeCnv_time_t;     ///< ファイル時間で使われている64bit整数な値.

#else   // linux(unix).

#include <stdint.h>
#include <time.h>
typedef struct tm           TimeCnv_Tm;         ///< 年月日時分秒のようにメンバー化された構造体.
typedef time_t              TimeCnv_time_t;     ///< ファイル時間で使われている整数な値.

/** タイムゾーンの時間を取得.
 */
inline long TimeCnv_get_timeZone() {
    static long s_timeZone = (long)0x80000000;
    if (s_timeZone == (long)0x80000000) {
        tzset();
        s_timeZone = timezone;
    }
    return s_timeZone;
}

#endif  // _WIN32



/** utc時間をローカル時間に変換.
 */
inline TimeCnv_time_t   TimeCnv_Utc_to_LocalTime(TimeCnv_time_t utcTime) {
  #if defined _WIN32
    TimeCnv_time_t  t = 0;
    return FileTimeToLocalFileTime((const FILETIME*)&utcTime, (FILETIME*)&t) ? t : (TimeCnv_time_t)(-1);
  #else // 手抜きでtimezoneのみを反映.
    long    tz = TimeCnv_get_timeZone();
    return  utcTime + tz;
  #endif
}


/** ローカル時間をutc時間に変換.
 */
inline TimeCnv_time_t   TimeCnv_Local_to_UtcTime(TimeCnv_time_t localTime) {
  #if defined _WIN32
    TimeCnv_time_t  t = 0;
    return LocalFileTimeToFileTime((const FILETIME*)&localTime, (FILETIME*)&t) ? t : (TimeCnv_time_t)(-1);
  #else // 手抜きでtimezoneのみを反映.
    long    tz = TimeCnv_get_timeZone();
    return  localTime - tz;
  #endif
}


/** 年月日時分秒構造体をローカル時間として、ローカル時間を求める.
 */
inline TimeCnv_time_t   TimeCnv_Tm_to_LocalTime(const TimeCnv_Tm* st) {
  #if defined _WIN32
    TimeCnv_time_t  localTim;
    return (SystemTimeToFileTime((const SYSTEMTIME*)st, (FILETIME*)&localTim)) ? localTim : (TimeCnv_time_t)(-1);
  #else
    TimeCnv_Tm  tm0 = *st;
    return mktime(&tm0);
  #endif
}


/** 年月日時分秒構造体をローカル時間として、utc時間を求める.
 */
inline TimeCnv_time_t   TimeCnv_Tm_to_UtcTime(const TimeCnv_Tm* st)
{
  #if defined _WIN32
    TimeCnv_time_t  localTim;
    TimeCnv_time_t  tim;
    return (   SystemTimeToFileTime((const SYSTEMTIME*)st, (FILETIME*)&localTim)
            && LocalFileTimeToFileTime((const FILETIME*)&localTim, (FILETIME*)&tim)
           ) ? tim : (TimeCnv_time_t)(-1);
  #else
    TimeCnv_time_t  t = TimeCnv_Tm_to_LocalTime(st);
    return  (t != (TimeCnv_time_t)-1) ? TimeCnv_Local_to_UtcTime(t) : t;
  #endif
}


/** utc時間から、ローカルな年月日時分秒構造体を求める.
 */
inline TimeCnv_Tm*  TimeCnv_Tm_from_UtcTime(TimeCnv_Tm* st, TimeCnv_time_t utcTime)
{
  #if defined _WIN32
    TimeCnv_time_t  localTim = 0;
    return FileTimeToLocalFileTime((const FILETIME*)&utcTime, (FILETIME*)&localTim)
        && FileTimeToSystemTime((const FILETIME*)&localTim, (SYSTEMTIME*)st) ? st : 0;
  #else
    return gmtime_r(&utcTime, st);
  #endif
}


/** ローカル時間から、ローカルな年月日時分秒構造体を求める.
 */
inline TimeCnv_Tm*  TimeCnv_Tm_from_LocalTime(TimeCnv_Tm* st, TimeCnv_time_t localTime)
{
  #if defined _WIN32
    return FileTimeToSystemTime((const FILETIME*)&localTime, (SYSTEMTIME*)st) ? st : 0;
  #else
    return localtime_r(&localTime, st);
  #endif
}




// ========================================================================
// カレンダー時間の winの SYSTEMTIME と struct tm との辻褄合わせ用クラス.
// ========================================================================

#ifdef __cplusplus

#if defined _WIN32

/// tm と SYSTEMTIME のメンバー名のラッパークラス.
class TimeCnv_TmEx : public TimeCnv_Tm {
    typedef TimeCnv_Tm  BASE;
public:
    explicit TimeCnv_TmEx(unsigned y=0, unsigned m=1, unsigned d=1, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        wYear=y, wMonth=m, wDayOfWeek=0, wDay=d, wHour=h, wMinute=mi, wSecond=sec, wMilliseconds=ms;
    }
    TimeCnv_TmEx(const TimeCnv_TmEx& r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; }
    TimeCnv_TmEx(const TimeCnv_Tm&   r) { *(TimeCnv_Tm*)this = r; }

    TimeCnv_TmEx& operator=(const TimeCnv_Tm&  r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; return *this; }
    TimeCnv_TmEx& operator=(const TimeCnv_TmEx& r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; return *this; }

    void set(unsigned y=0, unsigned m=1, unsigned d=1, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        wYear=y, wMonth=m, wDayOfWeek=0, wDay=d, wHour=h, wMinute=mi, wSecond=sec, wMilliseconds=ms;
    }

    void set_date(unsigned y, unsigned m=1, unsigned d=1) { set_year(y); set_month(m); set_day(d); }

    void set_time(unsigned h, unsigned m=0, unsigned s=0, unsigned ms=0) {
        set_hour(h); set_minute(m); set_second(s); set_milliseconds(ms);
    }

    unsigned    year()         const { return wYear; }
    unsigned    month()        const { return wMonth; }
    unsigned    dayOfWeek()    const { return wDayOfWeek; }
    unsigned    day()          const { return wDay; }
    unsigned    hour()         const { return wHour; }
    unsigned    minute()       const { return wMinute; }
    unsigned    second()       const { return wSecond; }
    unsigned    milliseconds() const { return wMilliseconds; }

    void        set_year (unsigned y)         { assert(y <= 0xffff);        wYear        = y  ; }
    void        set_month(unsigned m)         { assert(m >= 1 && m <= 12);  wMonth       = m  ; }
    void        set_dayOfWeek(unsigned wd)    { assert(wd <= 6);            wDayOfWeek   = wd ; }
    void        set_day(unsigned d)           { assert(1 <= d && d <= 31);  wDay         = d  ; }
    void        set_hour(unsigned h)          { assert(h <= 23);            wHour        = h  ; }
    void        set_minute(unsigned mi)       { assert(mi <= 59);           wMinute      = mi ; }
    void        set_second(unsigned sec)      { assert(sec <= 61);          wSecond      = sec; }
    void        set_milliseconds(unsigned ms) { assert(ms <= 0xffff);       wMilliseconds= ms ; }

    void tm_set(unsigned y=0, unsigned m=0, unsigned d=0, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        wYear=y+1900, wMonth=m+1, wDayOfWeek=0, wDay=d, wHour=h, wMinute=mi, wSecond=sec, wMilliseconds=ms;
    }

    unsigned    tm_year()  const { return wYear-1900; }
    unsigned    tm_mon()   const { return wMonth-(wMonth > 0); }
    unsigned    tm_yday()  const { return  0; } // dummy.
    unsigned    tm_wday()  const { return wDayOfWeek; }
    unsigned    tm_mday()  const { return wDay; }
    unsigned    tm_hour()  const { return wHour; }
    unsigned    tm_min()   const { return wMinute; }
    unsigned    tm_sec()   const { return wSecond; }
    int         tm_isdst() const { return -1; } // dummy.

    void        set_tm_year(unsigned y  ) { assert(y <= 0xffff);        wYear        = y  + 1900; }
    void        set_tm_mon (unsigned m  ) { assert(m >= 0 && m <= 11);  wMonth       = m  + 1; }
    void        set_tm_yday(unsigned    ) { ; } /* dummy */
    void        set_tm_wday(unsigned wd ) { assert(wd <= 6);            wDayOfWeek   = wd ; }
    void        set_tm_mday(unsigned d  ) { assert(1 <= d && d <= 31);  wDay         = d  ; }
    void        set_tm_hour(unsigned h  ) { assert(h <= 23);            wHour        = h  ; }
    void        set_tm_min (unsigned mi ) { assert(mi <= 59);           wMinute      = mi ; }
    void        set_tm_sec (unsigned sec) { assert(sec <= 61);          wSecond      = sec; }
    void        set_tm_isdst(int        ) { ; } /* dummy */
};

#else

/// tm と SYSTEMTIME のメンバー名のラッパークラス.
class TimeCnv_TmEx : public TimeCnv_Tm {
    typedef TimeCnv_Tm  BASE;
public:
    explicit TimeCnv_TmEx(unsigned y=0, unsigned m=1, unsigned d=1, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        set(y,m,d,h,mi,sec,ms);
    }
    TimeCnv_TmEx(const TimeCnv_TmEx& r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; }
    TimeCnv_TmEx(const TimeCnv_Tm&  r) { *(TimeCnv_Tm*)this = r; }

    TimeCnv_TmEx& operator=(const TimeCnv_Tm&  r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; return *this; }
    TimeCnv_TmEx& operator=(const TimeCnv_TmEx& r) { *(TimeCnv_Tm*)this = *(TimeCnv_Tm*)&r; return *this; }

    void set(unsigned y=0, unsigned m=1, unsigned d=1, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        set_date(y,m,d); set_time(h,mi,sec,ms);
    }

    void set_date(unsigned y, unsigned m=1, unsigned d=1) { set_year(y); set_month(m); set_day(d); }

    void set_time(unsigned h, unsigned m=0, unsigned s=0, unsigned ms=0) {
        set_hour(h); set_minute(m); set_second(s); ms;
    }

    unsigned    year()         const { return BASE::tm_year+1900; }
    unsigned    month()        const { return BASE::tm_mon+1; }
    unsigned    dayOfWeek()    const { return BASE::tm_wday; }
    unsigned    day()          const { return BASE::tm_mday; }
    unsigned    hour()         const { return BASE::tm_hour; }
    unsigned    minute()       const { return BASE::tm_min; }
    unsigned    second()       const { return BASE::tm_sec; }
    unsigned    milliseconds() const { return 0; }  // だみー

    void        set_year (unsigned y)         { assert(y <= 0xffff);        BASE::tm_year = y-1900; }
    void        set_month(unsigned m)         { assert(m >= 1 && m <= 12);  BASE::tm_mon  = m-1; }
    void        set_dayOfWeek(unsigned wd)    { assert(wd <= 6);            BASE::tm_wday = wd ; }
    void        set_day(unsigned d)           { assert(1 <= d && d <= 31);  BASE::tm_mday = d  ; }
    void        set_hour(unsigned h)          { assert(h <= 23);            BASE::tm_hour = h  ; }
    void        set_minute(unsigned mi)       { assert(mi <= 59);           BASE::tm_min  = mi ; }
    void        set_second(unsigned sec)      { assert(sec <= 61);          BASE::tm_sec  = sec; }
    void        set_milliseconds(unsigned ms) { assert(ms <= 0xffff); } // dummy.

    void tm_set(unsigned y=0, unsigned m=0, unsigned d=0, unsigned h=0, unsigned mi=0, unsigned sec=0, unsigned ms=0) {
        set_tm_year(y); set_tm_mon(m); set_tm_mday(d); set_tm_hour(h); set_tm_min(mi); set_tm_sec(sec);
    }

    unsigned    tm_year()  const { return BASE::tm_year ; }
    unsigned    tm_mon()   const { return BASE::tm_mon  ; }
    unsigned    tm_yday()  const { return BASE::tm_yday ; }
    unsigned    tm_wday()  const { return BASE::tm_wday ; }
    unsigned    tm_mday()  const { return BASE::tm_mday ; }
    unsigned    tm_hour()  const { return BASE::tm_hour ; }
    unsigned    tm_min()   const { return BASE::tm_min  ; }
    unsigned    tm_sec()   const { return BASE::tm_sec  ; }
    int         tm_isdst() const { return BASE::tm_isdst; }

    void        set_tm_year(unsigned y  ) { assert(y <= 0xffff);        BASE::tm_year = y  ; }
    void        set_tm_mon (unsigned m  ) { assert(m >= 0 && m <= 11);  BASE::tm_mon  = m  ; }
    void        set_tm_yday(unsigned yd ) { assert(yd>= 0 && yd<=365);  BASE::tm_yday = yd ; }
    void        set_tm_wday(unsigned wd ) { assert(wd <= 6);            BASE::tm_wday = wd ; }
    void        set_tm_mday(unsigned d  ) { assert(1 <= d && d <= 31);  BASE::tm_mday = d  ; }
    void        set_tm_hour(unsigned h  ) { assert(h <= 23);            BASE::tm_hour = h  ; }
    void        set_tm_min (unsigned mi ) { assert(mi <= 59);           BASE::tm_min  = mi ; }
    void        set_tm_sec (unsigned sec) { assert(sec <= 61);          BASE::tm_sec  = sec; }
    void        set_tm_isdst(int   isdst) {                             BASE::tm_isdst=isdst;}
};

#endif  // _WIN32.

#endif  // __cplusplus

#endif  // TIMECNV_H