#include "sysconfig.h"
#include "sysdeps.h"
#include "uae/time.h"
#include "options.h"
#include "events.h"
#include "uae.h"

#ifdef _WIN32

#include <process.h>

static int userdtsc = 0;
static int qpcdivisor = 0;
static SYSTEM_INFO si;

static frame_time_t read_processor_time_qpf(void)
{
	LARGE_INTEGER counter;
	frame_time_t t;
	QueryPerformanceCounter(&counter);
	if (qpcdivisor == 0)
		t = (frame_time_t) (counter.QuadPart);
	else
		t = (frame_time_t) (counter.QuadPart >> qpcdivisor);
	if (!t)
		t++;
	return t;
}

uae_s64 read_processor_time_rdtsc(void)
{
#ifdef __arm__
	return read_processor_time_qpf();
#else
	return __rdtsc();
#endif
}

uae_time_t uae_time(void)
{
	uae_time_t t;
#if 0
	static int cnt;

	cnt++;
	if (cnt > 1000000) {
		write_log(_T("**************\n"));
		cnt = 0;
	}
#endif
	if (userdtsc)
		t = read_processor_time_rdtsc();
	else
		t = read_processor_time_qpf();
	return t;
}

uae_s64 read_system_time(void)
{
	return GetTickCount64();
}

static volatile int dummythread_die;

static void _cdecl dummythread(void *dummy)
{
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
	while (!dummythread_die);
}

static uae_s64 win32_read_processor_time(void)
{
#if defined(X86_MSVC_ASSEMBLY)
	uae_u32 foo, bar;
	__asm
	{
		cpuid
			rdtsc
			mov foo, eax
			mov bar, edx
	}
	return (((uae_s64)bar) << 32) | foo;
#else
	return 0;
#endif
}

static void figure_processor_speed_rdtsc(void)
{
	static int freqset;
	frame_time_t clockrate;
	int oldpri;
	HANDLE th;

	if (freqset)
		return;
	th = GetCurrentThread ();
	freqset = 1;
	oldpri = GetThreadPriority(th);
	SetThreadPriority(th, THREAD_PRIORITY_HIGHEST);
	dummythread_die = -1;
	_beginthread(&dummythread, 0, 0);
	sleep_millis(500);
	clockrate = win32_read_processor_time();
	sleep_millis(500);
	clockrate = (win32_read_processor_time() - clockrate) * 2;
	dummythread_die = 0;
	SetThreadPriority(th, oldpri);
	write_log(_T("CLOCKFREQ: RDTSC %.2fMHz\n"), clockrate / 1000000.0);
	syncbase = clockrate >> 6;
}

static void figure_processor_speed_qpf(void)
{
	LARGE_INTEGER freq;
	static LARGE_INTEGER freq2;
	uae_u64 qpfrate;

	if (!QueryPerformanceFrequency (&freq))
		return;
	if (freq.QuadPart == freq2.QuadPart)
		return;
	freq2.QuadPart = freq.QuadPart;
	qpfrate = freq.QuadPart;
	/* limit to 10MHz */
	qpcdivisor = 0;
	while (qpfrate > 10000000) {
		qpfrate >>= 1;
		qpcdivisor++;
	}
	write_log(_T("CLOCKFREQ: QPF %.2fMHz (%.2fMHz, DIV=%d)\n"),
		  freq.QuadPart / 1000000.0,
		  qpfrate / 1000000.0, 1 << qpcdivisor);
	syncbase = qpfrate;
}

void uae_time_calibrate(void)
{
	if (userdtsc) {
		figure_processor_speed_rdtsc();
	}
	if (!userdtsc) {
		figure_processor_speed_qpf();
	}
}

void uae_time_use_rdtsc(bool enable)
{
	userdtsc = enable;
}

#elif defined(USE_GLIB)

#include <glib.h>

static gint64 epoch;

uae_time_t uae_time(void)
{
	return (uae_time_t) g_get_monotonic_time();
}

void uae_time_calibrate(void)
{

}

#endif

void uae_time_init(void)
{
	static bool initialized = false;
	if (initialized) {
		return;
	}
#ifdef _WIN32
	GetSystemInfo(&si);
#endif
	uae_time_calibrate();
	initialized = true;
}
