#pragma once
#ifndef _DTHRes_h_
#define _DTHRes_h_

/*
    Last update: 31 August 2022

    Use DTHRes32.dll for your 32-bit programs
    Use DTHRes64.dll for your 64-bit programs

    The version of Domain Time may be either 32-bit or 64-bit for either DLL to work.

    Structure alignment uses the Windows default packing (8-byte).
*/

#ifdef __cplusplus
extern "C" {
#endif

    #define SYNC_STATUS_INVALID         0       // this status means the service isn't running or has MAJOR problems
    #define SYNC_STATUS_UNKNOWN         1       // status isn't known yet, for whatever reason, probably first check hasn't completed
    #define SYNC_STATUS_CHECKING        2       // we are currently checking the time
    #define SYNC_STATUS_SYNCED          3       // we are synchronized
    #define SYNC_STATUS_UNSYNCED        4       // we are not synchronized


    #define ROLE_UNKNOWN            0
    #define ROLE_MASTER             1       // machine is a PDC
    #define ROLE_SLAVE              2       // machine is a BDC or participating server
    #define ROLE_INDIE              3       // machine is an independent server
    #define ROLE_CLIENT             4       // machine is a full client

    #ifndef _TIMEB_DEFINED
        #define _TIMEB_DEFINED

        struct __timeb32
        {
            __int32         time;
            unsigned short  millitm;
            short           timezone;
            short           dstflag;
        };

        struct __timeb64
        {
            __int64         time;
            unsigned short  millitm;
            short           timezone;
            short           dstflag;
        };

    #endif // _TIMEB_DEFINED

    //
    // These functions return a BOOL (32-bit unsigned): TRUE (1) if successful, or FALSE (0) if unsuccessful.
    // If the return code is FALSE, call GetLastError() for more information.
    //

BOOL    WINAPI  IsDomainTimeRunning             ();
BOOL    WINAPI  GetDomainTimeVersion            (PDWORD pdwMajor,PDWORD pdwMinor,PDWORD pdwDate);
BOOL    WINAPI  GetDomainTimeRole               (PDWORD pdwRole);
BOOL    WINAPI  GetDomainTimeStatus             (PDWORD pdwStatus);

    //
    // GetDomainTimeNotifyHandle() returns a HANDLE to an event if successful, NULL if unsuccessful.
    // If the returned handle is NULL, call GetLastError() for more information.
    //
    // Under Vista and later, this call will fail with ERROR_ACCESS_DENIED unless the service is already running or the caller has admin privileges
    // Under other operating systems, this call will succeeed for any user, whether or not Domain Time is already running. In these cases, you
    // can watch the handle to know when Domain Time starts up. If you receive ERROR_ACCESS_DENIED, you must call the function again after Domain
    // Time has started (or after raising your privilege level) before you can wait on the handle.
    //
    // The handle becomes signalled when there is a change in Domain Time's status. Use the handle in WaitForSingleObject, or any Win32 Wait function.
    // When the wait is satisfied, call GetDomainTimeStatus() to find out the new status. Refer to the SYNC_STATUS_ constants above for the meaning
    // of the return value. After you have called GetDomainTimeStatus(), you may return to wait on the handle again.
    //
    // It is the caller's responsibility to close the handle using CloseHandle(). Failure to do so will cause a memory leak in your program.
    //

HANDLE  WINAPI  GetDomainTimeNotifyHandle       ();

    //
    // GetDomainTime and GetLocalDomainTime are drop-in replacements for GetSystemTime and GetLocalTime respectively. They will return high-precision
    // UTC or local time, and set the return value to TRUE. If the return value is FALSE, the time values are valid but not high-precision. You may
    // call GetLastError() for more information. (Note that when GetDomainTime or GetLocalDomainTime return FALSE, the time value returned is exactly
    // the same as what would be returned by GetSystemTime or GetLocalTime.)

BOOL    WINAPI  GetDomainTime                   (LPSYSTEMTIME pST); // return high-precion UTC
BOOL    WINAPI  GetLocalDomainTime              (LPSYSTEMTIME pST); // return high-precision local time

    //
    // GetDomainTimeAsFileTime is a drop-in replacement for GetSystemTimeAsFileTime. It will return high-precision UTC time expressed as the number
    // of hectonanoseconds elapsed since midnight, January 1st, 1601 UTC. If the return value is FALSE, the time value is valid but not high-
    // precision. Call GetLastError() for more information.
    //

BOOL    WINAPI  GetDomainTimeAsFileTime         (PFILETIME pFT); // return high-precion UTC, using GetSystemTimePreciseAsFileTime() if available

    //
    // The GetDomainTimeAsFileTimeMonotonic() function tries to return monotonically incrementing time-of-day. This is not always
    // possible, because the time-of-day is not absolute at resolutions below the timer tick (and not even then, since it's possible
    // to have the clock stepped backward, either by Domain Time, at system startup, by another process, or by the foreground user).
    //
    // IMPORTANT NOTE: If you are using GetDomainTimeAsFileTimeMonotonic(), you should make sure your DLLs are version 5.2.b.20190331
    // or later. There was a bug in earlier versions that could, under specific circumstances, make the return value accurate only to
    // the frequency of the system's performance counter. The performance counter, even if based on the processor's TSC, is suitable
    // only for measuring small intervals, and will diverge from the time-of-day as your application runs.
    //
    // Most performance counters run at a fixed frequency which does not divide evenly into an integral number of ticks per hectonanosecond.
    // For example, if the QPC runs at 1.01 seconds/second, we'll be ahead of the tick by a handful of hectos just before the tick,
    // resolving back down just after the tick. This is a generic problem with HPET and APIC and their clocks not dividing down to an
    // integral PerfFrequency for the divisor. The QPC timer, which may be based on RDSTC or mobo timer (it's up to the HAL to choose),
    // is suitable for measuring very tiny intervals, but not for measuring wallclock time. We are marrying the GSTAFT and QPC, using the
    // QPC to interpolate between proper clock ticks. This should be a small enough interval, but isn't -- in practice, the derived wallclock
    // will move slightly ahead, then rewind, then move slightly ahead again, all based on how much the divisor doesn't match reality and
    // when the various ticks happen.
    //
    // The Monotonic() function keeps track of the last-delivered timestamp, and if the currently-derived wallclock is earlier than the
    // last time through, waits a bit and tries again. If the backward swing is very tiny, we're probably right before the tick, and
    // waiting to make the time monotonic won't cost much. If it's a large swing, then either the clock is goofy or the time actually has
    // gone backward.
    //
    // Two variables, signedMaxHectos and maxLoops, let you control how the monotonic calculator performs. The default for signedMaxHectos
    // is -10,000 (one millisecond). Anything over this amount is considered "large," and probably represents a real backward movement of
    // the wallclock. Anything less than this is worth waiting for, up to maxLoops (default 1000).
    //

DWORD   WINAPI  GetDomainTimeAsFileTimeMonotonic(PFILETIME pFT);    // returns NO_ERROR if high-res AND monotonic; error code if not
void    WINAPI  GetMonotonicityLimits           (PLARGE_INTEGER signedMaxHectos,PDWORD maxLoops);   // set the limits
void    WINAPI  SetMonotonicityLimits           (PLARGE_INTEGER signedMaxHectos,PDWORD maxLoops);   // read the limits

    //
    // FTime32 and FTime64 are high-precision replacements for _ftime32 and _ftime64. The dtsflag and timezone members are set to zero. If you need
    // time zone information, you should retrieve it separately. The remaining members of *timeptr are set with the current UTC time of day in seconds
    // and milliseconds since midnight January 1st 1970 UTC. If the return value is FALSE, the time value is valid but not high-resoluton. Call GetLastError()
    // for more information.
    //

BOOL    WINAPI  FTime32                         (struct __timeb32 *timeptr);
BOOL    WINAPI  FTime64                         (struct __timeb64 *timeptr);

//
// New starting with version 5.2.b.20190401 -- access PTP statistics and this DLLs version number
//

    //
    // IEEE 1588-2008 port state values; these values may appear in the PTP_STATISTICS portState member
    //

    #define PTP_OFFLINE         0   // not part of 1588 spec; indicates the node is offline
    #define PTP_INITIALIZING    1   // initializing datasets, hardware, sockets, etc.
    #define PTP_FAULTY          2   // fault state -- something very wrong (almost always duplicate node on network)
    #define PTP_DISABLED        3   // the PTP state engine is not running
    #define PTP_LISTENING       4   // the PTP state engine is listening for a master
    #define PTP_PRE_MASTER      5   // not used by Domain Time
    #define PTP_MASTER          6   // clock is source of time to its slaves
    #define PTP_PASSIVE         7   // PTP engine is passively monitoring the network
    #define PTP_CALIBRATING     8   // PTP engine is in the process of becoming a slave
    #define PTP_SLAVE           9   // PTP engine is synchronizing to a master clock

    //
    // IEEE 1588-2008 ProfileID values; these values may appear in the PTP_STATISTICS profileId member
    //

    #define PTP_PROFILE_UNKNOWN         0x00    // PTP profile unknown or not yet determined
    #define PTP_PROFILE_END_TO_END      0x01    // PTP standard profile for End-to-End
    #define PTP_PROFILE_PEER_TO_PEER    0x02    // PTP standard profile for Peer-to-Peer
    #define PTP_PROFILE_COMMON_P2P      0x03    // PTP v2.1 only; Common Peer-to-Peer delay measurement
    #define PTP_PROFILE_SPECIAL         0x04    // PTP v2.1 only; neither E2E nor P2P
    #define PTP_PROFILE_TELECOM         0xF0    // PTP TELECOM-2008 profile
    #define PTP_PROFILE_ENTERPRISE      0xF1    // PTP Enterprise profile
    #define PTP_DELAY_IS_DISABLED       0xFE    // Delay is disabled

    typedef struct 
    {
        union
        {
            struct  sockaddr_in     sin4;       // if sin4.sin_family is AF_INET, sin4 is a valid IPv4 address
            struct  sockaddr_in6    sin6;       // if sin6.sin6_family is AF_INET6, sin6 is a valid IPv6 address
        };
    } IP_AGNOSTIC_ADDRESS, *PIP_AGNOSTIC_ADDRESS;

    typedef struct
    {
        unsigned __int64    clockIdentity;      // clockIdentity    8 bytes, usually expressed as xxxxxx-xxxx-xxxxxx
        unsigned __int16    portNumber;         // portNumber       2 bytes, usually represented in decimal
    } PTPPortIdentity;

    typedef struct
    {
        DWORD               cbSize;                 // caller must fill with sizeof(PTP_STATISTICS)
        BYTE                bRunning;               // non-zero if PTP is running
        BYTE                portState;              // current PTP port state (Listening, Master, Slave, Faulty, etc)
        BYTE                domainNumber;           // current PTP domain number (0 - 239)
        BYTE                profildId;              // current PTP profile id
        signed __int64      hnsOffsetFromMaster;    // if slave, current offset from master, else meaningless
        unsigned __int64    hnsMeanPathDelay;       // if slave, current mean path delay, else meaningless
        signed __int32      utcOffset;              // if slave, master's TAI-UTC offset value, else meaningless
        IP_AGNOSTIC_ADDRESS MasterIP;               // if slave, current master's IP, else meaningless
        PTPPortIdentity     piMaster;               // if slave, current master's port identity, else meaningless
        PTPPortIdentity     piSelf;                 // the port identity of this instance (this clock)
    } PTP_STATISTICS, *PPTP_STATISTICS;


DWORD   WINAPI  GetPTPStats            (PPTP_STATISTICS pStats);   // returns NO_ERROR or error code
BOOL    WINAPI  GetSDKDLLVersion       (PDWORD pdwMajor,PDWORD pdwMinor,PDWORD pdwDate); // always returns TRUE

//
//  Example of using GetPTPStats
//
//  PTP_STATISTICS stats;
//  stats.cbSize = sizeof(PTP_STATISTICS);
//  DWORD dwError = GetPTPStats(&stats);
//
//  if (dwError)
//  {
//      do whatever error handling you want
//  }
//
//  else
//  {
//      if (PTP_SLAVE == stats.portState)
//      {
//          double dOffset = (double)stats.hnsOffsetFromMaster / 10000000.0;
//          printf("Current offset from master is %+1.7f seconds\n",dOffset);
//      }
//  }
//
//
//  Example of using GetSDKDLLVersion
//
//  DWORD dwMajor;
//  DWORD dwMinor;
//  DWORD dwDate;
//
//  GetSDKDLLVersion(&dwMajor,&dwMinor,&dwDate);
//  printf("Version is %u.%u.b.%04u%04u\n",
//      dwMajor,           // major version number
//      dwMinor,           // minor version number
//      HIWORD(dwDate),    // year
//      LOWORD(dwDate));   // month and day
//      
//
// GetDomainTimeAsFileTimeMonotonic() and GetPTPStats() return an error code directly; for all others, if the return
// value is FALSE, use GetLastError() to discover why the function failed:
//
//  ERROR_FILE_NOT_FOUND            The Domain Time service isn't running yet, so the shared memory isn't available
//  ERROR_ACCESS_DENIED             The caller has insufficient privileges to map the shared memory area
//  ERROR_SERVICE_NOT_ACTIVE        The shared memory was opened succesfully at some point and is still valid, but the service has stopped running since that time
//  ERROR_TIMER_RESOLUTION_NOT_SET  The Domain Time service has stopped providing high-resolution time, probably because the service has stopped
//  ERROR_TIME_SKEW                 The time returned is low-res because the system timer overflowed (normal) or went backward (monotonic)
//  ERROR_BAD_ENVIRONMENT           The DTHRESxx.DLL is not compatible with the version of Domain Time that is currently running
//  ERROR_BAD_FORMAT                The time returned is low-res because the performance counter value was unexpected
//  ERROR_INVALID_TIME              Domain Time has marked its own time as untrusted; this is usually a temporary condition
//  ERROR_INVALID_PARAMETER         You did not set cbSize of PTP_STATISTICS structure to sizeof(PTP_STATISTICS) before calling GetPTPStats()
//  Other Win32 error               A standard Win32 operating system error
//

//
// Function declarations, names, and C-Style API prototypes for use with dynamic loading
//

#define GET_PTP_STATS_NAME                          "GetPTPStats"           // only available in versions > 01 Apr 2019
#define GET_SDKDLL_VERSION_NAME                     "GetSDKDLLVersion"      // only available in versions > 01 Apr 2019
#define IS_DOMAINTIME_RUNNING_NAME                  "IsDomainTimeRunning"
#define GET_DOMAINTIME_VERSION_NAME                 "GetDomainTimeVersion"
#define GET_DOMAINTIME_ROLE_NAME                    "GetDomainTimeRole"
#define GET_DOMAINTIME_STATUS_NAME                  "GetDomainTimeStatus"
#define GET_DOMAINTIME_NOTIFY_HANDLE_NAME           "GetDomainTimeNotifyHandle"
#define GET_DOMAINTIME_NAME                         "GetDomainTime"
#define GET_LOCAL_DOMAINTIME_NAME                   "GetLocalDomainTime"
#define GET_DOMAINTIME_AS_FILETIME                  "GetDomainTimeAsFileTime"
#define GET_DOMAINTIME_AS_FILETIME_MONOTONIC_NAME   "GetDomainTimeAsFileTimeMonotonic"
#define GET_MONOTONICITY_LIMITS_NAME                "GetMonotonicityLimits"
#define SET_MONOTONICITY_LIMITS_NAME                "SetMonotonicityLimits"
#define FTIME32_NAME                                "FTime32"
#define FTIME64_NAME                                "FTime64"

typedef DWORD   (WINAPI* PGET_PTPSTATS)                         (IN OUT PPTP_STATISTICS pStats);
typedef BOOL    (WINAPI* PIS_DOMAINTIME_RUNNING)                ();
typedef BOOL    (WINAPI* PGET_DOMAINTIME_VERSION)               (OUT PDWORD pdwMajor,OUT PDWORD pdwMinor,OUT PDWORD pdwDate);
typedef BOOL    (WINAPI* PGET_SDKDLL_VERSION)                   (OUT PDWORD pdwMajor,OUT PDWORD pdwMinor,OUT PDWORD pdwDate);
typedef BOOL    (WINAPI* PGET_DOMAINTIME_ROLE)                  (OUT PDWORD pdwRole);
typedef BOOL    (WINAPI* PGET_DOMAINTIME_STATUS)                (OUT PDWORD pdwStatus);
typedef HANDLE  (WINAPI* PGET_DOMAINTIME_NOTIFY_HANDLE)         ();
typedef BOOL    (WINAPI* PGET_DOMAINTIME)                       (OUT LPSYSTEMTIME pST);
typedef BOOL    (WINAPI* PGET_LOCAL_DOMAINTIME)                 (OUT LPSYSTEMTIME pST);
typedef BOOL    (WINAPI* PGET_DOMAINTIME_AS_FILETIME)           (OUT PFILETIME pFT);
typedef DWORD   (WINAPI* PGET_DOMAINTIME_AS_FILETIME_MONOTONIC) (OUT PFILETIME pFT);
typedef VOID    (WINAPI* PGET_MONOTONICITY_LIMITS)              (OUT PLARGE_INTEGER signedMaxHectos,OUT PDWORD maxLoops);
typedef VOID    (WINAPI* PSET_MONOTONICITY_LIMITS)              (IN PLARGE_INTEGER signedMaxHectos,IN PDWORD maxLoops);
typedef BOOL    (WINAPI* PFTIME32)                              (OUT struct __timeb32* timeptr);
typedef BOOL    (WINAPI* PFTIME64)                              (OUT struct __timeb64* timeptr);

//
// Example of dynamically loading the SDK DLL and determining if Domain Time is running
/*
#include <windows.h>
#include "dthres.h"

void main()
{
    HINSTANCE hLib;
    DWORD     dwError = NO_ERROR;

    #ifdef _WIN64
    hLib = LoadLibrary("dthres64.dll");
    #else
    hLib = LoadLibrary("dthres32.dll");
    #endif

    if (NULL == hLib)
    {
        dwError = GetLastError();
    }
    
    else
    {
        PIS_DOMAINTIME_RUNNING pIsDomainTimeRunning = (PIS_DOMAINTIME_RUNNING)GetProcAddress(hLib,IS_DOMAINTIME_RUNNING_NAME);

        if (pIsDomainTimeRunning)
        {
            BOOL bRunning = pIsDomainTimeRunning();
            if (!bRunning)
            {
                dwError = GetLastError(); // almost certainly ERROR_FILE_NOT_FOUND, which means
                                          // the shared memory segment has not been created yet.
            }
        }

        FreeLibrary(hLib);
    }
}

*/

#ifdef __cplusplus
}
#endif

#endif //_DTHRes_h_
