/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2 
	as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


	Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#include "specifics.h"
#include "sys_cpu.h"
#include "sys_clock.h"
#include "sys_log.h"

#ifdef LINUX
	#include <sys/time.h>
#endif

namespace sys {
	float64_t laps_t::freq_inv;


	void laps_t::bootstrap() {
		#ifdef USE_PERFCOUNTERS
			LARGE_INTEGER f;
			QueryPerformanceFrequency(&f);

			freq_inv = 1000./double(f.QuadPart);
		#elif defined USE_RDTSC
			//freq_inv = 1000. / sys::get_cpu_frequency();
			freq_inv = 1000. / sys::cpu::cpu_frequency;
		#else
			#error not implemented.
		#endif
	}

	namespace wallclock {
		#ifdef WINDOWS
			static float64_t last = 0, reference = -1, granularity = 0;
		#else
		static float64_t last = 0, reference = -1;
		#endif

		float64_t get() {
			#ifdef WINDOWS
				/*
					Allright, allright,
					there's some weird interactions when the process gets binded to a cpu
					and stuff like that, the end result is that we seem to warp back in the past.

					We get a 64bit value in 100 nanosec units which has a >10ms granularity (more like 15ms here).
				*/
				union { FILETIME ft; uint64_t u; } time;
				GetSystemTimeAsFileTime(&time.ft);
				const float64_t current = float64_t(time.u) * 100e-9;	// back to seconds
				reference = reference < 0. ? current : reference;
				if (last > current)
					return last;	// kludge
				else {
					last = current;
					return current;
				}
			#elif defined LINUX
				timeval tv;
				gettimeofday(&tv, 0);
				const float64_t current = float64_t(tv.tv_sec) + float64_t(tv.tv_usec)*1e-6;
				reference = reference < 0. ? current : reference;
				if (last > current)
					return last;	// kludge
				else {
					last = current;
					return current;
				}
			#else
				#error not implemented.
			#endif
		}
	}


	namespace clock {
		void bootstrap() {
			// careful with the logging
			sys::laps_t::bootstrap();

			// sys::wallclock::reference = sys::wallclock::get();
			#ifdef WINDOWS
				DWORD adjustment = 0, clockInterval = 0;
				BOOL  adjustmentDisabled = 0;
				GetSystemTimeAdjustment(&adjustment, &clockInterval, &adjustmentDisabled);
				sys::wallclock::granularity = double(clockInterval) * 100e-9; // 10000.;
				sys::log("CLOCK: low res timer precision %.03f ms \n", sys::wallclock::granularity * 1000.);
			#endif
		}
	}
}

