#include "tile/base/thread/cond_var.h"

#include "tile/base/internal/logging.h"
#include "tile/base/make_unique.h"
#include "tile/base/thread/mutex.h"

#include <condition_variable>

namespace tile {

namespace detail {
namespace cond_var {

class CondVarProxy::Impl {
public:
    Impl() {}

    ~Impl() {}

    void NotifyOne() { cv_.notify_one(); }

    void NotifyAll() { cv_.notify_all(); }

    void Wait(Mutex *m)
    {
        std::mutex *p = reinterpret_cast<std::mutex *>(m->GetNativeHandle());
        TILE_CHECK(p);
        std::unique_lock<std::mutex> std_lock(*p, std::adopt_lock);
        cv_.wait(std_lock);
        std_lock.release();
    }

    bool WaitFor(Mutex *m, std::size_t nanoseconds)
    {
        std::mutex *p = static_cast<std::mutex *>(m->GetNativeHandle());
        TILE_CHECK(p);

        std::unique_lock<std::mutex> std_lock(*p, std::adopt_lock);
        bool res = cv_.wait_for(std_lock, std::chrono::nanoseconds(nanoseconds)) == std::cv_status::no_timeout;
        std_lock.release();
        return res;
    }

private:
    std::condition_variable cv_;
};

CondVarProxy::CondVarProxy() : impl_(make_unique<Impl>()) {}

CondVarProxy::~CondVarProxy() {}

void
CondVarProxy::Wait(Mutex *mutex)
{
    impl_->Wait(mutex);
}

bool
CondVarProxy::WaitFor(Mutex *mutex, std::size_t nanoseconds)
{
    return impl_->WaitFor(mutex, nanoseconds);
}

void
CondVarProxy::NotifyAll()
{
    impl_->NotifyAll();
}

void
CondVarProxy::NotifyOne()
{
    impl_->NotifyOne();
}

}// namespace cond_var
}// namespace detail
}// namespace tile
