#ifndef jf_timer_h
#define jf_timer_h

#include <chrono>
#include <functional>
#include <iostream>
#include <map>

namespace jf {

using timePoint = std::chrono::system_clock::time_point;
using timeDuration = std::chrono::system_clock::duration;
#define timeDurationZero std::chrono::system_clock::duration::zero
#define timeNow std::chrono::system_clock::now

class Timer {
   public:
    static void LoopOnceMannually() {
        timePoint frameStart = timeNow();
        for (const auto& pair : loopFuncs) {
            std::size_t last_duration_in_nano = m_tdLastDuration.count();
            double last_duration_in_second = double(last_duration_in_nano) / 1000000000;
            pair.second(last_duration_in_second);
        };
        m_tdLastDuration = timeNow() - frameStart;
    }
    static void StartLoop() {
        m_bLooping = true;
        while (m_bLooping) {
            LoopOnceMannually();
        }
    }
    static void StopLoop() { m_bLooping = false; }

    static int RegistLoopFunc(const std::function<void(double)>& in) {
        int ret = m_nHandleIndex;
        loopFuncs.insert(std::make_pair(m_nHandleIndex++, in));
        return ret;
    }

    static void UnregistLoopFunc(int handle) { loopFuncs.erase(handle); }

    static timePoint Now() { return timeNow(); }

    // second
    static double AsSeconds(const timeDuration& d) { return d.count() / 1000000000.0; }
    // millisecond
    static double AsMilliseconds(const timeDuration& d) { return d.count() / 1000000.0; }
    // microsecond
    static double AsMicroseconds(const timeDuration& d) { return d.count() / 1000.0; }
    // nanosecond
    static uint64_t AsNanoseconds(const timeDuration& d) { return d.count(); }

   private:
    static std::map<int, std::function<void(double)>> loopFuncs;
    static bool m_bLooping;
    static timeDuration m_tdLastDuration;
    static int m_nHandleIndex;
};

}  // namespace jf

#endif