#ifndef OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H
#define OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H

#include <chrono>
#include <thread>

namespace Misc
{
    class FrameRateLimiter
    {
    public:
        template <class Rep, class Ratio>
        explicit FrameRateLimiter(std::chrono::duration<Rep, Ratio> maxFrameDuration,
            std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
            : mMaxFrameDuration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(maxFrameDuration))
            , mLastMeasurement(now)
            , mLastFrameDuration(0)
        {
        }

        std::chrono::steady_clock::duration getLastFrameDuration() const { return mLastFrameDuration; }

        void limit(std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
        {
            const auto passed = now - mLastMeasurement;
            const auto left = mMaxFrameDuration - passed;
            if (left > left.zero())
            {
                std::this_thread::sleep_for(left);
                mLastMeasurement = now + left;
                mLastFrameDuration = mMaxFrameDuration;
            }
            else
            {
                mLastMeasurement = now;
                mLastFrameDuration = passed;
            }
        }

    private:
        std::chrono::steady_clock::duration mMaxFrameDuration;
        std::chrono::steady_clock::time_point mLastMeasurement;
        std::chrono::steady_clock::duration mLastFrameDuration;
    };

    inline Misc::FrameRateLimiter makeFrameRateLimiter(float frameRateLimit)
    {
        if (frameRateLimit > 0.0f)
            return Misc::FrameRateLimiter(std::chrono::duration<float>(1.0f / frameRateLimit));
        else
            return Misc::FrameRateLimiter(std::chrono::steady_clock::duration::zero());
    }
}

#endif
