#ifndef TILE_BASE_THREAD_RW_MUTEX_H
#define TILE_BASE_THREAD_RW_MUTEX_H

#include <memory>

#pragma once

namespace tile {
namespace detail {
namespace mutex {

class RWMutexReadRefWrapper;
class RWMutexWriteRefWrapper;

}// namespace mutex
}// namespace detail

class RWMutex final {
public:
    enum PriorityPolicy { Unspecified, ReadPreferring, WritePreferring };

    RWMutex(PriorityPolicy policy = Unspecified);

    RWMutex(const RWMutex &) = delete;
    RWMutex(RWMutex &&)      = delete;

    void WriteLock();
    bool WriteTryLock();
    void WriteUnlock();

    void ReadLock();
    bool ReadTryLock();
    void ReadUnlock();

    detail::mutex::RWMutexReadRefWrapper &ReadMutex() const;
    detail::mutex::RWMutexWriteRefWrapper &WriteMutex() const;

private:
    class Impl;
    std::unique_ptr<Impl> impl_;
};

namespace detail {
namespace mutex {

class RWMutexReadRefWrapper {
public:
    inline constexpr RWMutexReadRefWrapper(RWMutex &m) : m_(m) {}

    inline void Lock() { m_.ReadLock(); }

    inline bool TryLock() { return m_.ReadTryLock(); }

    inline void Unlock() { m_.ReadUnlock(); }

private:
    RWMutex &m_;
};

class RWMutexWriteRefWrapper {
public:
    inline constexpr RWMutexWriteRefWrapper(RWMutex &m) : m_(m) {}

    inline void Lock() { m_.WriteLock(); }

    inline bool TryLock() { return m_.WriteTryLock(); }

    inline void Unlock() { m_.WriteUnlock(); }

private:
    RWMutex &m_;
};

}// namespace mutex
}// namespace detail

}// namespace tile

#endif// TILE_BASE_THREAD_RW_MUTEX_H
