import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* <p>
* Classe representant les échanges UDP entre utilisateurs.
* </p>
*/

public class UDPEchange {
	
	/**
     * <p>
     * Méthode permettant d'envoyer un message à tout les utilisateurs
     * à l'aide du protocole UDP
     * </p>
     * 
     * @param broadcastAdress l'addresse de broadcast du réseau
     * @param broadcastMessage correspond au message à transmettre aux utilisateurs
     * @param port : port destination 
     * 
     */
	public static void connexion( InetAddress broadcastAdress , String broadcastMessage , Integer port) throws IOException {
		// Envoie en broadcast à tous les utilsateurs
		DatagramSocket socket = new DatagramSocket();
		socket.setBroadcast(true);
		byte[]buffer = broadcastMessage.getBytes();
	    DatagramPacket packet = new DatagramPacket( buffer, buffer.length,  InetAddress.getLoopbackAddress(), port );
	    socket.send(packet);
	    socket.close();
	    System.out.println("***********Message envoye***********");
	    System.out.println("Dest Ip: " + broadcastAdress.toString());
	    System.out.println("Dest port: " + port.toString());
	    System.out.println("Contenu: ");
	    System.out.println(broadcastMessage);
	    System.out.println("************************************");
	}
	
	/**
     * <p>
     * Méthode permettant la reception de messages d'utilisateurs
     * à l'aide du protocole UDP
     * </p>
     * 
     * @param app L'application de chat de l'utilisateur qui receptionne le message
     * 
     */
	public static void ecouteUDP(ChatApp app)
	{
		DatagramSocket socket = null;
		ExecutorService exec = Executors.newFixedThreadPool(1000);
		try {
			socket = new DatagramSocket(app.getMe().getPort());
		} catch (SocketException e1) {
			e1.printStackTrace();
		} 
		byte buffer[] = new byte[1024];
		System.out.println("Ecoute sur le port: "+app.getMe().getPort().toString());
	      while(true) 
	      { 
	        DatagramPacket data = new DatagramPacket(buffer,buffer.length); 
	        try {
				socket.receive(data);
			} catch (IOException e) {
				e.printStackTrace();
			}
	        // Un thread est lancé à chaque reception d'un message
	        System.out.println("Message recu!");
	        exec.submit(new RunnerUDP(data,app));
	      } 
	}
	
	public static void envoiUnicast( InetAddress broadcastAdress , String broadcastMessage , Integer port) throws IOException {
		// Envoie en broadcast Ã  tous les utilsateurs
		DatagramSocket socket = new DatagramSocket();
		byte[]buffer = broadcastMessage.getBytes();
	    //DatagramPacket packet = new DatagramPacket( buffer, buffer.length,  InetAddress.getLoopbackAddress(), port );
		DatagramPacket packet = new DatagramPacket( buffer, buffer.length,  broadcastAdress, port );
	    socket.send(packet);
	    socket.close();
	    System.out.println("***********Message envoye***********");
	    System.out.println("Dest Ip: " + broadcastAdress.toString());
	    System.out.println("Dest port: " + port.toString());
	    System.out.println("Contenu: ");
	    System.out.println(broadcastMessage);
	    System.out.println("************************************");
	}
}

/**
* <p>
* Classe implémentant l'interface Runnable. 
* Contient les traitements à executer dans un thread lancer par des méthodes de la class UDPEchange
* </p>
*/
class RunnerUDP implements Runnable {
    final DatagramPacket data ;
    ChatApp app ; 

    public RunnerUDP(DatagramPacket data, ChatApp app) {
        this.data= data;
        this.app = app ;
    }
    
