#ifndef CXXOMFORT_CTIME_HPP
#define CXXOMFORT_CTIME_HPP
/**
 * @file
 * @brief Backports for features in <tt>ctime</tt>.
 * @xrefitem std0ctime "<ctime>" ""
 * @ingroup std0ctime
 *
 */

#include <cxxomfort/base.hpp>
#if (defined(CXXOMFORT_NOTICES) && CXXOMFORT_NOTICES>1)
	#pragma message CXXO_NOTICE("enabled <cstdint> support.")
#endif
#include <ctime>

// 
// POSIX-related time facilities if possible
// 
#if defined(__unix__)
    #include <unistd.h>
    #include <time.h>
    #include <sys/times.h>
#elif defined(_WIN32)
    #if (CXXO_COMPILERREQ(CXXO_COMPILERID_MSC,>=1800) && defined(HAVE_STRUCT_TIMESPEC))
        #include <time.h>
    #else
        #include <winsock2.h>
    #endif
#else

#endif

// 
// generic wrapper around eg.: localtime_r where available
// 


// 
// timespec_get
// 

#if defined(DOXYGEN_DOC)
namespace cxxomfort { namespace cxxostd {
	/**
	 * @ingroup std0ctime
	 * @{
	 * */
	
    //! Struct timespec, from POSIX / C++17.
    struct timespec {
        time_t tv_sec;
        long tv_nsec;
    };

    /**
     * @brief Returns timespec information from the system clock and writes it into @a ts .
     * @cxx17b "Time" timespec_get
     * @see POSIX @c timespec_get() .
     * @see @cppref{time/timespec_get}
     * 
     * */
    int timespec_get ( struct timespec* ts, int base );
    
    /**
     * @}
     * */
    
} }
#elif (CXXOMFORT_CXX_STD >= 2017)
	#undef  CXXOMFORT_IMPLEMENTS_timespec_get
    #define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_NATIVE()
	#if (defined(CXXOMFORT_NOTICES) && CXXOMFORT_NOTICES>2)
		#pragma message CXXO_NOTICE("<ctime> C++17 native")
	#endif

#elif (defined(__ANDROID__) && defined(TIME_UTC) && defined(_POSIX_VERSION) && _POSIX_VERSION>=200809)
	/* bugfix for https://github.com/r-lib/later/issues/126 - timespec_get() missing
	 * this is a case where TIME_UTC is defined but timespec_get is missing
	 * */
	#undef  CXXOMFORT_IMPLEMENTS_timespec_get 
	#define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_BACKPORT()
	
namespace cxxomfort { namespace cxxostd {

static inline 
int timespec_get (struct timespec* ts, int ) {
	return clock_gettime(CLOCK_REALTIME, ts);
}
	
} }

namespace std {
	using ::timespec;
	using cxxomfort::cxxostd::timespec_get;
}

#elif (CXXOMFORT_CXX_STD >= 0)

    #include <time.h>
    //#include <sys/times.h>
    // we look for CLOCK_REALTIME to use http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

	#if (!defined(TIME_UTC))
		#define TIME_UTC 1
	#endif
	
/*
 * if in POSIX, we can just use their functions
 * when on Windows, we sandwich
 * */
    #if (defined(_POSIX_VERSION) && _POSIX_VERSION>=200809)
        #undef  CXXOMFORT_IMPLEMENTS_timespec_get 
        #define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_NATIVE()
        //#pragma message "timespec posix"

namespace cxxomfort { namespace cxxostd {
    using ::timespec;
    using ::timespec_get;
} }

    #elif (CXXO_COMPILERREQ(CXXO_COMPILERID_MSC,>=1800) && defined(HAVE_STRUCT_TIMESPEC))
        #undef  CXXOMFORT_IMPLEMENTS_timespec_get 
        #define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_NATIVE()
        //#pragma message ("timespec windows ucrt")

namespace cxxomfort { namespace cxxostd {
    using ::timespec;
    using ::timespec_get;
} }
    
    #elif defined(_WIN32)
        #undef  CXXOMFORT_IMPLEMENTS_timespec_get 
        #define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_EMULATION()
        //#pragma message ("timespec windows GetSystemTime")

namespace cxxomfort { namespace cxxostd {

    struct timespec {
        std::time_t tv_sec;
        long tv_nsec;
    };

    static inline 
    int timespec_get ( struct timespec* ts, int base ) {
        //using namespace std;
        (void)base;
        ts->tv_sec = time(0);
        uint_least64_t c0 = GetTickCount();
        SYSTEMTIME st;
        GetSystemTime(&st);
        unsigned long long millis= st.wMilliseconds;
        if (millis==0) {
/*
            unsigned seconds= st.wSecond;
            while (seconds==st.wSecond) {
                GetSystemTime(&st);
            }
            uint_least64_t c1= GetTickCount();
            millis= (c1-c0);
*/
/*
            FILETIME ft;
            GetSystemTimeAsFileTime(&ft);
            uint_least64_t uft= static_cast<uint_least64_t>(ft.dwLowDateTime) + static_cast<uint_least64_t>(ft.dwHighDateTime)<< 32;
            millis= uft % 86400000000ULL;
*/
        }
        ts->tv_nsec= static_cast<long>(millis); // * 1000UL * 1000UL; //static_cast<long>(st.wMilliseconds)*1000UL;
        return 0;
    }

} } // cxxomfort::cxxostd

    #else
        #undef  CXXOMFORT_IMPLEMENTS_timespec_get 
        #define CXXOMFORT_IMPLEMENTS_timespec_get CXXO_IMPLSTATUS_EMULATION()
        #pragma message ("timespec poorman's nsec=0")

namespace cxxomfort { namespace cxxostd {

