#pragma once

#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))

#define PFC_HAVE_PROFILER

#include <intrin.h>
namespace pfc {
	class profiler_static {
	public:
		profiler_static(const char * p_name);
		~profiler_static();
		void add_time(t_int64 delta) { total_time += delta;num_called++; }
	private:
		const char * name;
		t_uint64 total_time, num_called;
	};

	class profiler_local {
	public:
		profiler_local(profiler_static * p_owner) {
			owner = p_owner;
			start = __rdtsc();
		}
		~profiler_local() {
			t_int64 end = __rdtsc();
			owner->add_time(end - start);
		}
	private:
		t_int64 start;
		profiler_static * owner;
	};

}
#define profiler(name) \
	static pfc::profiler_static profiler_static_##name(#name); \
	pfc::profiler_local profiler_local_##name(&profiler_static_##name);
	

#endif

#ifdef _WIN32

namespace pfc {
#if _WIN32_WINNT >= 0x600
typedef uint64_t tickcount_t;
inline tickcount_t getTickCount() { return GetTickCount64(); }
#else
#define PFC_TICKCOUNT_32BIT
typedef uint32_t tickcount_t;
inline tickcount_t getTickCount() { return GetTickCount(); }
#endif

class hires_timer {
public:
    hires_timer() : m_start() {}
	void start() {
		m_start = g_query();
	}
	double query() const {
		return _query( g_query() );
	}
	double query_reset() {
		t_uint64 current = g_query();
		double ret = _query(current);
		m_start = current;
		return ret;
	}
	pfc::string8 queryString(unsigned precision = 6) const {
		return pfc::format_time_ex( query(), precision ).get_ptr();
	}
private:
	double _query(t_uint64 p_val) const {
		return (double)( p_val - m_start ) / (double) g_query_freq();
	}
	static t_uint64 g_query() {
		LARGE_INTEGER val;
		if (!QueryPerformanceCounter(&val)) throw pfc::exception_not_implemented();
		return val.QuadPart;
	}
	static t_uint64 g_query_freq() {
		LARGE_INTEGER val;
		if (!QueryPerformanceFrequency(&val)) throw pfc::exception_not_implemented();
		return val.QuadPart;
	}
	t_uint64 m_start;
};

class lores_timer {
public:
    lores_timer() : m_start() {}
	void start() {
		_start(getTickCount());
	}

	double query() const {
		return _query(getTickCount());
	}
	double query_reset() {
		tickcount_t time = getTickCount();
		double ret = _query(time);
		_start(time);
		return ret;
	}
	pfc::string8 queryString(unsigned precision = 3) const {
		return pfc::format_time_ex( query(), precision ).get_ptr();
	}
private:
	void _start(tickcount_t p_time) {
#ifdef PFC_TICKCOUNT_32BIT
		m_last_seen = p_time;
#endif
		m_start = p_time;
	}
	double _query(tickcount_t p_time) const {
#ifdef PFC_TICKCOUNT_32BIT
		t_uint64 time = p_time;
		if (time < (m_last_seen & 0xFFFFFFFF)) time += 0x100000000;
		m_last_seen = (m_last_seen & 0xFFFFFFFF00000000) + time;
		return (double)(m_last_seen - m_start) / 1000.0;
#else
		return (double)(p_time - m_start) / 1000.0;
#endif
	}
	t_uint64 m_start;
#ifdef PFC_TICKCOUNT_32BIT
	mutable t_uint64 m_last_seen;
#endif
};
}
#else  // not _WIN32

namespace pfc {
    
class hires_timer {
public:
    hires_timer() : m_start() {}
    void start();
    double query() const;
    double query_reset();
	pfc::string8 queryString(unsigned precision = 3) const;
private:
    double m_start;
};

typedef hires_timer lores_timer;

}

#endif // _WIN32

#ifndef _WIN32
struct timespec;
#endif

namespace pfc {
	uint64_t fileTimeWtoU(uint64_t ft);
	uint64_t fileTimeUtoW(uint64_t ft);
#ifndef _WIN32
    uint64_t fileTimeUtoW( timespec const & ts );
#endif
	uint64_t fileTimeNow();
}
