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

#define N 50

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

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

int smer=NONE;
int broj_vozila=0;
int broj_auto=0;
int broj_bus=0;
int broj_kamion=0;
int broj_hitno=0;
int hitno_sever=0;
int hitno_jug=0;
pthread_mutex_t mt;//=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t msever;//=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mjug;//=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_auto;//=PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_bus;//=PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_kamion;//=PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_hitno_sever;//=PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_hitno_jug;//=PTHREAD_COND_INITIALIZER;

// sleep ima problem, zakomentarisan svuda

void* voziSever(void * arg){
    long id = (long)arg;
    int vozilo = rand() % 4 +1;
    //printf("%d-%ld ", vozilo,id);
    if(vozilo==AUTO){
        //printf("Dolazi AUTO [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&msever);
        while(hitno_sever>0)
            pthread_cond_wait(&cond_hitno_sever,&msever);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&msever);

        pthread_mutex_lock(&mt);
        while(smer == JUG || broj_auto >=3 || broj_kamion > 0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=SEVER;
        broj_auto++;
        broj_vozila++;
        printf("Ulazi  AUTO [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_auto--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi AUTO [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==BUS){
        //printf("Dolazi BUS [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&msever);
        while(hitno_sever>0)
            pthread_cond_wait(&cond_hitno_sever,&msever);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&msever);

        pthread_mutex_lock(&mt);
        while(smer == JUG || broj_bus >0 || broj_kamion > 0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=SEVER;
        broj_bus++;
        broj_vozila++;
        printf("Ulazi  BUS [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_bus--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi BUS [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==KAMION){
        //printf("Dolazi KAMION [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&msever);
        while(hitno_sever>0)
            pthread_cond_wait(&cond_hitno_sever,&msever);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&msever);

        pthread_mutex_lock(&mt);
        while(smer !=NONE)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=SEVER;
        broj_kamion++;
        broj_vozila++;
        printf("Ulazi  KAMION [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_kamion--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi KAMION [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==HITNO){
        //printf("Dolazi HITNO [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&msever);
            hitno_sever++;
        printf("[%ld] HITNO PRIORITET..\n",id);
        pthread_mutex_unlock(&msever);

        pthread_mutex_lock(&mt);
        while(smer ==JUG || broj_kamion>0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=SEVER;
        broj_hitno++;
        broj_vozila++;
        hitno_sever--;
        pthread_cond_broadcast(&cond_hitno_sever);
        printf("Ulazi  HITNO [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_hitno--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi HITNO [%ld] SEVER |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    return NULL;
}

void* voziJug(void * arg){
    long id = (long)arg;
    int vozilo = rand() % 4 +1;
    //printf("%d-%ld ", vozilo,id);
    if(vozilo==AUTO){
        //rintf("Dolazi AUTO [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&mjug);
        while(hitno_jug>0)
            pthread_cond_wait(&cond_hitno_jug,&mjug);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&mjug);

        pthread_mutex_lock(&mt);
        while(smer == SEVER || broj_auto >=3 || broj_kamion > 0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=JUG;
        broj_auto++;
        broj_vozila++;
        printf("Ulazi  AUTO [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_auto--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi AUTO [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==BUS){
        //printf("Dolazi BUS [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&mjug);
        while(hitno_jug>0)
            pthread_cond_wait(&cond_hitno_jug,&mjug);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&mjug);

        pthread_mutex_lock(&mt);
        while(smer == SEVER || broj_bus >0 || broj_kamion > 0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=JUG;
        broj_bus++;
        broj_vozila++;
        printf("Ulazi  BUS [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_bus--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi BUS [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==KAMION){
        //printf("Dolazi KAMION [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&mjug);
        while(hitno_jug>0)
            pthread_cond_wait(&cond_hitno_jug,&mjug);
        //printf("[%ld] Cekanje zavrseno..\n",id);
        pthread_mutex_unlock(&mjug);

        pthread_mutex_lock(&mt);
        while(smer !=NONE)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=JUG;
        broj_kamion++;
        broj_vozila++;
        printf("Ulazi  KAMION [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_kamion--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi KAMION [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    if(vozilo==HITNO){
        //printf("Dolazi HITNO [%ld]\n",id);
        //sleep((rand() % 3) +1 ); // dolazi na cekanje
        pthread_mutex_lock(&mjug);
            hitno_jug++;
        printf("[%ld] HITNO PRIORITET..\n",id);
        pthread_mutex_unlock(&mjug);

        pthread_mutex_lock(&mt);
        while(smer ==SEVER || broj_kamion>0)
            pthread_cond_wait(&cond_auto,&mt);
        if(smer == NONE)
            smer=JUG;
        broj_hitno++;
        broj_vozila++;
        hitno_jug--;
        pthread_cond_broadcast(&cond_hitno_jug);
        printf("Ulazi  HITNO [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);

        //sleep((rand() % 3) + 1);

        pthread_mutex_lock(&mt);
        broj_hitno--;
        broj_vozila--;
        if(broj_vozila==0){
            smer=NONE; 
        }
        pthread_cond_broadcast(&cond_auto);
        printf("Izlazi HITNO [%ld] JUG |auto|bus|kam|hitno| |%d|%d|%d|%d|\n",id,broj_auto,broj_bus,broj_kamion,broj_hitno);
        pthread_mutex_unlock(&mt);
    }
    return NULL;
}

int main(){
    pthread_t niti[N];
    pthread_mutex_init(&mt,NULL);
    pthread_mutex_init(&msever,NULL);
    pthread_mutex_init(&mjug,NULL);
    pthread_cond_init(&cond_auto,NULL);
    pthread_cond_init(&cond_hitno_sever,NULL);
    pthread_cond_init(&cond_hitno_jug,NULL);

    for(int i=0;i<N;i++){
        if(i % 2 == 0) pthread_create(niti+i,NULL,voziSever,(void *)i);
        else pthread_create(niti+i,NULL,voziJug,(void *)i);
    }
    //pthread_exit(NULL);

}