#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>

char q[10];
int number = 5,buffer = 0;
sem_t fullsem, emptysem, producerSync, producerSync1, consumerSync, consumerSync1;

void producerPrinting(int i, char tmp, int p) {
    printf("\nProducer#%d at i=%d : Inserted %c , buffer: %d", p, i, tmp, buffer);
}

char generateChar() {
    sleep(1);
    srand(time(NULL));
    return 'a' + ((rand() * 100) % 26);
}

void consumerProcess(char *tmp) { *tmp = toupper(*tmp); }

void consumerPrinting(int i, char tmp, int c) {
    printf("\nConsumer#%d at i=%d: Getting %c, buffer:%d", c, i, tmp, buffer);
}

void consumerRead(char *tmp) { *tmp= q[--buffer]; }
void insert(char e) {
    if (buffer < number)
        q[buffer++] = e;
    else
        buffer = 0;
}


void *produce(void *a) {
    int i;
    for (i = 0; i < number * 2; i++) {
        sem_wait(&fullsem);
        sem_wait(&producerSync1);

        char tmp = generateChar();
        insert(tmp);
        producerPrinting(i, tmp, 1);

        sem_post(&emptysem);
        sem_post(&producerSync);
    }
}



void *produce2(void *a) {
    int i;
    for (i = 0; i < number * 2; i++) {
        sem_wait(&fullsem);
        sem_wait(&producerSync);
        char tmp = generateChar();
        insert(tmp);
        producerPrinting(i, tmp, 2);

        sem_post(&emptysem);
        sem_post(&producerSync1);

    }
}

void *consume(void *a) {
    int i;
    for (i = 0; i < number * 2; i++) {

        sem_wait(&emptysem);
        sem_wait(&consumerSync1);
        char tmp;
        consumerRead(&tmp);
        consumerProcess(&tmp);
        consumerPrinting(i, tmp, 1);

        sem_post(&fullsem);
        sem_post(&consumerSync);
    }


}



void *consume2(void *a) {
    int i, v;
    for (i = 0; i < number * 2; i++) {

        sem_wait(&emptysem);
        sem_wait(&consumerSync);

        char tmp;
        consumerRead(&tmp);
        consumerProcess(&tmp);
        consumerPrinting(i, tmp, 2);

        sem_post(&fullsem);
        sem_post(&consumerSync1);

    }


}

int main() {
    pthread_t t[4];
    sem_init(&fullsem, 0, 10);
    sem_init(&emptysem, 0, 0);
    sem_init(&producerSync, 0, 0);
    sem_init(&producerSync1, 0, 1);
    sem_init(&consumerSync, 0, 1);
    sem_init(&consumerSync1, 0, 1);

    pthread_create(&t[0], NULL, &produce, NULL);
    pthread_create(&t[1], NULL, &produce2, NULL);

    pthread_create(&t[2], NULL, &consume, NULL);


    pthread_create(&t[3], NULL, &consume2, NULL);

    pthread_join(t[0], NULL);
    pthread_join(t[1], NULL);
    pthread_join(t[2], NULL);
    pthread_join(t[3], NULL);
    sem_destroy(&fullsem);
    sem_destroy(&emptysem);
    sem_destroy(&consumerSync);
    sem_destroy(&consumerSync1);
    sem_destroy(&producerSync);
    sem_destroy(&producerSync1);


    return 0;
}