    /**
     * <p>
     * Méthode qui redefinie les traitements qui seront exécutés dans le thread:
     * Met à jour la liste des utilisateurs actifs
     * </p>
     * 
     */
    @Override
    public void run() {
        System.out.println("Thread started");
        String received = new String(data.getData(), 0, data.getLength());
        System.out.println("***********Message recu***********");
        System.out.println(received); 
        System.out.println("**********************************");
        String Type = received.split("\n")[0];
        
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Demande de connexion****************************************
        //************************************************************************************************************************
        //****************************************
        if (Type.equals("Connexion")) { // un utilisateur vient d'arriver sur le reseau
	        System.out.println("Reception d'une demande de connexion");
        	Utilisateur u =  Utilisateur.stringToUtilisateur(received.split("\n")[1]);
        	String reponse = "Reponse Connexion\n";
        	if (!( app.getActifUsers() ).verifierUnicite(u.getPseudo())) {
        		System.out.println("Pseudo deja present dans la liste");
        		reponse += "false\n";
        	}
        	else {
        		System.out.println("Ajout d'un nouvel utilisateur dans la liste des Utilisateurs");
        		( app.getActifUsers() ).addList(u);	
        		reponse += "true\n";
        		
        	}
        	reponse += app.getMe().toString();
        	
        	try {
    			UDPEchange.envoiUnicast(u.getIp(),reponse,u.getPort());
    		}catch(IOException e)
    		{
    			System.out.println("Echec de l'envoi du message");
    		}
	        
        	( app.getActifUsers() ).afficherListeUtilisateurs();
        }
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Reponse d'une connexion****************************************
        //************************************************************************************************************************
        //************************************************************************************************************************
        
        if (Type.equals("Reponse Connexion")) { // Un utilisateur te repond suite à ta demande de connexion
        	
        	if((received.split("\n")[1]).equals("true")) {	
        			Utilisateur u =  Utilisateur.stringToUtilisateur(received.split("\n")[2]);
        			app.getActifUsers().addList(u);
        			app.getActifUsers().afficherListeUtilisateurs();
        	}
        	else {
        		System.out.println("Pseudo deja pris");
        		System.out.println("Connexion echoue");
        	}
        }
        
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Demande de modification d'un pseudo****************************************
        //************************************************************************************************************************
        //************************************************************************************************************************
        
        if (Type.equals("Modification Pseudo")) {
        	if(( app.getActifUsers() ).verifierUnicite(received.split("\n")[2])) {
        		( app.getActifUsers() ).modifierList(received.split("\n")[1],received.split("\n")[2]);
        		Utilisateur Destination = ( app.getActifUsers() ).getPseudoList(received.split("\n")[1]);
        		String Message = "Bon Choix Pseudo\n" + received.split("\n")[2] ;
        		System.out.println(Message);
        		try {
        			
					//UDPEchange.envoiUnicast(Destination.getIp(),Message, Destination.getPort());
        			UDPEchange.envoiUnicast(InetAddress.getLoopbackAddress(),Message, 4000);
				} catch (IOException e) {
					e.printStackTrace();
				}
        	}
        	else {
        		Utilisateur Destination = ( app.getActifUsers() ).getPseudoList(received.split("\n")[1]);
        		String Message = "Mauvais Choix Pseudo\n" ;
        		System.out.println(Message);
        		try {
					UDPEchange.envoiUnicast(Destination.getIp(),Message, Destination.getPort());
				} catch (IOException e) {
					e.printStackTrace();
				}
        	}
        	( app.getActifUsers() ).afficherListeUtilisateurs();
        }
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Mauvais choix d'un pseudo****************************************
        //************************************************************************************************************************
        //****************************************
        

        if (Type.equals("Mauvais Choix Pseudo")) {
        	System.out.println("Ce choix de pseudo est déjà pris il te faut en choisir un autre");
        }
        
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Bon choix d'un pseudo****************************************
        //************************************************************************************************************************
        //************************************************************************************************************************
        
        if (Type.equals("Bon Choix Pseudo")) {
        	
        	( app.getActifUsers() ).supprimerList(app.getMe());
        	  
        	 app.getMe().setPseudo(received.split("\n")[1]);
        
        	( app.getActifUsers() ).addList(app.getMe());
        	System.out.println("Changement pseudo accepte, nouvelle liste des utilisateurs actifs:");
        	app.getActifUsers().afficherListeUtilisateurs();
        }
        
        //************************************************************************************************************************
        //*************************************************************************************************************************
        //****************************************Demande d'une deconnexion****************************************
        //************************************************************************************************************************
        //************************************************************************************************************************
        
        if (Type.equals("Deconnexion")) {
        	( app.getActifUsers() ).supprimerList(Utilisateur.stringToUtilisateur(received.split("\n")[1]));
        }
    }
}
