#ifndef STOPWATCH_HPP_
#define STOPWATCH_HPP_

#include <cstdint> //Standard integer typen wie uint64_t
#include <chrono> //Zeitmessung
#include <cassert> //Assertions
#include <string> //Strings
#include <iostream> //Ausgabe
#include <sstream> //Stringstreams zum Bauen von Strings

/// Klasse zur einfachen Zeitmessung
class Stopwatch {
public:
   typedef std::chrono::microseconds microseconds;

private:
   typedef std::chrono::high_resolution_clock clock_type;

public:
   Stopwatch(bool autoStart = false)
    : running_(false)
   {
      reset();
      if(autoStart){
         start();
      }
   }

   inline void reset()
   {
      count_ = microseconds::zero();
      if (running_) {
         start();
      }
   }

   inline void start()
   {
      assert (!running_);
      running_ = true;
      start_ = clock_type::now();
   }

   inline void stop()
   {
      assert (running_);
      clock_type::time_point end_ = clock_type::now();
      count_ += std::chrono::duration_cast<std::chrono::microseconds>(end_ - start_);
      running_ = false;
   }

   inline microseconds total_us() const
   {
      assert (!running_);
      return count_;
   }

   static std::string zeroFill(uint64_t t, int numDigits){
      std::stringstream ss;
      uint64_t j = 10;
      for(int i = 1; i < numDigits ; i++){
         if(t < j){
            ss << "0";
         }
         j *= 10;
      }
      ss << t;
      return ss.str();
   }

   static std::string microSecsToString(uint64_t mcs)
   {
      static const char* suffixes[]
            = { "mcs", "ms", "s" };

      unsigned i;
      uint64_t d = mcs;
      uint64_t reminder = mcs;
      for (i = 0; i < 3; i++) {
         if (d < 1000)
            break;
         reminder = d % 1000;
         d /= 1000;
      }
      std::ostringstream ost;
      ost << d << '.' << zeroFill(reminder/10,2) << ' ' << suffixes[i];
      return ost.str();
   }


   friend std::ostream& operator << (std::ostream &out, const Stopwatch &stats)
   {
      out << microSecsToString(stats.total_us().count());
      return out;
   }

private:
   microseconds count_;
   clock_type::time_point start_;
   bool running_;
};

#endif
