/**@file*/
/*
TelepatiTcp ver 1.5

Copyright (C) 2005 Gilberto Decaro
email: msgid@email.it

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef TelepatiTcp_h_DEFINED
#define TelepatiTcp_h_DEFINED

#include <OPENR/OObject.h>
#include <OPENR/OSubject.h>
#include <OPENR/OObserver.h>

/*Strutture delle connessioni usate da Telepati*/
#include "TCPConnection.h"  
/*Header per la gestione dello stack TCP/IP di IPStack*/
#include <ant.h>  
/*tipi di Endpoint*/
#include <EndpointTypes.h>  
/*package usati da Telepati i TelepatiPkges*/
#include <Packages.h>
/*Tipi di messaggi TCP*/ 
#include <TCPEndpointMsg.h>

//***
#include <fstream>

#include <vector> 
#include <queue>
#include "def.h"

/**Header della classe che si occupa della stampa delle statistiche di
   rete*/
#include "EtherStat.h"

/**Caratteri di Escape ANSI di inizio. Attiva la colorazione ANSI del
   testo che lo segue. (32 = green)*/
#define TLPT_BEG_COLOR "\e[32m" 
/**Caratteri di Escape ANSI di fine. Ristabilisce il colore di default
   dei caratteri che lo seguono. (37 = white).*/
#define TLPT_END_COLOR "\e[0m"
/**Nome dell'oggetto colorato utilizzando i colori ANSI. Utilizzato
   per la stampa dei messaggi sulla console telnet per permettere un
   piu' facile riconoscimento dell'oggetto che ha stampato il
   messaggio.*/
#define TLPT_NAME TLPT_BEG_COLOR"TelepatiTcp"TLPT_END_COLOR


using namespace std; /**<uso il namespace standard*/

/**file di configurazione della rete di Aibo. E' una lista di
   indirizzi IP, uno per riga, che corrispondo agli idirizzi degli
   Aibo con cui si vuole poter comunicare*/
const char* const AIBONET="/MS/OPEN-R/MW/CONF/AIBONET.CFG";

/** Oggetto per la comunicazione TCP via Rete Wireless tra Aibo.  E'
    la versione basata sul protocollo TCP di Telepati.  Si occupa di
    gestire la comunicazione di agenti cooperanti attraverso il
    protocollo TCP e la rete Wireless. E' un oggetto OPENR pertanto e'
    un processo che lavora in concorrenza con gli altri oggetti OPENR
    presenti nell'Aibo e comunica con loro attraverso lo scambio di
    messaggi (per informazioni sulla comunicazione inter-object e
    l'utilizzo di TelepatiTcp guardare il file Telepati1.1.doc).
    Attiva per ogni Aibo con cui comunicare una connessione per la
    spedizione ed una connessione per la ricezione.  La numerazione
    delle porta TCP e' automatica: se l'i-esimo aibo vuole comunicare
    con il j-esimo, con i e j-esimo ci si riferisce al loro numero di
    riga nel file AIBONET, allora la porta a cui deve collegarsi sara'
    data da:@n TELEPATI_BASE_PORT+(\<riga del i-esimo\> *
    \<numero_totale_righe\>)+ \<riga del j-esimo\>.*/
class TelepatiTcp : public OObject {
 public:
    
  /**Costruttore di Default*/
  TelepatiTcp();
  /**Distruttore di default*/
  virtual ~TelepatiTcp();
  
  /** Array dei subject dell'oggetto*/
  OSubject*    subject[numOfSubject]; 
  /** Array degli observer dell'oggetto*/
  OObserver*   observer[numOfObserver];

  /**Entry Point Standard: Inizializza l'oggetto e le strutture per la
     comunicazione inter-object e di Rete. @return oSUCCESS se ha
     avuto successo; oFAIL altrimenti*/
  virtual OStatus DoInit   (const OSystemEvent& event); 
  /**Entry Point Standard: Attiva la comunicazione con gli altri
     Oggetti e via Rete. @return oSUCCESS se ha avuto successo; oFAIL
     altrimenti.*/
  virtual OStatus DoStart  (const OSystemEvent& event); 
  /**Entry Point Standard: Ferma l'oggetto e la comunicazione. @return
     oSUCCESS se ha avuto successo; oFAIL altrimenti.*/
  virtual OStatus DoStop   (const OSystemEvent& event); 
  /**Entry Point Standard: Distrugge le strutture inizializzate per
     l'inter-object comunication. @return oSUCCESS se ha avuto
     successo; oFAIL altrimenti.*/
  virtual OStatus DoDestroy(const OSystemEvent& event); 
  
