#ifndef TILE_BASE_THREAD_EVENT_H
#define TILE_BASE_THREAD_EVENT_H

#pragma once

#include "tile/base/thread/mutex.h"
#include "tile/base/thread/scoped_lock.h"

namespace tile {
class Event {
public:
    enum Mode { ManualReset, AutoReset };

    explicit Event(Mode mode = ManualReset) : mode_(mode), signaled_(false) {}

    inline void Set()
    {
        ScopedLock _(m_);
        signaled_ = true;
        cv_.NotifyAll();
    }

    inline void Reset()
    {
        ScopedLock _(m_);
        signaled_ = false;
    }

    inline void Wait()
    {
        ScopedLock _(m_);
        cv_.Wait(_, [this] { return signaled_; });
        ResetIfNeeded();
    }

    template<typename Rep, typename Period>
    bool WaitFor(const std::chrono::duration<Rep, Period> &timeout)
    {
        ScopedLock _(m_);
        if (cv_.WaitFor(m_, timeout, [this] { return signaled_; })) {
            ResetIfNeeded();
            return true;
        }
        return false;
    }

    template<typename Clock, typename Duration>
    bool WaitUntil(const std::chrono::time_point<Clock, Duration> &timeout_time)
    {
        ScopedLock _(m_);
        if (cv_.WaitUntil(_, timeout_time, [this] { return signaled_; })) {
            ResetIfNeeded();
            return true;
        }
        return false;
    }

private:
    inline void ResetIfNeeded()
    {
        if (mode_ == AutoReset) { signaled_ = false; }
    }

    const Mode mode_;
    bool signaled_;
    Mutex m_;
    CondVar cv_;
};
}// namespace tile

#endif// TILE_BASE_THREAD_EVENT_H
