#include <list>
#include <thread>
#include <mutex>

#include <iostream>

using namespace std;

static const int NUM = 10;

/** klasa wejscia - wyjscia do kolejki znakow */
struct Queue {
    list<int> dane_;
    mutex mutex_;
};

struct Producent {

    Producent(int id, Queue& in, Queue& out) : id_(id), in_(in), out_(out) {}

	//bledne rozwiazanie jest zakomentowane
    // void operator()() {
    //     for(int num = 0; num < NUM; ) {
    //         lock_guard<mutex> lock_in(in_.mutex_);
    //         lock_guard<mutex> lock_out(out_.mutex_);
    //         if(!in_.dane_.empty() ) {
    //             int x = in_.dane_.front();
    //             in_.dane_.pop_front();

    //             cout << '.' << flush; //symuluje przetwarzanie danych

    //             out_.dane_.push_back(x+1);
    //             ++num;
    //         }
    //     }
    // }

	// poprawne rozwiazanie
    void operator()() {
        for(int num = 0; num < NUM; ) {
            int x = 0; //data
            this_thread::sleep_for(chrono::milliseconds(20));
         cout << '.' << flush; //symuluje przetwarzanie danych
            bool read = false;
            {
                lock_guard<mutex> lock_in(in_.mutex_);
                if(!in_.dane_.empty() ) {
                    read = true;
                    x = in_.dane_.front();
                    in_.dane_.pop_front();
                }
            }
            if(read) {
                cout << id_ << ':' << x << endl; //symuluje przetwarzanie danych
                ++num;

                lock_guard<mutex> lock_out(out_.mutex_);
                out_.dane_.push_back(x+1);
            }
        }
    }
    int id_;
    Queue& in_;
    Queue& out_;
};

int main() {
    Queue q1, q2, q3;
    q1.dane_.push_back(100);
    Producent p1(1, q1, q2);
    Producent p2(2, q2, q3);
    Producent p3(3, q3, q1);


    thread thrd1( ref(p1) );
    thread thrd2( ref(p2) );
    thread thrd3( ref(p3) );
    thrd1.join();
    thrd2.join();
    thrd3.join();
    return 0;
}