    struct timespec {
        std::time_t tv_sec;
        long tv_nsec;
    };

    static inline 
    int timespec_get ( struct timespec* ts, int base ) {
        (void)base;
        ts->tv_sec = time(0);
        ts->tv_nsec= 0 ; //static_cast<long>(st.wMilliseconds)*1000UL;
        return 0;
    }

} } // cxxomfort::cxxostd

    #endif // posix? windows? foo?


namespace std {
    using ::cxxomfort::cxxostd::timespec;
    using ::cxxomfort::cxxostd::timespec_get;
}

#else 


#endif // cxx_std


// 
// tm_time, tm_date 
// (these are storage-only classes)
// 

namespace cxxomfort {

#pragma pack(push)
#pragma pack(1)
/**
 * @struct tm_time 
 * @brief A storage-only class to store the time component of a struct @c std::tm .
 * */
struct tm_time {
	CXXO_CONSTEXPR 
	explicit tm_time (std::tm tm_) CXXO_NOEXCEPT
	: tm_hour(tm_.tm_hour), tm_min(tm_.tm_min), tm_sec(tm_.tm_sec)
	{ }
    explicit tm_time (int h, int m, int s) CXXO_NOEXCEPT
    : tm_hour(h), tm_min(m), tm_sec(s) 
    { }

	unsigned tm_hour: 6;
	unsigned tm_min: 6;
	unsigned tm_sec: 6;
};

/**
 * @struct tm_date 
 * @brief A storage-only class to store the date component of a struct @c std::tm .
 * */
struct tm_date {
	CXXO_CONSTEXPR 
	explicit tm_date (std::tm tm_) CXXO_NOEXCEPT
	: tm_year(tm_.tm_year), tm_mon(tm_.tm_mon), tm_mday(tm_.tm_mday)
	{ }
    explicit tm_date (int y, int m, int d) CXXO_NOEXCEPT
    : tm_year(y-1900), tm_mon(m-1), tm_mday(d) 
    { }

	unsigned tm_year: 12;
	unsigned tm_mon: 4;
	unsigned tm_mday: 5;
};
#pragma pack(pop)


#if (defined(DOXYGEN_DOC))
/**
 * @brief Composes a @c std::tm with information from a storage time and a storage date
 * @pre @a t is a valid (hour, minutes, seconds) tuple.
 * @pre @a d is a valid (year, month, day) tuple.
 * @return A <tt>struct tm</tt> object.
 * */

static 
std::tm to_tm (tm_time t, tm_date d) {}
#elif (defined(_POSIX_VERSION))
    // here we can shortcut most of the work via strptime
static inline
std::tm to_tm (tm_time t, tm_date d) {
    std::tm ret;
    char fmt[]= "%4d-%02d-%02d %02u:%02u:%02u";
    char buffer[255]= {0};
    sprintf(buffer, fmt, d.tm_year+1900, d.tm_mon+1, d.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
    strptime(buffer, "%Y-%m-%d %H:%M:%S", &ret);
    return ret;
}
#else
    // without POSIX and functions like strptime, slow path
static 
std::tm to_tm (tm_time t, tm_date d) {
	std::tm ret;
    ret.tm_year= d.tm_year;
    ret.tm_mon= d.tm_mon;
    ret.tm_mday= d.tm_mday;
    ret.tm_hour= t.tm_hour;
    ret.tm_min= t.tm_min;
    ret.tm_sec= t.tm_sec;
    // we use mktime to convert to a time_t and back into a valid tm
    time_t etime= mktime(&ret);
    if (etime==-1) {
        // error in conversion, we can't post-process ret;
    } else {
        ret= *localtime(&etime);
    }
	return ret;
}
#endif

} // cxxomfort

// 
// dotbeats
// 



// 
// msleep 
// 

#if defined(_POSIX_VERSION)
    #include <time.h>
namespace cxxomfort {
    void msleep (uint32_t u) {
        usleep(u*1000);
    }
} // cxxomfort

#elif defined(_WIN32)
	//#include <winbase.h>
	#include <winsock2.h>
	
// it is responsibility of the user to include the whatever Windows headers are necessary
//extern void WINAPI Sleep (unsigned long);

namespace cxxomfort {
    void msleep (uint32_t u) {
        Sleep(u);
    }
}

#endif

#endif


/**
 * @page std0ctime <ctime>
 * @brief Backports related to Standard header <tt><ctime></tt>
 * 
 * @code
 * #include <cxxomfort/ctime.hpp>
 * // or as part of:
 * #include <cxxomfort/backports.hpp>
 * @endcode
 * @endcode
 * 
 * (or automatically by including <tt><cxxomfort/backports.hpp></tt> / <tt><cxxomfort/cxxomfort.hpp></tt>)
 * 
 * @section Interfaces
 * 
 * Interfaces defined in this section:
 * 
 * * @c timespec_get() - obtain current time information for a system clock.
 * * @c timespec - information datatype for @c timespec_get .
 * * @c CXXOMFORT_IMPLEMENTS_timespec_get - implementation status for the above.
 * 
 * Non-backport interfaces:
 * 
 * * @c cxxomfort::msleep() - microseconds sleep.
 * * @c tm_date - storage class for the date part of a struct @c std::tm .
 * * @c tm_time - storage class for the time part of a struct @c std::tm .
 * 
 * @section timespec_notes Notes on timespec 
 * 
 * Note that the resolution on <tt>timespec::tv_nsec</tt> is limited by the system clock, and some machines' clocks may not be able to provide information with the required precision. Because of this, as part of the emulation this function is @e permitted to always return zero for this field. 
 * 
 * @section also See Also
 * 
 * @see @cppref{header/ctime} (cppreference) 
 * 
 * */

