/*
 * Fichero:     Pares.java
 * Fecha:       29/11/06
 * Autor:       Alvaro Sarmiento Losada
 * Asignatura:  Proyecto Fin de Carrera
 * Descripción: Contiene la clase que representa un par jxta ,
 *              ofreciendo métodos que permiten añadir el par al grupo,configurarlo,
 *              finalizar el par
 *
 * Este fichero pertenece al proyecto fin de carrera del autor,
 * consistente en el estudio de JXTA y su aplicación al comercio 
 * electrónico b2b.
 */

package com.pfc.lib.p2p;

import com.pfc.lib.utiles.UtilesDirectorio;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import net.jxta.discovery.DiscoveryService;
import net.jxta.exception.ConfiguratorException;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.rendezvous.RendezVousService;


/**
 * Clase que representa un par jxta. Distribuye las funcionalidades de red del par según su destinatario.
 * @author  Alvaro Sarmiento Losada
 */
public class Pares {

	 /**Gestiona la correción de las diversas acciones .*/
    private boolean correcto = true;
    /** Almacena si este objeto está conectado a la red JXTA */
    protected boolean conectado = false;
    /** Nombre del nodo.*/
    private String nombre = "";
    /** PeerGroup padre del grupo de la aplicación.*/
    public PeerGroup netpg = null;
    /** Grupo de la aplicación.*/
    public PeerGroup grupoCatalogo = null;
    /** Variable que permite descubrir anuncios y recursos del grupo principal en la red.*/
    private DiscoveryService discoGrupo = null;
    /** Variable que permite descubrir anuncios y recursos del grupo de la aplicación en la red.*/
    private DiscoveryService discoCatalogo = null;
    /** Permite instanciar un objeto para configurar la plataforma.*/
    private ConfiguraPlataforma cPlat = null;
    /** Anuncio del grupo.*/
    private PeerGroupAdvertisement anuncioGrupo = null;
    /** Puerto Http que utiliza el nodo.*/
    private int ptoHttp;
    /** Puerto Tcp que utiliza el nodo.*/
    private int ptoTcp;
    /** Representa un identificador del nodo.*/
    private PeerID idPar;
    /** Fichero en donde guardaremos los distintos nombres que le demos al par*/
    private File log_clientes;
    
    /** Crea una nueva instancia de esta clase, inicializando los distintos componentes. */
    public Pares(String nombre) { 
        this.nombre = nombre;
    }
    
    /**
	 * Devuelve el identificador Jxta del nodo
	 * @uml.property  name="idPar"
	 */
    public PeerID getIdPar(){
        return idPar;
    }
    
    /** Permite configurar el par y preparar el acceso del nodo a la red.
     * 
     * @param nombre Nombre del nodo a configurar.
     * @param password Contraseña del nodo.
     * @param pHttp Puerto http utilizado por el nodo.
     * @param pTcp Puerto Tcp utilizado por el nodo.
     * @param nombregrupo El nombre que tiene el grupo al que queremos unirnos.
     * @throws ConfiguratorException
     ** @throws Exception
     *@return True si no ha habido problemas en la configuración.Eoc False.
     */
    public boolean configuraPeer(String nombre,String password,int pHttp, int pTcp,String nombregrupo) throws ConfiguratorException, Exception{
        
        boolean correcto = true;
        this.nombre = nombre;
        //this.pathCarpeta = pathCarpeta;
        ptoHttp = pHttp;
        ptoTcp = pTcp;
        // Borramos la caché almacenada anteriormente en el nodo
        limpiarCache();
        // Configuramos la plataforma
        cPlat = new ConfiguraPlataforma(nombre,password,ptoHttp, ptoTcp);
        if ((correcto =cPlat.ConfiguraPar(netpg))){
            // Obtenemos el grupo por defecto
            this.netpg = cPlat.netpg;
            // Nos adherimos al grupo de la aplicación
            unirseGrupo(nombregrupo);
            // Configuramos el nodo como rendezvous si procede
            configuraRendezVous(grupoCatalogo);
            this.idPar = grupoCatalogo.getPeerID();
            if (grupoCatalogo.getRendezVousService().isRendezVous())
                System.out.println("Es un rdv");
            else
                System.out.println("No es un rdv");
            
            
        }
        
        return correcto;
        
    }
    
