#include <iostream>
#include <thread>
#include <cstdlib>
#include <string>
#include <mutex>

using namespace std;


class Filosofo {
public :

    int id;
    mutex *destra;
    mutex *sinistra;
    Filosofo(int n, mutex *d, mutex *s) {
        id=n;
        destra=d;
        sinistra=s;
    }

    void pensa() {
        string argomenti[5]= {
            "Alla fame nel mondo",
            "Al punto G",
            "All origine dell Universo",
            "Al senso della vita",
            "Alla domanda la cui risposta e' 42"
        };
        cout << id << ": Sto pensando... " << argomenti[id] << endl;
        usleep( 10000 * ( rand() % 90 + 10 ));
    };

    void mangia() {
        printf("%d: Cerco le bacchette...\n",id);
        std::unique_lock<std::mutex> lock_a(*destra, std::defer_lock);
        std::unique_lock<std::mutex> lock_b(*sinistra, std::defer_lock);
        std::lock(lock_a,lock_b);
        printf("%d: Sto mangiando...\n",id);
        usleep( 30000 * ( rand() % 90 + 10 ));
        printf("%d: Poso le bacchette, sazio\n",id);
    };


    void operator()() {
        printf("%d: Penso quindi sono \n",id);
        usleep( 30000 * ( rand() % 90 + 10 ));
        for(int a=0; a<5; a++) {
            pensa();
            mangia();
        }
    }
};


int main() {
    Filosofo* filosofi[5];
    mutex bacchette[5];
    std::thread* threads[5];
    int a;
    printf("I cinque filosofi\n\n");
    for(a=0; a<5; a++) {
        filosofi[a]=new Filosofo(a,&bacchette[a],&bacchette[(a+1)%5]);
        threads[a]= new std::thread(*filosofi[a]);
    }
    for(a=0; a<5; a++) {
        threads[a]->join();
        delete filosofi[a];
        delete threads[a];
    }
    printf("\nI filosofi hanno comprato altre bacchette...\n");
};

