# -*-coding:Latin-1 -*
"""serveur chat (UDP)"""
import socket
import threading
import os
import time

#### intialisation des variables ####
hote = ''
port = 6660
connectes = {}
taille_paquet = 1024

#### fils secondaires #### 

class DebitMetre(threading.Thread):
    """debitmetre permettant de mesurer le debit d'emission/reception du serveur"""
    def __init__(self, taille_paquet):
        """création du serveur vocal"""
        threading.Thread.__init__(self)
        self.mesureEnvoi = 0
        self.mesureReception = 0
        self.debitEmission = 0.00
        self.debitReception = 0.00
        #si oui, affiche les resultats chaque seconde:
        self.affichage = False
        
    def envoi(self, taille_paquet):
        """pour signaler l'envoi d'un paquet"""
        self.mesureEnvoi += taille_paquet

    def reception(self, taille_paquet):
        """pour signaler la reception d'un paquet"""
        self.mesureReception += taille_paquet

    def run(self):
        """mesure le volume en octets qui transite chaque seconde"""
        self.lance = True
        t0 = time.time()
        while self.lance:   
            delta = time.time() - t0
            if delta >= 1:
                self.debitEmission = self.mesureEnvoi / delta
                self.mesureEnvoi = 0
                self.debitReception = self.mesureReception / delta
                self.mesureReception = 0
                t0 = time.time()
                if self.affichage:
                    print(self)

    def __repr__(self):
        """affiche les derniers debits mesures (en ko/s)"""
        retour = "** Reception: {} ko/s ** -> Emission: {} ko/s **"\
                  "".format((self.debitReception * 0.001), (self.debitEmission * 0.001))   
        return retour    

    def stop(self):
        """Fermeture du fil debitmetre"""    
        self.lance = False

        
class ServeurVoc(threading.Thread):
    """serveur UDP dedie au chat vocal"""
    def __init__(self, port, taille_paquet):
        """création du serveur vocal"""
        threading.Thread.__init__(self)
        self.serveur_lance = False
        self.connectes = []
        self.taille_paquet = taille_paquet
        self.port = port
        
    def creer(self):
        """creation d'une entree serveur UDP"""
        try:
           self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
           self.socket.bind(('', self.port))
           retour = "Le serveur vocal ecoute a present sur le port {}\n".format(self.port)
           self.serveur_lance = True
           self.start()
        except IOError, e:
            if e.errno==10048:
               retour = "\nUne instance du serveur est déja lancée"
            else:   
               retour = "\nechec de lancement du serveur"     
        finally:
            print(retour)
        return retour

    
    def nouveauClient(self, adresse):
        """ajoute un nouveau client a la liste des connectes"""
        self.connectes.append(adresse)
        retour = "Nouveau client connecte: {}\n)".format(adresse)
        print(retour)
        return retour
    
    def supprimerClient(self, adresse):
        """ajoute un nouveau client a la liste des connectes"""
        self.connectes.remove(adresse)
        retour = "Client deconnecte: {}\n)".format(adresse)
        print(retour)
        return retour


    def run(self):
        """boucle de fonctionnement du serveur"""
        while self.serveur_lance:
            #on recoit les donnees envoyees 
            try:
               data, adresse = self.socket.recvfrom(self.taille_paquet)
               dM.reception(len(data))
            except IOError, e:
               if e.errno == 10022:  
                 # print("erreur de reception {}".format(adresse))
                 #self.supprimerClient(adresse)
                  time.sleep(0.02)
               else:
                  print("erreur {}".format(e.errno)) 

            if data:
                if not adresse in self.connectes:
                    #l'adresse n'est pas dans la liste: on l'ajoute aux connectes
                    self.nouveauClient(adresse)
                    #on lui envoie les parametres de la co?
                    
                else:
                    for dest in self.connectes:
                        if dest != adresse:
                            #le client ne recoit pas ses propres donnees
                            self.socket.sendto(data , dest)
                            dM.envoi(len(data))
                            #tmp = "{} -> {}".format(len(data), adresse)

        
    def stop(self):
        """Fermeture du serveur"""
        self.serveur_lance = False
        self.socket.close()
        print("\nFermeture du serveur vocal")

        
class FilClient(threading.Thread):
    """fil de connexion client au serveur TCP"""
    def __init__(self, cnn):
        """creation du fil"""
        threading.Thread.__init__(self)
        self.cnn = cnn
        
    def run(self):
        """echange avec le client"""
        try:
           nom = self.getName()
           while True:
               #reception du message client
               msgClient = self.cnn.recv(1024)
               dM.reception(len(msgClient))
               
               #on n'affiche pas si c'est du transfert de fichier ou d'objet
               if len(msgClient) > 0 and msgClient[0:1] != "f" and msgClient[0:1] != "o": 
                  print("{}> {}".format(nom, msgClient))
               
               try:
                  #fait suivre le message a tous les clients sauf l'emetteur:
                   #excepte si c'est une information
                  for cle in connectes:
                      if cle != self or msgClient[0:1] == "i":
                          connectes[cle].sendall(msgClient)
                          dM.envoi(len(msgClient))
               except RuntimeError:
                  #pour gérer le changement de taille du dictionnaire 'connectes'
                  #lors de l'arrivee ou du depart d'un client 
                  print("Changement de taille du dictionnaire") 

        except:
            print("Erreur {}".format(self.getName()))
            self.stop()

    def stop(self):
        """ferme le fil client"""
        #fermeture de la connexion avec le client:
        self.cnn.close()
        #on supprime le client de la liste des connectes
        del connectes[self]
        print("Client {} deconnecte.".format(self.getName()))

# Le fil se termine ici    

class Serveur(threading.Thread):
    """serveur TCP pour chat, echange de fichiers ou d'objets..."""
    def __init__(self, port):
        """creation du serveur"""
        self.serveur_lance = False
        threading.Thread.__init__(self)
        self.port = port

    def creer(self):
        """demarre le serveur"""
        try:
           self.cnn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
           self.cnn.bind((hote, self.port))
           self.cnn.listen(5)
           retour = "Le serveur ecoute a present sur le port {}".format(port)
           self.serveur_lance = True
           self.start()
        except:
           retour = "Impossible de demarrer le serveur"
        finally:   
           print(retour)

        return retour

    def run(self):
        """boucle de fonctionnement"""
        while self.serveur_lance:
            try:
               #tant que le serveur est lance, il reste disponible a de nouvelles connexions
               cnnClient, adresse = self.cnn.accept()
               #on cree un nouveau fil pour le nouveau client
               client = FilClient(cnnClient)
               client.start()
               #et on ajoute son adresse reseau a la liste des connectes
               nom = client.getName()
               connectes[client] = cnnClient
               print("Client {} connecte, adresse IP {}, port {}.".format(nom, adresse[0], adresse[1]))
            except:
               break        
        
    def stop(self):
        """ferme le serveur"""
        self.serveur_lance = False
        for client in connectes.keys():
            client.stop()
        self.cnn.close()
        print("\nFermeture du serveur TCP")
        
#### fil principal ####
if __name__ == "__main__":
   # si lancement direct:
   #debitmetre pour mesurer l'emission
   dM = DebitMetre(taille_paquet)
   dM.affichage = True
 
   s = Serveur(port)
   s.creer()
   sVoc = ServeurVoc(port, taille_paquet)
   sVoc.creer()
   dM.start()
   
   os.system("pause")
   s.stop()
   sVoc.stop()
   dM.stop()
   print("- Fin -")   
   time.sleep(1)


   
   
   




    
 