    /** Método para limpiar la caché*/
    public boolean limpiarCache() {
       
        System.out.println("conectado:"+conectado);
        return ( !conectado ? UtilesDirectorio.borrarDirectorio(".jxta")
                            : false);
    }
    
    
    /** Método que en caso de que exista un nodo RendezVous conecta el peer actual a él.
     *En otro caso configura el actual como nodo RendezVous.
     *
     * @param peerGroup grupo del nodo actual
     * @throws Exception 
     */
    public static boolean configuraRendezVous(PeerGroup peerGroup) throws Exception {
        
        RendezVousService rdv = peerGroup.getRendezVousService();
        
        if (rdv == null) {
            throw new RuntimeException("No está configurado el servicio RendezVous");
        }
        if(rdv.isRendezVous() || rdv.isConnectedToRendezVous() ) {
            return true;
        }
        int count = 0;
        while (!rdv.isConnectedToRendezVous()){
            Thread.currentThread().sleep(1*1000);
            if (++count >= 30){
                rdv.startRendezVous();
                return true;
            }
        }
        return rdv.isConnectedToRendezVous();
    }
    
   
    
    /** Método que en caso de que el grupo de la aplicación(nombregrupo) esté ya instanciado se adhiere a él.En caso contrario,
     * lo instancia y se adhiere, dejándolo listo para que otros peers se puedan conectar. 
     * 
     * @param nombregrupo El nombre que le vamos a poner al grupo que se forma o al que queremos unirnos 
     */
    private boolean unirseGrupo(String nombregrupo){
        
        // Máximo número de intentos de descubrimiento
        int contador = 3;
        // Tiempo de espera entre cada descubrimiento.En total esperamos durante 9 segundos buscando alguna instancia de nuestro grupo
        int tiempoEspera = 3000;
        //Almacena los peers descubiertos
        Enumeration anuncioDescubierto = null;
        // Variable que determina si el grupo se creó o no
        boolean grupoCreado = false;
      
        discoGrupo = this.netpg.getDiscoveryService();
        //Iteramos hasta que encontremos el anuncio del grupo o alcancemos el número máximo de intentos
        while (contador-->0  && grupoCreado != true){
            try{
                    /* Buscamos en los anuncios locales
                     *Buscamos un anuncio en el que el tag Nombre tiene un valor PrincipalFrame
                     *El método devuelve cualquier anuncio PeerGroup d ela caché en el que se cumpla lo anterior. */
                anuncioDescubierto = discoGrupo.getLocalAdvertisements(DiscoveryService.GROUP,"Name",nombregrupo);
                if (anuncioDescubierto != null && anuncioDescubierto.hasMoreElements())
                    // Si entre los anuncios locales se creó el grupo
                    grupoCreado = true;
                else{
                    /* Buscamos en anuncios de otros peers
                     * La peticion de descubrimiento se propaga a todos los peers de la subred
                     * y a aquellos peers rendez vous
                     * El argumento es 1 para especificar que solo hay un anuncio peergroup PrincipalFrame. */
                    discoGrupo.getRemoteAdvertisements(null,DiscoveryService.GROUP,"Name",nombregrupo,1,null) ;
                    
                     /* El nodo se duerme para dar tiempo a los otros peers a responderle
                      * Dormimos el nodo durante un momento antes de chequear en la cache si hemos encontrado respuesta
                      * Al recibir una respuesta el anuncio es automaticamente guardado en la cache local. */
                    try{
                        Thread.sleep(tiempoEspera);
                    }catch(InterruptedException ie){
                        //FicheroLog.escribirLinea(-1,ie.toString());
                        correcto = false;
                        ie.printStackTrace();
                    }
                    
                }
            }catch(Exception e){
               // FicheroLog.escribirLinea(-1,e.toString());
                correcto = false;
                e.printStackTrace();
            }
        }
        
        /* Si no hubo problemas y el grupo no se había creado anteriormente
         * Se crea el grupo y se publica el anuncio del Nodo. */
        if (correcto && !grupoCreado){
            correcto = crearGrupoCatalogo(discoGrupo,nombregrupo);
            try{
                discoGrupo.publish(grupoCatalogo.getPeerAdvertisement());
                discoGrupo.remotePublish(grupoCatalogo.getPeerAdvertisement());
            }catch(Exception e){
                //FicheroLog.escribirLinea(-1,e.toString());
                e.printStackTrace();
                correcto = false;
            }
        }
        
       /* Usamos el anuncio para instanciar el grupo(aunque se llame a newgroup lo que hacemos es join)
        * No es preciso publicar. */
        
        else if (correcto && grupoCreado) {
            try{
          
                anuncioGrupo = (PeerGroupAdvertisement)anuncioDescubierto.nextElement();
                grupoCatalogo = netpg.newGroup(anuncioGrupo);
                discoCatalogo = grupoCatalogo.getDiscoveryService();
                discoGrupo.publish(grupoCatalogo.getPeerAdvertisement());
                discoGrupo.remotePublish(grupoCatalogo.getPeerAdvertisement());
            }catch(Exception e){
               
                correcto = false;
                e.printStackTrace();
            }
        }
        
        return correcto;
    }
    
