// Pour compiler, utiliser gcc -Wall carrefour_multiple.c -o carrefour_multiple -O3 -lpthread

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

void access_init(void);

void feux();        // un processus de changement qui gere le changement des feux
void traversee1();  // voitures passant sur la premiere voie
void traversee2();  // voitures passant sur la deuxieme voie

int feu;		    // le feu
int n_traverse;   // nombre de voitures en train de traverser
int n_voiture;		// identifiant de voiture

int traversees[ 100 ];	// traversees (pour debogage)

#define PREMIERE  1  // feu rouge pour deuxieme, vert pour premier
#define DEUXIEME  2  // feu rouge pour premier,  vert pour deuxieme

pthread_cond_t cond;
pthread_mutex_t mutex;

int main( void )
{
	int i;

	access_init();

	pthread_t thread_feu;
	pthread_t threads_voiture[ 100 ];

	pthread_create( &thread_feu, NULL, (void*)&feux, NULL );

	for( i = 0 ; i < 50 ; i++ )
	{
		pthread_create( &threads_voiture[ 2*i   ], NULL, (void*)&traversee1, NULL );
		pthread_create( &threads_voiture[ 2*i+1 ], NULL, (void*)&traversee2, NULL );
	}

	for( i = 0 ; i < 100 ; i++ )
	{
		pthread_join( threads_voiture[ i ], NULL );
	}

/*

	for( i = 0 ; i < 100 ; i++ )
	{
		printf(		"Traversee de la voiture %3d: voie %d quand le feu est %d\n",
					i, (int)(traversees[ i ]/10), traversees[ i ] - 10*(int)(traversees[ i ]/10));
	}

	for( i = 0 ; i < 100 ; i++ )
	{
		if( (int)(traversees[ i ]/10) != traversees[ i ] - 10*(int)(traversees[ i ]/10) )
		{
			printf(		"ATTENTION: Traversee de la voiture %3d: voie %d quand le feu est %d\n",
						i, (int)(traversees[ i ]/10), traversees[ i ] - 10*(int)(traversees[ i ]/10));
		}
	}

*/

	return  0;
}

void access_init( void )
{
	int i;

	pthread_mutex_init( &mutex, NULL);

	pthread_cond_init( &cond, NULL);

	feu = PREMIERE;

	n_traverse = 0;

	n_voiture = 0;

	for( i = 0 ; i < 100 ; i++ )
	{
		traversees[ i ] = 0;
	}

	printf( "Init OK\n" );
}

void traversee( int voie )
{
	pthread_mutex_lock( &mutex );

	int n = n_voiture++;

	while( feu != voie || n_traverse > 5 )
	{
//		printf( "La voiture %3d attend sur la voie %d\n", n, voie );

		// Attendre tant que le feu est rouge et qu'il y a qqn qui traverse
		pthread_cond_wait( &cond, &mutex );
	}

	// J'ai assez attendu
	printf( "La voiture %3d traverse la voie %d quand le feu est %d et il y a %d voitures\n", n, voie, feu, n_traverse );
	if( voie != feu || n_traverse > 5 )
	{
		printf( "ATTENTION: La voiture %3d traverse la voie %d quand le feu est %d et il y a %d voitures\n", n, voie, feu, n_traverse );
	}

	n_traverse++;
	traversees[ n ] = 10*voie + feu;
	pthread_mutex_unlock( &mutex );

	// Je traverses, ce qui prend un certain temps
	usleep( 9 );

	// J'ai fini de traverser
	pthread_mutex_lock( &mutex );

//	printf( "La voiture %3d a fini de traverser la voie %d quand le feu est %d\n", n, voie, feu );
	n_traverse--;

	pthread_cond_broadcast( &cond );
	pthread_mutex_unlock( &mutex );
}

void feux( void )
{
	// Changer le feu
	pthread_mutex_lock( &mutex );

	if( feu == PREMIERE )
	{
		feu = DEUXIEME;
	}
	else
	{
		feu = PREMIERE;
	}

	printf( "Le feu est devenu %d\n", feu );

	pthread_cond_broadcast( &cond );
	pthread_mutex_unlock( &mutex );

	usleep( 37 );
	feux();
}

void traversee1( void )
{
	traversee( PREMIERE );
}

void traversee2( void )
{
	traversee( DEUXIEME );
}
