#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

char buff;             // Споделен буфер (глобална променлива)
pthread_mutex_t mu;    // Мутекс за взаимно изключване при достъп до buff
sem_t sem1, sem2;      // Семафори за координация (пълно/празно)

void *producer(void* arg) {
  char c;
  for (c = 'a'; c <= 'z'; c++) {
    // 1. Чакаме sem1 да стане > 0 (т.е. буферът да е свободeн за писане)
    // В началото sem1 е 1, така че влизаме веднага.
    sem_wait(&sem1);

    // 2. Влизаме в критична секция (заключваме мутекса)
    pthread_mutex_lock(&mu);
    buff = c;  // Записваме буквата в споделената памет
    pthread_mutex_unlock(&mu); // Излизаме от критична секция

    // 3. Сигнализираме на потребителя, че има данни (вдигаме sem2)
    sem_post(&sem2);
  }
  pthread_exit(NULL);
}

void *consumer(void* arg) {
  int i;
  for (i = 0; i < 26; i++) {
    // 1. Чакаме sem2 да стане > 0 (т.е. да има записани данни)
    // В началото sem2 е 0, така че тази нишка ще чака Producer-а.
    sem_wait(&sem2);

    // 2. Влизаме в критична секция
    pthread_mutex_lock(&mu);
    printf("%c\n", buff); // Четем и отпечатваме
    pthread_mutex_unlock(&mu); // Излизаме от критична секция

    // 3. Сигнализираме на производителя, че сме готови за нова буква (вдигаме sem1)
    sem_post(&sem1);
  }
  pthread_exit(NULL);
}

int main(int argc, char* argv[]) {
  pthread_t t1, t2;

  // Инициализиране на мутекса  - това е динамично, но може и статично инициализиране
  pthread_mutex_init(&mu, NULL);

  // Инициализиране на семафорите:
  // sem1 = 1: Разрешаваме на Producer да пише веднага (буферът е празен)
  sem_init(&sem1, 0, 1);
  // sem2 = 0: Забраняваме на Consumer да чете веднага (няма данни още)
  sem_init(&sem2, 0, 0);

  // Създаване на нишките
  pthread_create(&t1, NULL, producer, NULL);
  pthread_create(&t2, NULL, consumer, NULL);

  // Изчакване на нишките да приключат работа
  pthread_join(t1, NULL);
  pthread_join(t2, NULL);

  // Унищожаване на мутекса (добра практика, макар и незадължително тук)
  pthread_mutex_destroy(&mu);

  pthread_exit(NULL);
}

// Makefile
all:
        gcc -O0 -o zad3 zad3.c -lpthread
start:
        ./zad3