#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

pthread_mutex_t tunel_lock = PTHREAD_MUTEX_INITIALIZER
pthread_cond_t tunel_wait = PTHREAD_COND_INITIALIZER

pthread_mutex_t hitna_lock = PTHREAD_MUTEX_INITIALIZER


#define BROJ_VOZILA 10
#define AUTOMOBIL 0
#define AUTOBUS 1
#define KAMION 2
#define HITNO_VOZILO 3

#define SEVER 0
#define JUG 1

int usliUTunel[2] = {0,0};
int suprotan_smer = -1;
int flagHitnaCeka=0;

int brojAutomobila = 0;
int brojAutobusa = 0;
int brojKamiona = 0;
int brojHitne = 0;

typedef struct{
    int id_vozila;
    int tip_vozila;
    int vreme_dolaska_u_tunel;
    int vreme_prolaska_kroz_tunel;
    int smer_vozila;
}Vozilo;

void funkcija(void &arg){

    Vozilo* vozilo = (Vozilo*)arg;

    printf("Vozilo %d tipa %d krece ka tunelu iz smera %d\n", vozilo->id_vozila, vozilo->tip_vozila, vozilo->smer_vozila);

    sleep(vozilo->vreme_dolaska_u_tunel);

    printf("Vozilo %d tipa %d je stiglo na red za ulazak u tunel iz smera %d i ceka\n", vozilo->id_vozila, vozilo->tip_vozila, vozilo->smer_vozila);
    
    if(vozilo->tip_vozila ==  HITNO_VOZILO)
    {
        pthread_mutex_lock(&hitna_lock);
        flagHitnaCeka=1;
        pthread_mutex_unlock(&hitna_lock);
    }
    
    pthread_mutex_lock(&tunel_lock);

    suprotan_smer = vozilo->smer_vozila == SEVER ? JUG: SEVER;

    while( usliUTunel[suprotan_smer] || 
    (vozilo->tip_vozila == AUTOMOBIL && (brojAutomobila > 3 || brojKamiona > 0 ||  flagHitnaCeka == 1 )) ||
    (vozilo->tip_vozila == AUTOBUS && ( brojAutobusa > 0 || flagHitnaCeka > 0 || brojKamiona > 0 )) ||
    (vozilo->tip_vozila == KAMION && (brojAutobusa > 0 || brojAutomobila > 0 || brojKamiona >0 || brojHitne >0 || flagHitnaCeka==1)) ||
    (vozilo->tip_vozila == HITNO_VOZILO && (brojKamiona > 0))
    )
    {
        if(vozilo->tip_vozila ==  AUTOMOBIL) printf ("Automobil ceka...\n");
        else if(vozilo->tip_vozila == AUTOBUS) printf ("Autotobus ceka..\n");
        else if(vozila->tip_vozila ==  KAMION) printf ("Kamion ceka...\n");
        else if(vozila->tip_vozila == HITNO_VOZILO) printf ("Hitno vozilo ceka...\n");

        pthread_cond_wait(&tunel_wait, &tunel_lock);
    }

    usliUTunel[vozilo->smer]++;

    if(vozilo->tip_vozila ==  AUTOMOBIL) brojAutomobila++;
    if(vozilo->tip_vozila == AUTOBUS) brojAutobusa++;
    if(vozila->tip_vozila ==  KAMION) brojKamiona++;
    if(vozila->tip_vozila == HITNO_VOZILO) 
    {
        brojHitne++;
        pthread_mutex_lock(&hitna_lock);
        flagHitnaCeka = 0;
        pthread_mutex_unlock(&hitna_lock);
    }
    
    
    pthread_mutex_unlock(&tunel_lock);

    printf("Vozilo %d tipa %d prolazi kroz tunel iz smera %d \n", vozilo->id_vozila, vozilo->tip_vozila, vozilo->smer_vozila);
    sleep(vozilo->vreme_prolaska_kroz_tunel);

    pthread_mutex_lock(&tunel_lock);

    if(vozilo->tip_vozila ==  AUTOMOBIL) brojAutomobila--;
    if(vozilo->tip_vozila == AUTOBUS) brojAutobusa--;
    if(vozila->tip_vozila ==  KAMION) brojKamiona--;
    if(vozila->tip_vozila == HITNO_VOZILO) brojHitne--;
    usliUTunel[vozilo->smer]--;

    pthread_mutex_broadcast(&tunel_wait);

    pthread_mutex_unlock(&tunel_lock);
        
    printf("Vozilo %d tipa %d je izaslo iz tunela iz smera %d \n", vozilo->id_vozila, vozilo->tip_vozila, vozilo->smer_vozila);

}

int main(){

    Vozilo vozila[BROJ_VOZILA];
    pthread_t niti[BROJ_VOZILA];

    for(int i=0; i<BROJ_VOZILA; i++){
        vozila[i]->id_vozila = i;
        vozila[i]->tip_vozila = rand()%4;
        vozila[i]->smer_vozila = rand()%2;
        vozilo[i]->vreme_prolaska_kroz_tunel = rand()%10+1;
        vozilo[i]->vreme_dolaska_u_tunel = rand()%15+1;

        pthread_create(&niti[i], NULL, funkcija, &vozila[i]);
    }

    for(int i=0;i<BROJ_VOZILA; i++){
        pthread_join(niti[i], NULL);
    }

    pthread_mutex_destroy(&tunel_lock);
    pthread_cond_destroy(&tunel_wait);
    pthread_mutex_destroy(&hitna_lock);

    return 0;
}