/*Une piscine peut contenir au maximum un nombre N de nageurs. Chaque nageur
dispose d'un panier pour ranger ses habits ; il y a donc N paniers. A l'entrée comme
à la sorite de cette piscine, les nageurs entrent en concurrence pour l'acquisition d'un panier et d'une cabine d'habillage ; 
il y a C cabines avec 1<C<<N
Les clients de cette piscine sont tenus de réaliser les opérations suivantes dans cet ordre :
1- Acquérir un panier ;
2- Acquerir une cabine ; 
3- Se changer ; 
4- Libérer la cabine ; 
5- Nager ; 
6- Acquérir une cabine
7- Se changer
8- Libérer la cabine
9- Libérer la panier

De plus, on distingue les clients abonnées des autres, et on privilègie les abonnés en
leur permettant d'acquérir plus facilement un panier. 
On assimile ces clients à des processus parallèles en compétition pour le partage 
des ressources qui sont les paniers et les cabines.
Donner l'algorithme d'un processus abonné et celui d'un non-adonné en les 
synchronisant par les sémaphores. La priorité sera réalisée à l'aide d'un obstacle que
devront franchir les non-abonnés : un seul à la fois pouvant franchir cet obstacle et seulement
s'il n'y a pas d'abonné en attente de panier.*/
/* piscine.c */
#include <stdio.h>
#include <pthread.h>

#define NbTh1 2      //Nombre de threads symbolisant les abonnes
#define NbTh2 3      //Nombre de threads symbolisant les non abonnes 
#define N  2  // Nombre de paniers
#define C  2  // Nombre de cabines

pthread_t tid[NbTh1+NbTh2];
pthread_mutex_t mutex;
pthread_cond_t condPiscine, condCabine, condPanier, condBarriere;

// initialisation
int NbCabinesOccupees=0, NbPaniersOccupes=0, NbAbonnesAttente=0;

void Abonne_se_changer (int i)
{
pthread_mutex_lock(&mutex);
        if(NbCabinesOccupees==N)
	{
           NbAbonnesAttente ++;
	   printf("L'abonne %d se bloque car pas de panier disponible \n", i);
           pthread_cond_wait(&condPanier, &mutex);
           NbAbonnesAttente --;
        }

/* prend un panier */
printf("L'abonne %d prend un panier \n", i);
             NbPaniersOccupes++ ; 
	if (NbAbonnesAttente==0)  { pthread_cond_broadcast(&condPiscine); }
             if (NbCabinesOccupees == C)  {
 	     printf("L'abonne %d se bloque car pas de cabine disponible \n", i);
     pthread_cond_wait(&condCabine, &mutex); 
 }
            /* entre dans une cabine */
	printf("L'abonne %d entre dans une cabine \n", i);
	NbCabinesOccupees++ ;
	printf("L'abonne %d se change et libère la cabine \n", i); 
	NbCabinesOccupees-- ;
	pthread_cond_signal(&condCabine);
	
	
pthread_mutex_unlock(&mutex);
}

	
void  NonAbonne_se_changer (int i) {
pthread_mutex_lock(&mutex);
	
	while  (NbAbonnesAttente >0)  
	 {
		printf("Le non abonne %d se bloque car abonnes en attente \n", i);
		pthread_cond_wait(&condPiscine, &mutex);	
  }
	
	if  (NbPaniersOccupes == N)  {
	 	printf("Le non abonne %d se bloque car pas de panier disponible \n", i);
     		pthread_cond_wait(&condPanier, &mutex);
}
             NbPaniersOccupes ++ ;
 /* prend un panier */
printf("Le non abonne %d prend un panier \n", i);

	pthread_cond_signal(&condBarriere);  /* tente de réveiller un non abonné */
	if (NbCabinesOccupees == C)  {
 		printf("Le non abonne %d se bloque car pas de cabine disponible \n", i);
     	pthread_cond_wait(&condCabine, &mutex); 
}
             NbCabinesOccupees++ ; 
/* entre dans une cabine */
printf("Le non abonne %d entre dans une cabine \n", i);
	printf("Le non abonne %d se change et libère la cabine \n", i); 
	NbCabinesOccupees-- ;
	pthread_cond_signal(&condCabine);  
	
pthread_mutex_unlock(&mutex);
}
	
void Se_rhabiller (int i)
{
pthread_mutex_lock(&mutex);

	if (NbCabinesOccupees==C)  {
		printf("Le nageur %d veut se rhabiller mais pas de cabine disponible \n", i);
     	   	pthread_cond_wait(&condCabine, &mutex);

}
		
/* entre dans une cabine */			
             NbCabinesOccupees ++ ;
printf("Le nageur %d entre dans une cabine \n", i);
	printf("Le nageur %d se change et libère la cabine \n", i); 
NbCabinesOccupees -- ;
	pthread_cond_signal(&condCabine);
	printf("Le nageur %d libère le panier \n", i);
	NbPaniersOccupes -- ;
pthread_cond_signal(&condPanier);
	
pthread_mutex_unlock(&mutex);

}


void * fonc_Abonne(void * i)
{

srand(pthread_self());
printf("L'abonne %d arrive ...\n",(int)i);
Abonne_se_changer((int)i);
/* temps de natation */
printf("L'abonne %d nage ...\n",(int)i);
usleep(rand()%200000);
Se_rhabiller((int)i);
printf("L'abonne %d quitte la piscine\n",(int) i);

}

void * fonc_nonAbonne(void * i)
{

srand(pthread_self());
printf("Le non abonne %d arrive ...\n",(int)i);
NonAbonne_se_changer((int)i);
/* temps de natation */
printf("Le non abonne %d nage ...\n",(int)i);
usleep(rand()%200000);
Se_rhabiller((int)i);
printf("Le non  abonne %d quitte la piscine\n",(int) i);

}


int main()
{
int num;
pthread_mutex_init(&mutex,0);
pthread_cond_init(&condCabine,0);
pthread_cond_init(&condPanier,0);
pthread_cond_init(&condBarriere,0);

//creation des threads
for(num=0;num<NbTh1;num ++)
        pthread_create(tid+num,0,(void *(*)())fonc_Abonne,(void*)num);
for(num=NbTh1;num<NbTh2+NbTh1;num ++)
        pthread_create(tid+num,0,(void *(*)())fonc_nonAbonne,(void*)num);

//attend la fin de toutes les threads
for(num=0;num<NbTh1+NbTh2;num ++)
        pthread_join(tid[num],NULL);
        
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condPanier);
pthread_cond_destroy(&condCabine);
pthread_cond_destroy(&condBarriere);

exit(0);
}
