/*
  Petr Bulena
  Semestralni prace z predmetu Operacni systemy
  21. Komunikacni sit
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>


#define pocetBTS 15  // pocet BTS
#define maxMobilu 100  // maximalni pocet mobilu
#define mobiluStart 50  // kolik bude na zacatku vytvoreno mobilu
#define intervalAktualizace 5  // jak casto mobil kontroluje stav signalu, meni BTS...
#define intervalAktualizaceSignalu 3 // jak casto se zaktualizuji tabulky signalu
#define pravdepodobnostOdhlaseni 100 // odhlasi se, kdyz random()%pravdepodobnostOdhlaseni == 0
#define intervalPrihlasovani 2 // jak casto je pokus prihlasit mobil do site
#define intervalZpravOdBTS 1 // jak casto BTSka zkousi odeslat zpravy mobilum
#define pravdepodobnostZpravyOdMobilu 20

struct intenzitaInfo
{
  int cid, signal;
};

int pocetMobilu, pocetHandoveru, maxPrihlaseno;
int konec;  // vlakna si kontroluji a kdyz konec==1, tak se ukonci
pthread_t mobily[maxMobilu];
int mobilyPrihlasene[maxMobilu]; //pomocne pole pro snadne urceni, ktery mobil je prihlasen
// Kazdy mobil zna jen uroven signalu aktualniho a 8 okolnich sektoru jako v GSM
struct intenzitaInfo mobilyIntenzitySignalu[maxMobilu][9]; 
int zpravyMobilum[maxMobilu]; //simuluje zpravy mobilum - provede se inkrementace
int zpravyBTSkam[pocetBTS]; //simuluje zpravy BTSkam

// mutexy zamykajici pristup k prislusnemu prvku pole
pthread_mutex_t mobilyPrihlaseneMutex[maxMobilu]; //tez pro mobilyIntenzitaInfo
pthread_mutex_t pocetMobiluMutex;
pthread_mutex_t zpravyBTSkamMutex[pocetBTS];
pthread_mutex_t statistiky;

void cekej()
{
  int t = random() % 5;
  sleep(t);
}

void handler(int signum)
{
  printf("==== UKONCUJI ====\n");
  konec=1;
}

// simuluje cinnost zakladnove stanice - nahodne vybere cislo mobilu, urci, zda je prihlasen na tuto BTS
// a pripadne odesle zpravu
void *btska(void *tid)
{
 int cid, mobil;
 cid = (int) tid;
 printf("### BTS cid %d zapnuto a aktivni\n", cid);
 sleep(intervalZpravOdBTS*2);
 while (konec==0)
 {
  mobil = random() % maxMobilu;
  pthread_mutex_lock(&mobilyPrihlaseneMutex[mobil]); // zamkne praci s mobilem, aby nehandoveroval na jinou BTS
  if (mobilyPrihlasene[mobil]==1 && mobilyIntenzitySignalu[mobil][0].cid == cid)
  {
   zpravyMobilum[mobil]++;
   printf("@@@ BTS %d poslala zpravu mobilu %d\n", cid, mobil);
  } 
  pthread_mutex_unlock(&mobilyPrihlaseneMutex[mobil]);
  sleep(intervalZpravOdBTS + random()%2);
 }
 printf("### BTS cid %d znicena\n", cid);
 return NULL;
}

// Mobil - kontroluje si intenzity signalu, vysila BTSkam, nekdy se odhlasi ze site a ukonci se
void *mobil(void *tid)
{
 int imsi, vypnu, i, puvCid, pomCid, pomSignal;
 imsi = (int) tid;
 vypnu = 0;
 pthread_mutex_lock(&mobilyPrihlaseneMutex[imsi]);
 
 printf("=== Mobil %d prihlasen do site na Cid %d se signalem -%d dBm\n", imsi,
 mobilyIntenzitySignalu[imsi][0].cid, mobilyIntenzitySignalu[imsi][0].signal);
 pthread_mutex_unlock(&mobilyPrihlaseneMutex[imsi]);

 while (konec==0 && vypnu==0)
 {
   pthread_mutex_lock(&mobilyPrihlaseneMutex[imsi]);
   puvCid = mobilyIntenzitySignalu[imsi][0].cid;
   for (i=1; i<9; i++) // projdi tabulku sousedu, kdyz ma lepsi signal, prohod s aktulni BTS
    if (mobilyIntenzitySignalu[imsi][i].signal < mobilyIntenzitySignalu[imsi][0].signal
        && mobilyIntenzitySignalu[imsi][i].cid>-1)
    {
      pomCid = mobilyIntenzitySignalu[imsi][0].cid;
      pomSignal = mobilyIntenzitySignalu[imsi][0].signal;
      mobilyIntenzitySignalu[imsi][0].cid = mobilyIntenzitySignalu[imsi][i].cid;
      mobilyIntenzitySignalu[imsi][0].signal = mobilyIntenzitySignalu[imsi][i].signal;
      mobilyIntenzitySignalu[imsi][i].cid = pomCid;
      mobilyIntenzitySignalu[imsi][i].signal = pomSignal;
    }
   // a vypiseme pripadny handover 
   if (puvCid != mobilyIntenzitySignalu[imsi][0].cid) {
    pthread_mutex_lock(&statistiky);
    pocetHandoveru++;   
    printf(">>> Handover mobilu %d: %d --> %d\n", imsi, puvCid, mobilyIntenzitySignalu[imsi][0].cid);
    pthread_mutex_unlock(&statistiky);
    }
    else  // pokud nebyl handover, zkusi aspon poslat zpravu BTSce
     if (random()%pravdepodobnostZpravyOdMobilu == 0)
     {
      pthread_mutex_lock(&zpravyBTSkamMutex[mobilyIntenzitySignalu[imsi][0].cid]);
      zpravyBTSkam[mobilyIntenzitySignalu[imsi][0].cid]++;
      printf("@@@ Mobil %d poslal zpravu BTSce %d\n", imsi, mobilyIntenzitySignalu[imsi][0].cid);
      pthread_mutex_unlock(&zpravyBTSkamMutex[mobilyIntenzitySignalu[imsi][0].cid]);
     }
    
   pthread_mutex_unlock(&mobilyPrihlaseneMutex[imsi]);
   sleep(intervalAktualizace + random()%2);
   if (random() % pravdepodobnostOdhlaseni == 0) vypnu=1; // vypnu se?
 }
 
 // odhlaseni mobilu ze site
 pthread_mutex_lock(&pocetMobiluMutex);
 pthread_mutex_lock(&mobilyPrihlaseneMutex[imsi]);
 pocetMobilu--; 
 mobilyPrihlasene[imsi]=0;
// for (i=0; i<9; i++) mobilyIntenzitySignalu[imsi][i].cid=-1;

 printf("### Mobil %d odhlasen, pocet mobilu je %d\n", imsi, pocetMobilu);
 pthread_mutex_unlock(&mobilyPrihlaseneMutex[imsi]);
 pthread_mutex_unlock(&pocetMobiluMutex);
 return NULL;
}

// Nahodne vytvari nove mobily, pokud je misto v tabulce mobilu
void *prihlasMobil(void *tid)
{
 int i, u, rnd, result;
 float pravdep;
 pthread_mutex_lock(&pocetMobiluMutex);
 for (i=0; i<mobiluStart; i++)  // nejdrive vytvori mobily podle konstanty mobiluStart
  {
    pthread_mutex_lock(&mobilyPrihlaseneMutex[i]);
    result = pthread_create(&mobily[i], NULL, mobil, (void *) i);
    if (result) printf("!!! Nepodarilo se vytvorit mobil"); else
    {
    mobilyIntenzitySignalu[i][0].cid = 1 + (random() % pocetBTS);
    mobilyIntenzitySignalu[i][0].signal = 35 + (random() % 65);
    for (u=1; u<9; u++)
    {
     mobilyIntenzitySignalu[i][u].cid = -1;
     mobilyIntenzitySignalu[i][u].signal = 150;
    }
    mobilyPrihlasene[i]=1;
    pocetMobilu++;
    pthread_mutex_unlock(&mobilyPrihlaseneMutex[i]);
    // printf("*** Pocet mobilu v siti: %d\n", pocetMobilu);
    }
  }
 pthread_mutex_unlock(&pocetMobiluMutex);
  
 while (konec==0) 
 {
  pthread_mutex_lock(&pocetMobiluMutex);
  if (pocetMobilu < maxMobilu)
  {
    pravdep = 100*(float)pocetMobilu / (float)maxMobilu; // pravdepodobnost noveho mobilu
    //printf("PRAVDEP je %f\n", pravdep);
    rnd = random() % 100;
    if (rnd>pravdep)
    {
     i=0;
     while (mobilyPrihlasene[i]==1) i++;
     pthread_mutex_lock(&mobilyPrihlaseneMutex[i]);
     result = pthread_create(&mobily[i], NULL, mobil, (void *) i);
     if (result) printf("!!! Nepodarilo se vytvorit mobil"); else
     {
     mobilyIntenzitySignalu[i][0].cid = 1 + (random() % pocetBTS);
     mobilyIntenzitySignalu[i][0].signal = 35 + (random() % 65);
     for (u=1; u<9; u++)
     {
      mobilyIntenzitySignalu[i][u].cid = -1;
      mobilyIntenzitySignalu[i][u].signal = 150;
     }
     mobilyPrihlasene[i]=1;
     pocetMobilu++;
     }
     pthread_mutex_unlock(&mobilyPrihlaseneMutex[i]);
     if (pocetMobilu>maxPrihlaseno) maxPrihlaseno=pocetMobilu;
     // printf("*** Pocet mobilu v siti: %d\n", pocetMobilu);
    }
  }
  pthread_mutex_unlock(&pocetMobiluMutex);
  sleep(intervalPrihlasovani+random()%2);
 }
 printf("### Prihlasovani mobilu ukonceno\n");
 return NULL;
} // prihlasMobil konec

// temer nahodne aktualizuje tabulky intenzit signalu od BTS vsech mobilu
void *signalyMobilu(void *tid)
{
 int i,u;
 while (konec==0)
 {
  for (i=0; i< maxMobilu; i++) //pro vsechny mozne mobily
  {
   pthread_mutex_lock(&mobilyPrihlaseneMutex[i]);
   if (mobilyPrihlasene[i]==1) // pokud mobil i je v siti
   {
     mobilyIntenzitySignalu[i][0].signal += 10 - random()%20;
     for (u=1; u<9; u++) // pro vsechny sousedni BTS
      if (mobilyIntenzitySignalu[i][u].cid==-1) // pokud neni BTS prirazena
      {
       mobilyIntenzitySignalu[i][u].cid = 1 + (random() % pocetBTS);
       mobilyIntenzitySignalu[i][u].signal = 35 + (random() % 65);
      }
      else
      {
       mobilyIntenzitySignalu[i][u].signal += 2 - random()%5;
       // pri nedostatecnem signalu se BTS vypusti z tabulky
       // neni realne, ale je to jednoduche
       if (mobilyIntenzitySignalu[i][u].signal>105) mobilyIntenzitySignalu[i][u].cid=-1;
       // extremne velky signal netrpime
       if (mobilyIntenzitySignalu[i][u].signal<15) mobilyIntenzitySignalu[i][u].signal=20;
      }
   }
   pthread_mutex_unlock(&mobilyPrihlaseneMutex[i]);
  }
  sleep(intervalAktualizaceSignalu + random()%2);
 }
 return NULL;
} // signalyMobilu konec

void vypisStatistik()
{
  int i,pom;
  printf("\n\n==== STATISTIKY ====\n\n");
  printf("Maximalne prihlaseno mobilu: %d\n", maxPrihlaseno);
  printf("Pocet handoveru: %d\n", pocetHandoveru);
  pom=0;
  for (i=0; i<pocetBTS; i++) pom=pom+zpravyBTSkam[i];
  printf("Pocet zprav BTSkam: %d\n", pom);
  pom=0;
  for (i=0; i<maxMobilu; i++) pom=pom+zpravyMobilum[i];
  printf("Pocet zprav mobilum: %d\n\n\n", pom);
  
}

int main()
{
  int i;
  int result;
  
  pthread_t bts[pocetBTS+1];
  pthread_t prihlasovac, aktualizatorSignalu;
  struct sigaction sa;

  konec = 0;
  pocetMobilu = 0;
  pocetHandoveru = 0;
  maxPrihlaseno = 0;
  for (i=0; i<maxMobilu; i++)
  {
   mobilyPrihlasene[i]=0;
   pthread_mutex_init(&mobilyPrihlaseneMutex[i], NULL);
   zpravyMobilum[i]=0;
//   for (u=0; u<9; u++) mobilyIntenzitySignalu[i][u].cid=0;
  }
  pthread_mutex_init(&pocetMobiluMutex, NULL);
  pthread_mutex_init(&statistiky, NULL);

  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = &handler;
  sigaction(SIGQUIT, &sa, NULL);
 
  
  for (i=0; i<pocetBTS+1; i++)
  {
    pthread_create(&bts[i], NULL, btska, (void *) i);
    pthread_mutex_init(&zpravyBTSkamMutex[i], NULL);
    zpravyBTSkam[i]=0;
  }
  sleep(2);
  
  result = pthread_create(&prihlasovac, NULL, prihlasMobil, NULL);
  printf("### Vytvoreni threadu prihlasovac: %d\n", result);
  result = pthread_create(&aktualizatorSignalu, NULL, signalyMobilu, NULL);
  printf("### Vytvoreni threadu aktualizatorSignalu: %d\n", result);
  
  
  // Cekani na konec
  
  while (konec==0) {
   sleep(5);
//   if (random()%5 == 0) konec=1;
   } 
  for (i=0; i<maxMobilu; i++)
  {
    pthread_join(mobily[i], NULL);
  }
  for (i=0; i<pocetBTS; i++)
  {
    pthread_join(bts[i], NULL);
  }
  pthread_join(prihlasovac, NULL);
  
  
  for (i=0; i<maxMobilu; i++)
  {
    pthread_mutex_destroy(&mobilyPrihlaseneMutex[i]);
  }
  for (i=0; i<pocetBTS; i++)
  {
    pthread_mutex_destroy(&zpravyBTSkamMutex[i]);
  }
  pthread_mutex_destroy(&pocetMobiluMutex);
  pthread_mutex_destroy(&statistiky);
  printf("### koncime...\n");
  vypisStatistik();
  return 0;
}