  /**Extra Entry Point: spedisce il messaggio. Quando l'oggetto OPENR
     IPStack (che si occua della gestione dello stack TCP/IP) avra'
     copiato tutti i dati nei suoi buffer, e saranno quindi
     riutilizzabili i buffer delle connessioni di spedizione, verra'
     invocata questa funzione. @param msg puntatore al messaggio
     spedito.*/
  void SendCont(ANTENVMSG msg);  
  /** Extra Entry Point: riceve il messaggio. Riceve dalla rete i
      messaggi attraverso IPStack e, se e' un messaggio di dati, lo
      inoltra all'oggetto observer.*/
  void ReceiveCont(ANTENVMSG msg); 
  /** Extra Entry Point: Attiva le ricezioni. Ha il compito di
      attivare la ricezione TCP dell'Aibo, viene avviata quando l'oggetto
      IPStack riceve una richiesta di connessione TCP all'Aibo stesso.*/ 
  void ListenCont(ANTENVMSG msg);  
  /** Extra Entry Point: Connette l'Aibo. Ha il compito di connettere
      l'Aibo con gli altri Aibo, viene avviata quando l'oggetto
      IPStack e' pronto a stabilire una connessione con l'Aibo
      specificato nel parametro msg.*/
  void ConnectCont(ANTENVMSG msg);  
  /**Extra Entry Point: chiamato allo scadere del timer periodico di
     Statistiche di Rete. E' attiva nel caso venga definita in
     compilazione OPENR_ETHERSTAT (gcc -DOPENR_ETHERSTAT) e stampa
     sulla console virtuale informazioni sullo stato della rete
     wireless.*/
  void EtherStat(void* msg);  

  /**Entry Point Standard: attua la comunicazione inter-object. Viene
     invocata al ricevimento di un messaggio da parte un oggetto
     OPEN-R subject e si occupa di spedire il messaggio attraverso la
     rete chiamando la funzione send(i) che spedisce il messaggio
     all'i-esimo Aibo attraverso la i-esima connessione di
     spedizione.*/
  void SendNotify(const ONotifyEvent& event);  
  /** Funzione di Assert Ready. Viene invocata al ricevimento da parte
      dell'observer dell'Assert Ready o del Deassert Ready.*/
  void ObserverReady(const OReadyEvent& event); 

  /** Dimensione in byte dei buffer di ricezione/spedizione*/
  static const int TELEPATI_BUFFER_SIZE= 65535;  
  /** Durata, in sec, del timer ciclico per il rilevamento dello stato
      di attivita' degli Aibo (vedi documentazione) @see
      BoneTimerEnd.*/
  static const longword TELEPATI_BONE_TIME= 8; 
  /** Numero base delle porte TCP. Rappresenta il numero base da cui
      parte la numerazione delle porte di ricezione / spedizione
      utilizzate dal protocollo TCP. */
  static const int TELEPATI_BASE_PORT= 30000;  
  /** Numero massimo di errori di connessioni consentite. Superato il
      suo valore non verra' ritentata una riconnessione verso
      l'i-esimo Aibo.*/
  static const int TELEPATI_MAX_CONNECT_ERROR= 50; 
  /** Numero massimo di messaggi consecutivi all'observer. Rappresenta
      il numero massimo di messaggi che possono essere mandati
      all'observer senza prima ricevere un Asser Ready.*/
  static const int TELEPATI_MAX_MESSAGE_TO_OBSERVER = 20; 
  /** Tempo di attesa di riconnessione. Tempo di attesa tra un
      tentativo di connessione ed un altro sulla stessa porta;
      espresso in nano secondi.*/
  static const longword TELEPATI_RECONNECT_TIME= 800000000; 
  
 private:
  /*--- Variabili per le statistiche di comunicazione ---*/
  unsigned int sendMessage;          /*numero di messaggi spediti*/	    
  unsigned int totalRecvMessage;     /*numero di messaggi totali ricevuti*/ 
  unsigned int dataRecvMessage;      /*numero di messaggi di dati ricevuti*/
  unsigned int byteSended;           /*numero di byte spediti*/
  unsigned int byteReceived;         /*numero di byte ricevuti*/
  int sendSpeed;                     /* velcita' di spedizione in byte/s*/
  /* memorizza il momento in cui e' iniziata la spedizione del pkg
     della i-esima connessione*/
  SystemTime** startSendTime;        

  /* Vettore di indirizzi IP degli AIBO*/
  vector<IPAddress> aiboIp;          
  /* Array di connessioni per la ricezione*/
  TCPRecvConnection* recvConnection; 
  /* Array di connessioni per la spedizione*/
  TCPSendConnection* sendConnection; 
 /* Riferimento allo stack IP (richiesto da OPENR)*/
  antStackRef ipstackRef;        
  /* Stato di attivita' degli aibo presenti. Array di aiboState,in link
     con aiboIp*/
  aiboState* aiboIpActive;          
  /* Numero di aibo presenti nel campo meno 1 (escluso colui su cui
     gira Telepati)*/
  int numaibo;                     

  /* Coda di TelepatiPkg. Si occupa della memorizazione dei messaggi
     in attesa di essere spediti*/
  queue<TelepatiPkg*> sendQueue;   
  /* Coda dei messaggi in attesa di essere spediti all'observer.*/
  queue<TelepatiPkg*> interObjectQueue; 

  /* Array di byte dove l'i-esimo elemento rappresenta il numero di
     errori di connessione della i-esima connessione di spedizione. */
  byte* numOfReconnect;  

