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

// tunel ima jednu traku, vozila idu u oba smera
// vozilo moze da zapocne prolazak kroz tunel samo ako nema vozila iz drugog smera


// vozila u tunelu: 
// 1. automobil - istovremeno moze biti najvise 3 automobila
#define AUTO 1
#define MAX_AUTO 3
// 2. autobus - u tunelu moze biti najvise jedan autobus
#define AUTOBUS 2
#define MAX_AUTOBUS 1
// 3. kamion, ako kamion prolazi, moze biti samo jedino vozilo u tunelu
#define KAMION 3
// 4. hitno vozilo, ima prioritet u odnosu na sva vozila koja cekaju u istom smeru
// moze da ih bude vise u trenutku
// i moze da bude u smeru sa istim vozilima
#define HITNO_VOZILO 4

#define BR_VOZILA 20


char* vozilo(int tipVozilo)
{
    switch(tipVozilo)
    {
        case 1:
            return "AUTO";
        case 2: 
            return "AUTOBUS";
        case 3:
            return "KAMION";
        case 4: 
            return "HITNO_VOZILO";
    }
}

char* smerVozila(int mojSmer)
{
    switch(mojSmer)
    {
        case 1:
            return "SEVER";
        case 2: 
            return "JUG";
    }
}


#define NONE 0
#define SEVER 1
#define JUG 2

int smer = NONE;

pthread_mutex_t mutex_stanje = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_stanje = PTHREAD_COND_INITIALIZER;

int br_auto = 0;
int br_autobus = 0;
int br_kamion = 0;
int br_hitno_vozilo = 0;
int br_hitno_vozilo_ceka_sever = 0;
int br_hitno_vozilo_ceka_jug = 0;
int br_vozila = 0;

void* vozi(void* arg)
{
    long id = (long)arg;

    int mojeVozilo = rand()%4 + 1;
    int mojSmer = rand()%2 + 1;
    int br_hitno_vozilo_ceka = 0;

    sleep(rand()%10 + 1);

    pthread_mutex_lock(&mutex_stanje);
    printf("Dolazi %s sa strane %s, id=%ld\n", vozilo(mojeVozilo),smerVozila(mojSmer), id);

    if(mojeVozilo == HITNO_VOZILO)
    {
        if(mojSmer == SEVER)
            br_hitno_vozilo_ceka_sever++;
        else 
            br_hitno_vozilo_ceka_jug++;
    }

    while(smer != NONE && smer != mojSmer)
    {
        pthread_cond_wait(&cond_stanje, &mutex_stanje);
    }

    if(smer == NONE)
    {
        printf("\n\nPROMENA SMERA, SMER = %s\n\n", smerVozila(mojSmer));
        smer = mojSmer;
    }

    pthread_cond_broadcast(&cond_stanje);
    pthread_mutex_unlock(&mutex_stanje);

    // Provera da li moze da se udje u traku
    pthread_mutex_lock(&mutex_stanje);

    if(mojSmer == SEVER)
        br_hitno_vozilo_ceka = br_hitno_vozilo_ceka_sever;
    else
        br_hitno_vozilo_ceka = br_hitno_vozilo_ceka_jug;
    
    if(mojeVozilo == AUTO)
    {
        while(br_hitno_vozilo_ceka > 0 || br_auto == MAX_AUTO || br_kamion != 0)
            pthread_cond_wait(&cond_stanje, &mutex_stanje);

        br_auto++;
    }
    else if(mojeVozilo == AUTOBUS)
    {
        while(br_hitno_vozilo_ceka > 0 || br_autobus == MAX_AUTOBUS || br_kamion != 0)
            pthread_cond_wait(&cond_stanje, &mutex_stanje);

        br_autobus++;
    }
    else if(mojeVozilo == KAMION)
    {
        while(br_hitno_vozilo_ceka > 0 || br_vozila > 0)
            pthread_cond_wait(&cond_stanje, &mutex_stanje);

        br_kamion++;
    }
    else if(mojeVozilo == HITNO_VOZILO)
    {
        if(mojSmer == SEVER)
            br_hitno_vozilo_ceka_sever--;
        else
            br_hitno_vozilo_ceka_jug--;

        br_hitno_vozilo++; 
    }

    br_vozila++;
    printf("Prolazi %s sa strane %s, id=%ld\n", vozilo(mojeVozilo),smerVozila(mojSmer), id);
    
    pthread_cond_broadcast(&cond_stanje);
    pthread_mutex_unlock(&mutex_stanje);

    //ide kroz traku
    sleep(rand()%5 + 1);

    pthread_mutex_lock(&mutex_stanje);

    if(mojeVozilo == AUTO)
    {
        br_auto--;
    }
    else if(mojeVozilo == AUTOBUS)
    {
        br_autobus--;
    }
    else if(mojeVozilo == KAMION)
    {
        br_kamion--;
    }
    else if(mojeVozilo == HITNO_VOZILO)
    {
        br_hitno_vozilo--; 
    }

    br_vozila--;

    if(br_vozila == 0)
    {
        smer = NONE;
    }

    printf("Zavrsava %s sa strane %s, id=%ld\n", vozilo(mojeVozilo),smerVozila(mojSmer), id);
    pthread_cond_broadcast(&cond_stanje);
    pthread_mutex_unlock(&mutex_stanje);

    return NULL;
}

int main()
{
    srand(time(NULL));

    pthread_t vozila[BR_VOZILA];
    for(long i = 0; i < BR_VOZILA; i++)
    {
        pthread_create(&vozila[i], NULL, vozi, (void *)i);
    }

    for(long i = 0; i < BR_VOZILA; i++)
    {
        pthread_join(vozila[i], NULL);
    }

    return 0;

}