/*
    libmaus2
    Copyright (C) 2009-2013 German Tischler
    Copyright (C) 2011-2013 Genome Research Limited

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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, see <http://www.gnu.org/licenses/>.
*/


#if ! defined(LIBMAUS2_TIMING_REALTIMECLOCK_HPP)
#define LIBMAUS2_TIMING_REALTIMECLOCK_HPP

#include <libmaus2/LibMausConfig.hpp>
#include <libmaus2/types/types.hpp>
#include <sstream>
#include <iomanip>
#include <chrono>

namespace libmaus2
{
	namespace timing
	{
		struct RealTimeClockBase
		{
			virtual ~RealTimeClockBase() {}

			static std::string formatTime(double dsecs)
			{
				uint64_t days = 0;
				uint64_t hours = 0;
				uint64_t minutes = 0;
				uint64_t secs = 0;
				bool printdays = false;
				bool printhours = false;
				bool printminutes = false;
				bool printsecs = false;

				if ( static_cast<uint64_t>(dsecs) >= 60*60*24 )
				{
					days = static_cast<uint64_t>(dsecs)/(60*60*24);
					dsecs -= days*60ull*60ull*24ull;
					printdays = true;
					printhours = true;
					printminutes = true;
					printsecs = true;
				}
				if ( static_cast<uint64_t>(dsecs) >= 60*60 )
				{
					hours = static_cast<uint64_t>(dsecs)/(60*60);
					dsecs -= hours*60ull*60ull;
					printhours = true;
					printminutes = true;
					printsecs = true;
				}
				if ( static_cast<uint64_t>(dsecs) >= 60 )
				{
					minutes = static_cast<uint64_t>(dsecs)/(60);
					dsecs -= minutes*60ull;
					printminutes = true;
					printsecs = true;
				}
				if ( static_cast<uint64_t>(dsecs) >= 1 )
				{
					secs = static_cast<uint64_t>(dsecs);
					dsecs -= secs;
					printsecs = true;
				}
				std::ostringstream timestr;
				timestr << std::setfill('0');
				if ( printdays )
					timestr << days << ":";
				if ( printhours )
					timestr << std::setw(2) << hours << ":";
				if ( printminutes )
					timestr << std::setw(2) << minutes << ":";
				if ( printsecs )
					timestr << std::setw(2) << secs << ":";

				unsigned int ddigs = 0;
				unsigned int const maxddigs = 8;
				timestr << std::setw(0);
				while ( dsecs && (ddigs++ < maxddigs) )
				{
					dsecs *= 10;
					unsigned int dig = static_cast<unsigned int>(dsecs);
					timestr << dig;
					dsecs -= dig;
				}
				if ( !ddigs )
					timestr << 0;

				return timestr.str();
			}
		};

		struct RealTimeClock : public RealTimeClockBase
		{
			private:
			std::chrono::time_point<std::chrono::system_clock> started;

			public:
			RealTimeClock(bool rstart = false) : started()
			{
				if ( rstart )
					start();
			}
			~RealTimeClock() throw() {}

			bool start() throw()
			{
				started = std::chrono::system_clock::now();
				return true;
			}

			double getElapsedSeconds() const
			{
				std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
				std::chrono::duration<double> const dif = now-started;
				return dif.count();
			}
		};

		std::ostream & operator<<(std::ostream & out, RealTimeClock const & rtc);
	}
}
#endif
