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

#define N 100

#define AUTO 1
#define BUS 2
#define KAMION 3
#define HITNA 4

int u_tunelu = 0;
int br_auta = 0;
int br_autobusa = 0;
int br_kamiona = 0;
int br_hitna = 0;


#define NEDEFINISAN 0
#define SEVER 1
#define JUG 2

int smer = NEDEFINISAN;

int prioritet = 0; //za hitnu

pthread_mutex_t mutex;
pthread_cond_t cond_auto;
pthread_cond_t cond_kamion;
pthread_cond_t cond_bus;
pthread_cond_t cond_hitna;

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

    int tip = rand() % 4 + 1; //tip vozila na random
    int vreme = rand() % 5 + 1; //cekanje

    switch(tip)
    {
        case HITNA:
        {
            pthread_mutex_lock(&mutex);
            while(smer != NEDEFINISAN || br_kamiona > 0)
                pthread_cond_wait(&cond_hitna, &mutex);

            prioritet=1;
            
            br_hitna++;
            u_tunelu++;
            smer = JUG;

            printf("HITNO VOZILO %ld sa JUGA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            
            prioritet = 0;
            
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            
            br_hitna--;
            u_tunelu--;
            
            printf("HITNO VOZILO %ld sa JUGA proslo kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case AUTO:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN || br_kamiona > 0 || br_auta >= 3)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_auta++;
            u_tunelu++;
            smer = JUG;
            printf("AUTO %ld sa JUGA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            br_auta--;
            u_tunelu--;
            printf("AUTO %ld sa JUGA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case BUS:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN || br_kamiona > 0 || br_autobusa >= 1)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_autobusa++;
            u_tunelu++;
            smer = JUG;
            printf("BUS %ld sa JUGA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            br_autobusa--;
            u_tunelu--;
            printf("BUS %ld sa JUGA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case KAMION:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_kamiona++;
            u_tunelu++;
            smer = JUG;
            printf("KAMION %ld sa JUGA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            //pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            //zakomentarisano lock i unlock
            //jer kamion sme da bude jedini u tunelu
            //pthread_mutex_lock(&mutex);
            br_kamiona--;
            u_tunelu--;
            printf("KAMION %ld sa JUGA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            
            smer = NEDEFINISAN;
            //pthread_cond_broadcast(&cond_kamion);
            pthread_cond_broadcast(&cond_hitna);
            pthread_cond_broadcast(&cond_bus);
            pthread_cond_broadcast(&cond_auto);
            
            pthread_mutex_unlock(&mutex);
        }
        default:
            break;
    }
    pthread_exit(NULL);
}



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

    int tip = rand() % 4 + 1; //tip vozila na random
    int vreme = rand() % 5 + 1; //cekanje

    switch(tip)
    {
        case HITNA:
        {
            pthread_mutex_lock(&mutex);
            while(smer != NEDEFINISAN || br_kamiona > 0)
                pthread_cond_wait(&cond_hitna, &mutex);

            prioritet=1;
            
            br_hitna++;
            u_tunelu++;
            smer = JUG;

            printf("HITNO VOZILO %ld sa JUGA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            
            prioritet = 0;
            
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            br_hitna--;
            u_tunelu--;
            prioritet = 0;
            
            printf("HITNO VOZILO %ld sa SEVERA proslo kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case AUTO:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN || br_kamiona > 0 || br_auta >= 3)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_auta++;
            u_tunelu++;
            smer = SEVER;
            printf("AUTO %ld sa SEVERA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            br_auta--;
            u_tunelu--;
            printf("AUTO %ld sa SEVERA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case BUS:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN || br_kamiona > 0 || br_autobusa >= 1)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_autobusa++;
            u_tunelu++;
            smer = SEVER;
            printf("BUS %ld sa SEVERA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            pthread_mutex_lock(&mutex);
            br_autobusa--;
            u_tunelu--;
            printf("BUS %ld sa SEVERA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            if(u_tunelu == 0)
            {
                smer = NEDEFINISAN;
                pthread_cond_broadcast(&cond_kamion);
                //pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_bus);
                //pthread_cond_broadcast(&cond_auto);
            }
            else
            {
                pthread_cond_broadcast(&cond_hitna);
                //pthread_cond_broadcast(&cond_kamion);
                pthread_cond_broadcast(&cond_bus);
                pthread_cond_broadcast(&cond_auto);
            }
            pthread_mutex_unlock(&mutex);
        }
        case KAMION:
        {
            pthread_mutex_lock(&mutex);
            while(prioritet != 0 || smer != NEDEFINISAN)
                pthread_cond_wait(&cond_auto, &mutex);
            
            br_kamiona++;
            u_tunelu++;
            smer = SEVER;
            printf("KAMION %ld sa SEVERA prolazi kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            //pthread_mutex_unlock(&mutex);
            
            sleep(vreme); //prolazi kroz tunel
            
            //zakomentarisano lock i unlock
            //jer kamion sme da bude jedini u tunelu
            //pthread_mutex_lock(&mutex);
            br_kamiona--;
            u_tunelu--;
            printf("KAMION %ld sa SEVERA PROSAO kroz tunel, u tunelu trenutno auta(%d), autobusa(%d), kamiona(%d) i hitnih vozila(%d)\n", id, br_auta, br_autobusa, br_kamiona, br_hitna);
            
            smer = NEDEFINISAN;
            //pthread_cond_broadcast(&cond_kamion);
            pthread_cond_broadcast(&cond_hitna);
            pthread_cond_broadcast(&cond_bus);
            pthread_cond_broadcast(&cond_auto);
            pthread_mutex_unlock(&mutex);
        }
        default:
            break;
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t vozila[N];

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond_auto, NULL);
    pthread_cond_init(&cond_bus, NULL);
    pthread_cond_init(&cond_hitna, NULL);
    pthread_cond_init(&cond_kamion, NULL);

    for(long i = 0; i < N; i++)
    {
        if(i%2 == 0)
            pthread_create(vozila + i, NULL, saJuga, (void*)i);
        else
            pthread_create(vozila + i, NULL, saSevera, (void*)i);
    }

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

    pthread_exit(NULL);
    return 1;
}