#include "pch.h"
#include "System/TimeProvider.h"

#if defined(_WIN32)
#include <windows.h>
#else
#include <chrono>
#endif

namespace DotNetDupe {
    namespace System {

        class SystemTimeProvider : public TimeProvider {
        public:
            DateTimeOffset GetUtcNow() const override {
#if defined(_WIN32)
                FILETIME ft;
                GetSystemTimePreciseAsFileTime(&ft);
                int64_t ticks = (((int64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
                // Windows FILETIME starts from 1601-01-01, .NET Ticks start from 0001-01-01.
                // Difference is 504911232000000000 ticks.
                return DateTimeOffset(ticks + 504911232000000000LL);
#else
                auto now = std::chrono::system_clock::now();
                auto duration = now.time_since_epoch();
                auto ticks = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() / 100;
                // Unix epoch is 1970-01-01. .NET Ticks start from 0001-01-01.
                // Difference is 621355968000000000 ticks.
                return DateTimeOffset(ticks + 621355968000000000LL);
#endif
            }

            DateTimeOffset GetLocalNow() const override {
#if defined(_WIN32)
                SYSTEMTIME st;
                GetLocalTime(&st);
                FILETIME ft;
                SystemTimeToFileTime(&st, &ft);
                int64_t ticks = (((int64_t)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
                return DateTimeOffset(ticks + 504911232000000000LL);
#else
                // Simple implementation for local now on POSIX
                return GetUtcNow(); // TODO: Add offset
#endif
            }

            int64_t GetTimestamp() const override {
#if defined(_WIN32)
                LARGE_INTEGER li;
                QueryPerformanceCounter(&li);
                return li.QuadPart;
#else
                auto now = std::chrono::steady_clock::now();
                return std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
#endif
            }

            int64_t GetTimestampFrequency() const override {
#if defined(_WIN32)
                LARGE_INTEGER li;
                QueryPerformanceFrequency(&li);
                return li.QuadPart;
#else
                return 1000000000LL; // Nanoseconds
#endif
            }
        };

        TimeSpan TimeProvider::GetElapsedTime(int64_t startingTimestamp) const {
            return GetElapsedTime(startingTimestamp, GetTimestamp());
        }

        TimeSpan TimeProvider::GetElapsedTime(int64_t startingTimestamp, int64_t endingTimestamp) const {
            double ticks = (double)(endingTimestamp - startingTimestamp) * TimeSpan::TicksPerSecond / GetTimestampFrequency();
            return TimeSpan((int64_t)ticks);
        }

        TimeProviderPtr TimeProvider::GetSystem() {
            static auto system = SmartPointer<SystemTimeProvider>::NewShared();
            return system.template DynamicCast<TimeProvider>();
        }
    }
}

Generated by OpenCppCoverage (Version: 0.9.9.0)