#pragma once

#include <chrono>
#include <iostream>
#include <iomanip>

namespace psemek::util
{

	namespace detail
	{

		template <typename Duration, typename UpTo>
		struct pretty_print_time_wrapper
		{
			Duration d;
			UpTo up_to;
		};

		void pretty_print_time(std::ostream & o, std::int64_t d, std::int64_t up_to);

		template <typename Duration, typename UpTo>
		std::ostream & operator << (std::ostream & o, pretty_print_time_wrapper<Duration, UpTo> t)
		{
			std::int64_t const d = std::chrono::duration_cast<std::chrono::nanoseconds>(t.d).count();
			std::int64_t const up_to = std::chrono::duration_cast<std::chrono::nanoseconds>(t.up_to).count();
			pretty_print_time(o, d, up_to);
			return o;
		}

		struct pretty_print_memsize_wrapper
		{
			std::size_t value;
		};

		inline std::ostream & operator << (std::ostream & o, pretty_print_memsize_wrapper m)
		{
			if (m.value < (1 << 10))
				return o << m.value << " b";
			else if (m.value < (1 << 20))
				return o << std::setprecision(3) << (m.value * 1.f / (1 << 10)) << " Kb";
			else if (m.value < (1 << 30))
				return o << std::setprecision(3) << (m.value * 1.f / (1 << 20)) << " Mb";
			else
				return o << std::setprecision(3) << (m.value * 1.f / (1 << 30)) << " Gb";
		}

	}

	template <typename Rep, typename Period, typename UpTo>
	auto pretty(std::chrono::duration<Rep, Period> d, UpTo up_to)
	{
		return detail::pretty_print_time_wrapper<std::chrono::duration<Rep, Period>, UpTo>{d, up_to};
	}

	template <typename Rep, typename Period>
	auto pretty(std::chrono::duration<Rep, Period> d)
	{
		return pretty(d, std::chrono::seconds{1});
	}

	inline auto pretty_memsize(std::size_t value)
	{
		return detail::pretty_print_memsize_wrapper{value};
	}

}
