#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;

class shared_mutex {
public:
    shared_mutex(): numberOfWriters(0), numberOfReaders(0), waitingWriters(0) {
    }
    void lock() {
        unique_lock<mutex> lock(m);
        ++waitingWriters;
        while (numberOfWriters == 1 || numberOfReaders > 0) {
            okToWrite.wait(lock);
        }
        --waitingWriters;
        ++numberOfWriters;
    }
    void unlock() {
        lock_guard<mutex> lock(m);
        --numberOfWriters;
        if (waitingWriters > 0) {
            okToWrite.notify_one();
        }
        else {
            okToRead.notify_all();
        }
    }
    void lock_shared() {
        unique_lock<mutex> lock(m);
        while (numberOfWriters == 1 || waitingWriters > 0) {
            okToRead.wait(lock);
        }
        ++numberOfReaders;
    }
    void unlock_shared() {
        lock_guard<mutex> lock(m);
        --numberOfReaders;
        if (numberOfReaders == 0 && waitingWriters > 0) {
            okToWrite.notify_one();
        }
    }
private:
    int numberOfWriters, numberOfReaders, waitingWriters;
    mutex m;
    condition_variable okToWrite, okToRead;
};

shared_mutex m;
mutex mCout;
typedef lock_guard<mutex> guard;

void task(int id, int delay, int read, int write) {
    if (delay > 0) {
        this_thread::sleep_for(chrono::seconds(delay));
    }
    { 
        guard g(mCout); 
        cout << "Thread " << id << " wil lezen" << endl;
    }
    m.lock_shared();
    { 
        guard g(mCout); 
        cout << "Thread " << id << " leest" << endl;
    }
    if (read > 0) {
        this_thread::sleep_for(chrono::seconds(read));
    }
    { 
        guard g(mCout); 
        cout << "Thread " << id << " stopt met lezen" << endl;
    }
    m.unlock_shared();
    { 
        guard g(mCout); 
        cout << "Thread " << id << " wil schrijven" << endl;
    }
    m.lock();
    { 
        guard g(mCout); 
        cout << "Thread " << id << " schrijft" << endl;
    }
    if (write > 0) {
        this_thread::sleep_for(chrono::seconds(write));
    }
    { 
        guard g(mCout); 
        cout << "Thread " << id << " stopt met schrijven" << endl;
    }
    m.unlock();
}

int main() {
    thread t1(&task, 1, 1, 3, 3);
    thread t2(&task, 2, 1, 3, 3);
    thread t3(&task, 3, 2, 0, 3);
    thread t4(&task, 4, 3, 3, 3);
    this_thread::sleep_for(chrono::milliseconds(500));
    for (int i = 1; i <= 20; ++i) {
        { 
            guard g(mCout); 
            cout << i << endl;
        }
        this_thread::sleep_for(chrono::seconds(1));
    }
    t1.join(); t2.join(); t3.join(); t4.join();
    cin.get();
    return 0; 
}