    /** Crea el grupo de la aplicación.Se debe evitar la situacion en la que diferentes peers cree diferentes versiones de un mismo grupo
     * 
     *@param discoGrupo Permite realizar descubrimientos de anuncios y recursos en la red 
     *
     */
    private boolean crearGrupoCatalogo(DiscoveryService discoGrupo,String nombregrupo){
        
        try {
            /* Creamos una implementación de un anuncio del PeerGroup general
             * Creamos el nuevo grupo, mediante el anuncio anterior.Especificamos el nombre del grupo*/
            ModuleImplAdvertisement implAdv =  netpg.getAllPurposePeerGroupImplAdvertisement();
            grupoCatalogo = netpg.newGroup( null, implAdv, nombregrupo, "Fs");
            
            /* Ahora que el grupo se a creado es automáticamente publicado y almacenado localmente
             * Al igual es publicado remotamente para que otros peers puedan descubrirlo y adherirse a él. */
            discoGrupo.remotePublish( grupoCatalogo.getPeerGroupAdvertisement() );
        } catch (Exception e) {
            e.printStackTrace();
            correcto = false;
        }
        
        
        return correcto;
    }
    
    
    /** Método en donde se guardará el nombre del par en un fichero
     * 
     * @param nombre El nombre del par a guardar
     */
    public void guardarEnFichero(String nombre){
        try{
           	 
       		log_clientes=new File("clientes.txt");
       		
           		 FileWriter escritor = new FileWriter(log_clientes,true);                      		
           		 escritor.write(nombre);
           		 escritor.write("\r\n");
           		 escritor.flush();     
       		
       	 
        }catch(Exception e){
          System.out.println("Error a escribir en fichero" + e.getMessage());
        }
    }
    
    
    /** Se obtienen los distintos nombres que ha tenido el par en la red JXTA
     * 
     * @return Un listado de nombres
     */
    public String[] leerNombresPeer(){
    	String []resultado=new String[100];
    	log_clientes=new File("clientes.txt");
		
    	try{
    		if(log_clientes.exists()){
		    	BufferedReader br = new BufferedReader(new FileReader(log_clientes));
		    	String s;
		   	      
		   	 	int i=0;
		   	 	while ((s = br.readLine())!= null) {
		   	     resultado[i]=s;
		   	      i++;
		   	 	}
    		}else
    			resultado[0]="0";
    	}catch(IOException e){}
    	
    	return resultado;
    }
    
}
