#include "Lan.h"
#include "Node.h"
#include "Packet.h"
#include <iostream>
#include <stdlib.h>
#include <time.h>

Lan::Lan( int bandwidth ):
max_bandwidth_( bandwidth ), used_bandwidth_( 0 )
{
   cout << "Lan con banda massima " << bandwidth << "p" <<  endl;
   // eventuale inizializzazione del vettore
   cout << "Nessun nodo nella Lan" << endl;
}

Lan::Lan(const vector< Node  *>& nodesvect):
max_bandwidth_( 10 ), used_bandwidth_( 0 ), nodesVect_( nodesvect )
{
   // se non è fornita la banda della lan
   // si assume che sia di 10 pacchetti
   cout << "Lan con banda massima 10p" 
        << " e " <<  nodesvect.size() << " nodi" << endl;
}

Lan::Lan( int bandwidth, const vector< Node *>& nodesvect):
max_bandwidth_( bandwidth ), used_bandwidth_( 0 ), nodesVect_( nodesvect )
{
   cout << "Lan con banda massima " << bandwidth << "p"
        << " e " <<  nodesvect.size() << " nodi" << endl;
	   
}     

Lan::~Lan()
{
   vector<Node *>::iterator i;
   for( i = nodesVect_.begin(); i != nodesVect_.end(); i++ )
   {
      delete *i;
   }
   
   while( !packetQueue_.empty() )
   {
      // distruzione dei pacchetti in coda
      // si sarebbe anche potuto decidere
      // di recapitarli, invece di distruggerli
      const Packet *packet = packetQueue_.front();
      packetQueue_.pop();
      delete packet;
   }
    
   cout << "Lan distrutta" << endl;
}

void Lan:: addNode( Node* node)
{
   nodesVect_.push_back( node );
}

int Lan::bandWidth() const
{
   return (max_bandwidth_ - used_bandwidth_);
}

bool Lan::enqueue(const Packet *packet )
{
   bool enqueued;
   
   // se c'e' banda disponibile si accoda il pacchetto
   // altrimenti lo si rifiuta
   if( bandWidth() > 0  )
   {
      packetQueue_.push( packet );
      used_bandwidth_++;
      enqueued = true;
   }
   else
   {
      // cout << "Pacchetto rifiutato" << endl;
      enqueued = false;
   }
   return enqueued;
}

bool Lan::deliverPacket() 
{
   // se tutta la banda e' libera si esce con successo
   // si assume che non vi siano pacchetti in coda
   // e che quindi siano stati precedentemente recapitati
   if( bandWidth() == max_bandwidth_ )
      return true;
   
   //se si arriva a questo punto vuol dire che
   //vi sono pacchetti in coda da recapitare
   
   //cout << "Debug_prima: " << packetQueue_.size() << endl;
   const Packet *packet = packetQueue_.front();
   packetQueue_.pop();
   
   //cout << "Debug_dopo: " << packetQueue_.size() << endl;
   used_bandwidth_--;
   
   int nodenum = nodesVect_.size();
   bool delivered = false;
   
   for(int i = 0; (i < nodenum) && !delivered; i++)
   {
      //cout << "--> nel for" << i << endl;
      if( nodesVect_[i]->accept( *packet ) )
	 delivered = true;
   }
   if( !delivered )
      cout <<  "Pacchetto " << packet->getInfo()
           << " ha destinazione sconosciuta" << endl;
  
   delete packet;
   return delivered;
}

void Lan::doWork()
{
   // se e' una lan con banda massima 0 allora si esce
   if( max_bandwidth_ == 0 )
      return;
      
   Node* currentNode;
   int nodesNum = nodesVect_.size();
   //cout << "dimensione: " << nodesNum << endl;
   
   
   // l'indice j segnala il numero di iterazioni
   // a partire dal nodo i, l'accesso al nodo,
   // nel corpo del for e' gestito in modo circolare
   // esempio con 6 nodi:
   //     se si parte dal nodo con indice 2 per effettuare 6
   //     iterazioni si finirà nel nodo con indice 1
   srand( long(time(0))  );
   int j = ( (int(lrand48()) % max_bandwidth_) + 1 );
   int i = ( int(lrand48()) % nodesNum);
   
   for ( i ; i < j; i++ )
   {
      currentNode = nodesVect_[(i % nodesNum)];
      
      if ( bandWidth() != 0 )
      {
         // se il nodo corrente e' in uno stato di sospensione
         // il metodo Node::send() ritorna un valore NULL
	 // ( e decrementa il contatore
         // dei cicli di attesa del nodo )
         Packet* tosend = currentNode->send();
         if( tosend != NULL )
            if ( enqueue( tosend ) == true )
	       cout << "Pacchetto accodato da " << currentNode->getAddress()
	            << ", banda libera: " << bandWidth() << endl;
            else
	       ;//cout << "Impossibile accodare il pacchetto" << endl;
      }
      else
      {
         // inizializzo il tempo casuale fra 1 e 5
         // per il quale un nodo restera' sospeso nel caso
         // di banda piena.
	 // storicamente, nelle lan Ethernet il tempo di attesa
	 // nel caso di una collisione e' compreso fra 1 e 16 ms
         srand48( long(time(0)) + i ); 
         int suspendTime = ( (int(lrand48()) % 5) + 1 );
         currentNode->suspend( suspendTime );
      }

   }
   
   // una decisione vagamente casuale di quanti pacchetti
   // recapitare per ogni ciclo di spedizioni
   j = ( int(lrand48()) % max_bandwidth_ );
   
   for ( int i=0; i < j; i++)
       deliverPacket();
   //cout << "bandWidth: " << bandWidth() << endl;
   
}