#ifndef TILE_BASE_THREAD_LATCH_H
#define TILE_BASE_THREAD_LATCH_H

#pragma once
#include "tile/base/internal/logging.h"
#include "tile/base/thread/cond_var.h"
#include "tile/base/thread/mutex.h"
#include "tile/base/thread/unique_lock.h"

namespace tile {
class Latch {
public:
    explicit Latch(std::ptrdiff_t count);
    void CountDown(std::ptrdiff_t update = 1);
    bool TryWait() const noexcept;
    void Wait() const;

    template<typename Rep, typename Period>
    bool WaitFor(const std::chrono::duration<Rep, Period> &timeout) const
    {
        UniqueLock<Mutex> lk(m_);
        TILE_CHECK_GE(count_, 0);
        return cv_.WaitFor(lk, timeout, [this] { return count_ == 0; });
    }

    template<typename Clock, typename Duration>
    bool WaitUntil(const std::chrono::time_point<Clock, Duration> &abs_time) const
    {
        UniqueLock<Mutex> lk(m_);
        TILE_CHECK_GE(count_, 0);
        return cv_.WaitUntil(lk, abs_time, [this] { return count_ == 0; });
    }

    void ArriveAndWait(std::ptrdiff_t update = 1);

private:
    mutable Mutex m_;
    mutable CondVar cv_;
    std::ptrdiff_t count_;
};
}// namespace tile

#endif// TILE_BASE_THREAD_LATCH_H
