// prod-cons.cpp - Productor-consumidor con semáforos
#include <iostream>
#include <iomanip>
#include <pthread.h>
#include <semaphore.h>
using namespace std;

#define ELEMENTOS_BUFER 16 
#define TOTAL_DATOS 512

void *productor(void *); 
void *consumidor(void *); 
void *finalizar(void *);

// Estructura de datos compartida por los hilos
struct
{
  int bufer[ELEMENTOS_BUFER]; // matriz de datos
  int ind_p, ind_c; // índices del productor y del consumidor
  sem_t sem_exmut;  // exclusión mutua
  sem_t sem_vacios; // semáforo contador de elementos vacíos
  sem_t sem_llenos; // semáforo contador de elementos llenos
  bool finalizar;
} st_bufer;

int main()
{
  const int NHILOSP = 2; // productores
  const int NHILOSC = 3; // consumidores
  int i;
  pthread_t hilo_p[NHILOSP], hilo_c[NHILOSC];
  pthread_t hilo_f;         // hilo finalizar

  // Iniciar la estructura st_bufer
  st_bufer.ind_p = st_bufer.ind_c = 0;
  sem_init(&st_bufer.sem_exmut, 0, 1);
  sem_init(&st_bufer.sem_vacios, 0, ELEMENTOS_BUFER);
  sem_init(&st_bufer.sem_llenos, 0, 0);
  st_bufer.finalizar = false;
  // Mensaje al usuario
  cout << "Pulse \"Entrar\" cuando desee finalizar.\n";
  cout << "Ahora, pulse \"Entrar\" para continuar.\n";
  cin.ignore();
  // Crear los hilos
  pthread_create(&hilo_f, 0, finalizar, 0);
  for (i = 0; i < NHILOSP; i++)
    pthread_create(&hilo_p[i], 0, productor, 0);
  for (i = 0; i < NHILOSC; i++)
    pthread_create(&hilo_c[i], 0, consumidor, 0);
  // Esperar a que los hilos trabajadores terminen
  for (i = 0; i < NHILOSP; i++)
    pthread_join(hilo_p[i], 0);
  for (i = 0; i < NHILOSC; i++)
    pthread_join(hilo_c[i], 0);
  
  system("pause");
}

// Productor
void *productor(void *arg)
{
  int i = 0, a;
  while (!st_bufer.finalizar)
  {
    sem_wait(&st_bufer.sem_vacios); // decrementar contador vacíos
    sem_wait(&st_bufer.sem_exmut);  // decrementar contador exmut
    
    // Bufer circular: añadir dato
    a =  rand();
    st_bufer.bufer[st_bufer.ind_p] = a;
    st_bufer.ind_p = (st_bufer.ind_p + 1) % (ELEMENTOS_BUFER);
    cout << "bufer  <-- " << setw(6) << a << endl;
    i++;
    
    sem_post(&st_bufer.sem_exmut);  // incrementar contador exmut
    sem_post(&st_bufer.sem_llenos); // incrementar contador llenos
  }
  return 0;
}

// Consumidor
void *consumidor(void *arg)
{
  int i, a;
  while (!st_bufer.finalizar)
  {
    sem_wait(&st_bufer.sem_llenos); // decrementar contador llenos
    sem_wait(&st_bufer.sem_exmut);  // decrementar contador exmut
    
    // Bufer circular: recoger elemento
    a = st_bufer.bufer[st_bufer.ind_c];
    st_bufer.ind_c = (st_bufer.ind_c + 1) % (ELEMENTOS_BUFER);
    cout << "bufer  --> " << setw(6) << a << endl;
    
    sem_post(&st_bufer.sem_exmut);  // incrementar contador exmut
    sem_post(&st_bufer.sem_vacios); // incrementar contador vacíos
  }
  return 0;
}

// Finalizar
void *finalizar(void *arg)
{
  cin.ignore();
  st_bufer.finalizar = true;
  return 0;
}

