#include "stopwatch.h"

#include <cmath>
#include <sstream>

#include <boost/date_time/posix_time/posix_time.hpp>

Stopwatch::Stopwatch() : _running(false), _sum(boost::posix_time::seconds(0))
{
}

Stopwatch::Stopwatch(bool start) :
	_running(start),
	_startTime(boost::posix_time::microsec_clock::local_time()),
	_sum(boost::posix_time::seconds(0))
{
}

Stopwatch::~Stopwatch()
{
}

void Stopwatch::Start()
{
	if(!_running) {
		_startTime = boost::posix_time::microsec_clock::local_time();
		_running = true;
	}
}

void Stopwatch::Pause()
{
	if(_running) {
		_sum += (boost::posix_time::microsec_clock::local_time() - _startTime);
		_running = false;
	}
}

void Stopwatch::Reset(bool start)
{
	_running = false;
	_sum = boost::posix_time::seconds(0);
	if(start)
		Start();
}

std::string Stopwatch::ToString() const
{
	if(_running) {
		boost::posix_time::time_duration current = _sum + (boost::posix_time::microsec_clock::local_time() - _startTime);
		return to_simple_string(current);
	} else {
		return to_simple_string(_sum);
	}
}

std::string Stopwatch::ToShortString() const
{
	const long double seconds = Seconds();
	if(seconds >= 60*60*24)
		return ToDaysString();
	else if(seconds >= 60*60)
		return ToHoursString();
	else if(seconds >= 60)
		return ToMinutesString();
	else if(seconds >= 1.0)
		return ToSecondsString();
	else if(seconds >= 0.001)
		return ToMilliSecondsString();
	else if(seconds >= 0.000001)
		return ToMicroSecondsString();
	else
		return ToNanoSecondsString();
}

std::string Stopwatch::ToDaysString() const
{
	const long double days = roundl(Seconds() / (60.0*60.0))/24.0;
	std::stringstream str;
	if(days >= 10.0)
		str << roundl(days) << " days";
	else
		str << floorl(days) << 'd' << (days*24.0) << 'h';
	return str.str();
}

std::string Stopwatch::ToHoursString() const
{
	const long double hours = roundl(Seconds() / 60.0)/60.0;
	std::stringstream str;
	if(hours >= 10.0)
		str << roundl(hours) << 'h';
	else
		str << floorl(hours) << 'h' << (hours*60.0) << 'm';
	return str.str();
}

std::string Stopwatch::ToMinutesString() const
{
	const long double mins = roundl(Seconds())/60.0;
	std::stringstream str;
	if(mins >= 10.0)
		str << roundl(mins) << " min";
	else
		str << floorl(mins) << 'm' << fmod(mins*60.0,60.0) << 's';
	return str.str();
}

std::string Stopwatch::ToSecondsString() const
{
	const long double seconds = roundl(Seconds()*10.0)/10.0;
	std::stringstream str;
	if(seconds >= 10.0)
		str << roundl(Seconds()) << 's';
	else
		str << seconds << 's';
	return str.str();
}

std::string Stopwatch::ToMilliSecondsString() const
{
	const long double msec = roundl(Seconds()*10000.0)/10.0;
	std::stringstream str;
	if(msec >= 10.0)
		str << roundl(Seconds()*1000.0) << "ms";
	else
		str << msec << "ms";
	return str.str();
}

std::string Stopwatch::ToMicroSecondsString() const
{
	const long double usec = roundl(Seconds()*10000000.0)/10.0;
	std::stringstream str;
	if(usec >= 10.0)
		str << roundl(Seconds()*1000000.0) << "µs";
	else
		str << usec << "µs";
	return str.str();
}

std::string Stopwatch::ToNanoSecondsString() const
{
	const long double nsec = roundl(Seconds()*10000000000.0)/10.0;
	std::stringstream str;
	if(nsec >= 10.0)
		str << roundl(Seconds()*1000000000.0) << "ns";
	else
		str << nsec << "ns";
	return str.str();
}

long double Stopwatch::Seconds() const
{
	if(_running) {
		boost::posix_time::time_duration current = _sum + (boost::posix_time::microsec_clock::local_time() - _startTime);
		return (long double) current.total_milliseconds()/1000.0;
	} else {
		return (long double) _sum.total_milliseconds()/1000.0;
	}
}
