/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include "system-precomp.h"	 // Precompiled headers
//
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/time.h>
#include <time.h>
#endif

#include <mrpt/core/exceptions.h>
#include <mrpt/system/CTicTac.h>

#include <cassert>
#include <cstring>
#include <type_traits>

// For Windows: get the common code out of CTicTac so it's only run once!
#ifdef _WIN32
struct AuxWindowsTicTac
{
	static AuxWindowsTicTac& GetInstance() noexcept
	{
		static AuxWindowsTicTac obj;
		return obj;
	}

	double dbl_period;

   private:
	LARGE_INTEGER m_freq;

	AuxWindowsTicTac() noexcept : dbl_period(.0)
	{
		QueryPerformanceFrequency(&m_freq);
		assert(m_freq.QuadPart != 0);
		dbl_period = 1.0 / static_cast<double>(m_freq.QuadPart);
	}
};

#endif

using namespace mrpt::system;

// Macros for easy access to memory with the correct types:
#ifdef _WIN32
#define LARGE_INTEGER_NUMS reinterpret_cast<LARGE_INTEGER*>(largeInts)
#define LARGE_INTEGER_NUMS_CONST                                               \
	reinterpret_cast<const LARGE_INTEGER*>(largeInts)
#else
#define TIMEVAL_NUMS reinterpret_cast<struct timespec*>(largeInts)
#define TIMEVAL_NUMS_CONST reinterpret_cast<const struct timespec*>(largeInts)
#endif

CTicTac::CTicTac() noexcept
{
	::memset(largeInts, 0, sizeof(largeInts));

#ifdef _WIN32
	static_assert(
		sizeof(largeInts) >= 1 * sizeof(LARGE_INTEGER),
		"sizeof(LARGE_INTEGER) failed!");
#else
	static_assert(
		sizeof(largeInts) >= 1 * sizeof(struct timespec),
		"sizeof(struct timespec) failed!");
#endif
	Tic();
}

void CTicTac::Tic() noexcept
{
#ifdef _WIN32
	LARGE_INTEGER* l = LARGE_INTEGER_NUMS;
	QueryPerformanceCounter(&l[0]);
#else
	auto* ts = TIMEVAL_NUMS;
	clock_gettime(CLOCK_MONOTONIC, &ts[0]);
#endif
}

double CTicTac::Tac() const noexcept
{
#ifdef _WIN32
	const LARGE_INTEGER* l = LARGE_INTEGER_NUMS_CONST;
	LARGE_INTEGER l1;
	QueryPerformanceCounter(&l1);
	return (l1.QuadPart - l[0].QuadPart) *
		AuxWindowsTicTac::GetInstance().dbl_period;
#else
	struct timespec ts1;
	const auto* ts = TIMEVAL_NUMS_CONST;
	clock_gettime(CLOCK_MONOTONIC, &ts1);
	return static_cast<double>(ts1.tv_sec - ts[0].tv_sec) +
		1e-9 * static_cast<double>(ts1.tv_nsec - ts[0].tv_nsec);
#endif
}
