#ifndef TIME_HPP
#define TIME_HPP

#include <unordered_map>
#include <chrono>

namespace OpenEngine {
    using highres_time_point = std::chrono::time_point<std::chrono::high_resolution_clock>;

    class Time
    {
        public:
            Time(const Time&) = delete;
            Time& operator=(const Time&) = delete;

            static void Update();

            static void Init()
            {
                if (instance == nullptr)
                    instance.reset(new Time);
            };

            static double DeltaTime() {
                return instance->delta_time.count();
            };

            struct ProfilingResult
            {
                double duration = 0;
                const char* name;
            };

            /* TODO: Consider:
             *
             *      Timer::Start();
             *
             *      Timer::SetMark("name");
             *      Timer::GetSinceMark("name");
             *      Timer::StopMark("name");
             *      Timer::GetMarkDuration("name");
             *
             *      Timer::Stop();
             */
            template<typename Fn>
            class Timer
            {
                public:

                    Timer(const char* name, Fn funct)
                        : name(name), func(funct)
                    {
                        start_timepoint = std::chrono::high_resolution_clock::now();
                    };
                    ~Timer()
                    {
                        if (!stopped)
                            Stop();
                    };

                    void Stop()
                    {
                        auto end_timepoint = std::chrono::high_resolution_clock::now();

                        long long start = std::chrono::time_point_cast<std::chrono::microseconds>(start_timepoint).time_since_epoch().count();
                        long long end = std::chrono::time_point_cast<std::chrono::microseconds>(end_timepoint).time_since_epoch().count();

                        stopped = true;

                        ProfilingResult result = {0.001f * (end - start), name};

                        func(result);
                    };

                private:
                    std::unordered_map<const char*, highres_time_point> time_points;
                    const char* name;
                    highres_time_point start_timepoint;
                    bool stopped = false;
                    Fn func;
            };

        private:
            Time() {};

        private:
            static std::unique_ptr<Time> instance;

            std::chrono::high_resolution_clock::time_point previous_frame;
            std::chrono::duration<double> delta_time;
    };
}

#endif // TIME_HPP