  /* Array di buffer di byte contenente "pezzi" di messaggi. Usato in
     ReceiveCont per poter riassemblare i messaggi che vengono
     tagliati in quanto spediti in due pacchetti TCP differenti.*/
  byte** chunk;
  /* Dimensione del buffer chunk.*/
  size_t* chunkSize;

  /* Il mio indirizzo IP*/
  IPAddress* myIp;               

  /* Buffer per il parsing dei file*/
  static const size_t LINEBUFFERSIZE= 256; 
  
  /* Parser del file di configurazione. Imposta e inizializza numaibo,
     aiboIp, aiboIpActive, numOfReconnect, startSendTime, chunk e
     chunkSize e imposta le porte di comunicazione TCP delle varie
     connessioni. @return oSUCCESS se ha avuto successo; oFAIL
     altrimenti.*/
  OStatus parseIpConfigFile();       
  /* Inizializza le connessioni di spedizione e di ricezione. @return
     oSUCCESS se ha avuto successo; oFAIL altrimenti.*/
  OStatus initConnections();       
  /* Attiva la ricezione della i-esima connessione. @return oSUCCESS
     se ha avuto successo; oFAIL altrimenti.*/
  OStatus listen(int i);         
  /* Connette l'i-esima connessione. @return oSUCCESS se ha avuto
     successo; oFAIL altrimenti. */
  OStatus connect(int i);           
  
  /* Spedisce il messaggio. Imposta IPStack mandandogli i dati da
     spedire attraverso la connessione di spedizione corretta, non
     appena il buffer di spedizione di IPStack sara' riempito verra'
     invocata SendCont(). In caso la connessione sia in spedizione
     accoda il messaggio nella coda sendQueue.@param msg messaggio da
     spedire*/
  void send(TelepatiPkg* msg); 
  
  /* Avvia la ricezione sulla i-esima connessione. Imposta IPStack in
     modo da restare in ascolto sulla rete, non appena verra' ricevuto
     un pacchetto verra' invocata la funzione ReceiveCont().*/
  void receive(int i);          
  
  /* Fa il merge del buffer contenuto nel messaggio con il buffer di
     chunk della i-esima connessione. @param msg messaggio TCP su cui
     fare il merge. @param connID id della connessione.*/
  byte* mergeWithChunk(const TCPEndpointReceiveMsg* msg, int connID);
  /* Salva nel chunk della connID connessione i byte contenuti in ptr
     a partire dall'offset offset. @param ptr buffer contenente i dati
     da copiare. @param offset l'offset di ptr da cui iniziare a
     copiare i dati. @param size quanti byte copiare. @connID id della
     connessione. */
  void saveInChunk(const byte* ptr, int offset, int size, int connID);
  
  /* Manda un ByeMsg (msg di arrivederci) all'i-esimo Aibo; blocca
     l'esecuzione dell'oggetto fino al completamento della
     spedizione. @param i aibo a cui mandare il byte message*/
  void sendByeMsg(int i);
  /* Manda il package agli observer se pronti a riceverlo, altrimenti
     lo acconda. @param pkg package da mandare.*/
  void sendToObserver(TelepatiPkg* pkg);
  /* Manda un HiMsg (msg di saluto) all'i-esimo Aibo. @param i aibo a
     cui mandare l'hy message*/
  inline void sendHiMsg(int i){
    OSYSDEBUG((TLPT_NAME"::sendHiMsg(%d)\n",i));
    send(new HiPkg(i));
  };

  /* Manda un HiReplyMsg (msg di risposta al saluto) all'i-esimo
     Aibo. @param i aibo a cui mandare l'HiReply message*/
  inline void sendHiReplyMsg(int i){
    OSYSDEBUG((TLPT_NAME"::sendHiReplyMsg(%d)\n",i));
    send(new HiReplyPkg(i));
  };
  
  /* Manda un AiboInfoMsg, messaggio di informazione sullo stato degli
     Aibo, all'observer di Telepati*/
  void sendAiboInfoMsg();  
  /* Manda un CounterPkg all'observer di Telepati*/
  void sendCounterMsg();
  /* Controlla la presenza di messaggi in attesa di essere spediti e,
     se presenti, li spedisce.*/
  void checkQueue();
  /* Recupera l'indirizzo IP dell'Aibo stesso*/
  IPAddress* retrieveMyIp();     
  /* Imposta l'array aiboIpActive per i-esimo Aibo in base al valore
     passato e se cambia ne informa l'observer. @param aibo aibo di
     cui modificare lo stato di attivita'. @param newVal nuovo stato
     di attivitą. @return true: se ha cambiato stato; false:
     altrimenti.*/
  bool setAiboIpActive(int aibo, aiboState newVal);  
  /* Calcola la velocita' di spedizione, aggiorna e stampa
     sendSpeed. @param aibo indice della connessione di spedizione su
     cui calcolare la velocita'*/
  void computeSpeed(int aibo);

  //***
  ofstream* debugFile;

};

#endif  //TelepatiTcp_h_DEFINED
