#include "config.h"

// Implementation
#include "Time.hpp"

// STDC++
#include <cassert>
#include <limits>

// Local
#include "Sys.hpp"
#include "Text.hpp"

namespace UI {
namespace Util {

long int getTimeValSec()
{
	struct timeval tv;
	Sys::gettimeofday(&tv, 0);
	return tv.tv_sec;
}

long int getTimeValUSec()
{
	struct timeval tv;
	Sys::gettimeofday(&tv, 0);
	return tv.tv_usec;
}

unsigned int nanosleep(unsigned int seconds, long int nanoseconds)
{
	assert(nanoseconds >= 0);
#ifndef WIN32
	timespec sleep;
	sleep.tv_sec = seconds;
	sleep.tv_sec += nanoseconds / 1000000000;
	sleep.tv_nsec = nanoseconds % 1000000000;
	return ::nanosleep(&sleep, 0);
#else
	int nMS = seconds * 1000 + nanoseconds / 1000000;
	Sleep(nMS);
	return 0;
#endif
}

unsigned int nssleep(unsigned int seconds)
{
	return nanosleep(seconds, 0);
}

unsigned int nsnsleep(long int nanoseconds)
{
	return nanosleep(0, nanoseconds);
}


long int const RealTimeStamp::tenExpSix_(1000000);

RealTimeStamp::RealTimeStamp(long int const & sec, long int const & usec)
{
	set(sec, usec);
}

RealTimeStamp & RealTimeStamp::set(long int const & sec, long int const & usec)
{
	sec_ = sec;
	usec_ = usec;
	normalize();
	return *this;
}

RealTimeStamp & RealTimeStamp::setMax()
{
#if defined( max )
	#undef max
#endif
	sec_ = std::numeric_limits<long int>::max();
	usec_ = std::numeric_limits<long int>::max();
	return *this;
}

RealTimeStamp & RealTimeStamp::setMin()
{
	set(0, 0);
	return *this;
}

RealTimeStamp & RealTimeStamp::stamp()
{
	struct timeval tv;
	Sys::gettimeofday(&tv, 0);
	sec_ = tv.tv_sec;
	usec_ = tv.tv_usec;
	return *this;
}

long int RealTimeStamp::getSec() const
{
	return sec_;
}

long int RealTimeStamp::getUSec() const
{
	return usec_;
}

long double RealTimeStamp::getSeconds() const
{
	return (long double) sec_ + (long double) usec_ / tenExpSix_;
}

bool RealTimeStamp::operator == (RealTimeStamp const & rt) const
{
	return sec_ == rt.getSec() && usec_ == rt.getUSec();
}

bool RealTimeStamp::operator < (RealTimeStamp const & rt) const
{
	return sec_ < rt.sec_ || (sec_ == rt.sec_ && usec_ < rt.usec_);
}

bool RealTimeStamp::operator <= (RealTimeStamp const & rt) const
{
	return sec_ < rt.sec_ || (sec_ == rt.sec_ && usec_ <= rt.usec_);
}

RealTimeStamp & RealTimeStamp::operator += (RealTimeStamp const & rt)
{
	sec_ += rt.sec_;
	usec_ += rt.usec_;
	normalize();
	return *this;
}

RealTimeStamp RealTimeStamp::operator + (RealTimeStamp const & rt) const
{
	RealTimeStamp result(*this);
	result += rt;
	return result;
}

RealTimeStamp & RealTimeStamp::operator -= (RealTimeStamp const & rt)
{
	sec_ -= rt.sec_;
	usec_ -= rt.usec_;

	if (usec_ < 0)
	{
		sec_ += usec_ / tenExpSix_;
		usec_ = usec_ % tenExpSix_;
		if (usec_ != 0)
		{
			--sec_;
			usec_ += tenExpSix_;
		}
	}
	normalize();
	return *this;
}

RealTimeStamp RealTimeStamp::operator - (RealTimeStamp const & rt) const
{
	RealTimeStamp result(*this);
	result -= rt;
	return result;
}

void RealTimeStamp::normalize()
{
	sec_ += usec_ / tenExpSix_;
	usec_ = usec_ % tenExpSix_;

	// Never leave us with negative values; silently set to 0
	//
	// We are not imposing an exception here as this might happen
	// with a diff (s1-s0) when the system time has changed between
	// the two stamps (administrator, ntp, etc...).
	sec_ = std::max<long int>(0, sec_);
	usec_ = std::max<long int>(0, usec_);
}

std::ostream & operator << (std::ostream & os, RealTimeStamp const & rt)
{
	os << rt.getSeconds() << " seconds";
	return os;
}


ScopeRealTime::ScopeRealTime(std::ostream * const out)
	:out_(out)
{
	stamp();
}

ScopeRealTime::~ScopeRealTime()
{
	if (out_) { *out_ << "Scope real time: " << get() << std::endl; }
}

RealTimeStamp ScopeRealTime::get() const
{
	return RealTimeStamp().stamp() - *this;
}

}}
