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

template<typename T, int size>
class CircularBuffer {
public:
    CircularBuffer();
    void put(const T& t);  // schrijf element  in de buffer. WACHT als buffer vol is!
    const T get();         // lees een element uit de buffer. WACHT als buffer leeg is!
    bool isEmpty() const;  // vraag of het buffer leeg is
private:
//  Nog invullen!
};

template<typename T, int size>
CircularBuffer<T,size>::CircularBuffer() /*: nog invullen! */{
//  Nog invullen!
}

template<typename T, int size>
void CircularBuffer<T,size>::put(const T& t) {
//  Nog invullen!
}
    
template<typename T, int size>
const T CircularBuffer<T,size>::get() {
//  Nog invullen!
    return T(); /* dummy */
}
    
template<typename T, int size>
bool CircularBuffer<T,size>::isEmpty() const {
//  Nog invullen!
    return false; /* dummy */
}

class TestCircularBuffer {
private:
    CircularBuffer<char, 10> b; // buffer dat getest wordt
    mutex m_cout; // zorgt voor mutual excusive gebruik van cout (1 thread tegelijk)
    void producer(char c);
    void consumer();
public:
    void run();
};

void TestCircularBuffer::producer(char c) { // memberfunctie voor producer thread
    {
        lock_guard<mutex> lock(m_cout);
        cout << "Thread: " << this_thread::get_id() << " met arg: " << c << " gestart" << endl;
    }
    for (int i = 0; i < 500; ++i) {
        b.put(c);
    }
    {
        lock_guard<mutex> lock(m_cout);
        cout << "Thread: " << this_thread::get_id() << " gestopt" << endl;
    }
}

void TestCircularBuffer::consumer() { // memberfunctie voor consumer thread
    {
        lock_guard<mutex> lock(m_cout);
        cout << "Thread: " << this_thread::get_id() << " gestart" << endl;
    }
    for (int i = 0; i < 1000; ++i) {
        char c = b.get();
        {
            unique_lock<mutex> lock(m_cout);
            cout << c;
        }
    }
    {
        lock_guard<mutex> lock(m_cout);
        cout << "Thread: " << this_thread::get_id() << " gestopt" << endl;
    }
}

void TestCircularBuffer::run() { // run test
    thread tc(&TestCircularBuffer::consumer, this);
    thread tp1(&TestCircularBuffer::producer, this, 'k'); 
    thread tp2(&TestCircularBuffer::producer, this, 'f'); 
    tp2.join();
    tp1.join();
    tc.join();
    if (!b.isEmpty()) {
        lock_guard<mutex> lock(m_cout);
        cout << "Error: buffer is niet leeg!" << endl;
    }
}

int main() {
    TestCircularBuffer test;
    test.run();
    cin.get();
    return 0; 
}