
//////////////////////////////////////////////////////
//            Jeu de Morpion Tetedechat V0          //
//Classe Morpion definissant les methodes du serveur//
//////////////////////////////////////////////////////




//Declaration des imports
import java.rmi.*;
import java.rmi.server.*;
import java.util.Vector;
import java.util.*;



/** <B>Caracteristiques :</B><BR>
* 
* Classe regoupant toutes les methodes du jeu sur le serveur.
* 
* @author Audrey Sanz-Vella
* @author Alexandre Martin
* @see Serveur
* @see Client
* @see MorpionDistant
* @see ClientDistant
*/

//Declaration de la classe morpion presente sur le serveur.
//Implements MorpionDistant permet l accession du client a ces methodes
public class Morpion extends UnicastRemoteObject implements MorpionDistant {






    //////////////////////////////////////////////////////////////////////////////////////////

    /** Inner classe a l essai*/
    public class Joueur{


        //Declaration des variables
        private ClientDistant clientDistant;
        private String nomClient;
        private int numeroPion;
        

        /** Constructeur de l'inner Classe
         * @param<code>clientDistant</<code> reference a un client particulier.
         * @param<code>nomClient</<code> reference au nom du clientDistant.
         * @param<code>nomClient</<code> reference au numero de pion du clientDistant.
         */

        public Joueur(ClientDistant clientDistant, String nomClient,int numeroPion){

            //Recuperation des parametres
            this.clientDistant=clientDistant;
            this.nomClient=nomClient;
            this.numeroPion=numeroPion;
        }//Fin constructeur


        /** Methode permettant d'acceder au nom d'un client*/

        String getNomClient(){
            return nomClient;
        }//Fin methode getNomClient


        /** Methode permettant d'acceder a un client distant*/

        ClientDistant getClient(){
            return clientDistant;
        }//Fin methode getClient

        
        /** Methode permettant d'acceder au numero d'un client*/

        int getNumeroClient(){
            return numeroPion;
        }//Fin methode getNumeroClient

    }//Fin de la classe

    /////////////////////////////////////////////////////////////////////////////////////////


    
    
    //Declaration des variables utilisees
    private Vector tableauVirtuel;
    private int nbClients;
    private int nbJoueurs;
    private int joueurJoue;
    private int joueurJoueAncien;
    private int nbCases;
    private int nbCoups=0;
    private int nbCasesVirtuelles;
    private int tableauPlanJeu[][];
    



    /** Constructeur de la classe Morpion*/

    //Constructeur de la classe
    public Morpion() throws RemoteException{
        
        //Creation d une instance du tableau virtuel
        tableauVirtuel=new Vector();
        
    }//Fin Constructeur







    /** Methode ajoutant un client dans le jeu.
     * @param <code>clientDistant</code> reference au client distant.
     * @param <code>nomClient</code> donne un nom au client distant.
     * @param <code>nbJoueursTemp</code> valuer eventuelle du nombre de joueurs.
     */
 
    //Methode qui ajoute un client dans un tableau Vector
    public synchronized void enregistrerClient(ClientDistant client, String nomClient,int nbJoueursTemp){
    
        try{
            //Test
            afficherStringServeur("Le client retardataire est bien arrive...");
            
            //On initialise alors la valeur de la variable nbClients
            setNbClients();
           
            //Test
            afficherStringServeur("Il y a actuellement dans le serveur  ",this.nbClients," client(s).");
        
            //Cas ou le serveur est plein
            if ((this.nbClients==this.nbJoueurs) && (this.nbClients!=0)){
               
                //Initialise la variable reponseServeur du client et lui Envoie un message.
                client.setReponseServeur("Pleing");
                
                System.out.println("trop de monde");
client.afficherStringClient("Le numero que vous avez appele est actuellement en derangement. Veuillez rappeler ulterieurement. Merci !");
            }

            //Cas ou le serveur attend des joueurs
            else{
                
                //Cas ou le serveur ne contient aucun clients
                if (this.nbClients==0){

                    //Definition du nombre de joueurs
                    this.nbJoueurs=nbJoueursTemp;
                        
                    //Ajout du client
                    ajouterClientVector(client,nomClient,this.nbClients);
                }
                
                //Cas ou il existe deja des clients dans le serveur
                else{
                
                    //Ajout du client
                   ajouterClientVector(client,nomClient,this.nbClients);
            
                   //Affichage d'un message client
                   client.afficherStringClient("PFFFF, il faut encore attendre les autres...");

                }
                
                //Cas ou le client est ajoute, on regarde si le nombre de clients est atteint
                if (this.nbClients==this.nbJoueurs){
                        
                    //Test
                    afficherStringServeur("Le nombre de joueur ideal est atteint ! Lancement de l'affichage !");
                
                    //initialisation du tableau representant le jeu
                    initPlanJeu();
                        
                    //Choix aleatoire d un joueur
                    joueurJoue=setJoueurDebut();

                    //Affichage du morpion chez les clients
                    for(int i=0;i<this.nbClients;i++){
                        ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherMorpion(this.nbJoueurs);
                        
                        ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherStringClient("le joueur "+((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getNomClient()+" va commencer la partie. Bonne chance a tous !!!!");
                        }
                    
                    //Donne la valeur true pour le joueur qui doit debuter
                    ((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getClient().setJoueurTour(true);
                }
            }//Fin du else

        }//Fin du try
        
        catch (Exception e){
            e.printStackTrace();
            afficherStringServeur("Probleme de connection avec la methode enregistrerClient!");

        }

    }//Fin de la methode ajouterClient








    /** Methode utilisee pour afficher des informations.
     * @param <code>message1</code> premiere chaine de caracteres a afficher.
     */
    
    //Methode permettant l affichage d une chaine String sur la fenetre du serveur
    public void afficherStringServeur(String message1){
        
        try{
            System.out.println("**************************************");
            System.out.println("                                      ");
            System.out.println(message1);
            System.out.println("                                      ");
        }   
        catch (Exception e){
            e.printStackTrace();
            System.out.println("***************************************");
            System.out.println("                                ");
            System.out.println("Probleme avec la methode afficherStringServeur");
            System.out.println("**************************************");
      }
    }//Fin de la methode afficherStringServeur


    /** Methode utilisee pour afficher des informations.
     * @param <code>message1</code> premiere chaine de caracteres a afficher.
     * @param <code>nombre</code> nombre a afficher.
     * @param <code>message2</code> seconde chaine de caracteres a afficher.
     */

    //Methode permettant l affichage d une chaine String et d une variable int sur la fenetre du serveur
    public void afficherStringServeur(String message1, int nombre, String message2){
        
        try{
            System.out.println("**************************************");
            System.out.println("                                      ");
            System.out.println(message1+nombre+message2);
            System.out.println("                                      ");
        }
        catch (Exception e){
            e.printStackTrace();
            System.out.println("***************************************");
            System.out.println("                                ");
            System.out.println("Probleme avec la methode afficherStringServeur");
            System.out.println("**************************************");
      }
    }//Fin methode afficherStringServeur


    /** Methode utilisee pour afficher des informations.
     * @param <code>message1</code> premiere chaine de caracteres a afficher.
     * @param <code>message2</code> seconde chaine de caracteres a afficher.
     */

    //Methode permettant l affichage de deux chaines String sur la fenetre du serveur
    public void afficherStringServeur(String message1,String message2){
        
        try{
            System.out.println("**************************************");
            System.out.println("                                      ");
            System.out.println(message1+message2);
            System.out.println("                                      ");
        }   
        catch (Exception e){
            e.printStackTrace();
            System.out.println("***************************************");
            System.out.println("                                ");
            System.out.println("Probleme avec la methode afficherStringServeur");
            System.out.println("**************************************");
      }
    }//Fin de la methode afficherStringServeur









    /** Methode permettant de definir un tableau de cases correspondant a l'echiquier*/

    //Methode initialisant le tableau representant le PlanJeu
    public void initPlanJeu(){

        //Initialisation des variables pour le tableau
        nbCasesVirtuelles=((this.nbJoueurs+(this.nbJoueurs-1))+4);
        nbCases=(this.nbJoueurs+(this.nbJoueurs-1));
        
        //Initialisation d un tableau contenant les positions des pions clients
        //pour simplifier l'algorithme de test de cases gagnantes,
        //2 lignes et deux colonnes sont ajoutees autour du damier client.
        //Chaque pion a donc le meme comportement de test
        tableauPlanJeu=new int[nbCasesVirtuelles][nbCasesVirtuelles];
        for(int x=0; x<(this.nbCasesVirtuelles);x++) {
            for (int y=0 ;y<(this.nbCasesVirtuelles);y++) {
                
                //la valeur 7 correspond a une case vide
                tableauPlanJeu[x][y]=7;
            }
        }
    }//Fin methode initPlanJeu










    /** Methode traitant les demandes de client par rapport a une case de l'echiquier.
     * @param <code>caseX</code> numero de la colonne.
     * @param <code>caseY</code> numero de la ligne
     */
 
    //Methode pour regarder si la case jouee est libre
    public synchronized void getCaseLibre(int caseX,int caseY){
        
        try{

            //Cas ou la case choisit est vide
            if (this.tableauPlanJeu[caseX][caseY]==7 ){
                
                //Verifie si il y a un trois
                if(algorithme(caseX,caseY,joueurJoue)==true){
                  
                    //Execute la methode affichant le message gagnant
                    gagnerClient(joueurJoue,caseX,caseY);
                        
                    //Arrete la fonction
                    return;
                }
                
                
                //Ajoute le coups
                this.nbCoups=this.nbCoups+1;
                
                //Met a jour les cases
                updateTableauPlanJeu(caseX,caseY);
                
                //Change le joueur qui a la main
                updateJoueurJoue();
  
                //Affichage du morpion chez les clients
                for(int i=0;i<this.nbClients;i++){

                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherMorPion(((Joueur)(tableauVirtuel.elementAt(joueurJoueAncien))).getNumeroClient(),caseX-3,caseY-3);
                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherStringClient("le joueur "+((Joueur)(tableauVirtuel.elementAt(joueurJoueAncien))).getNomClient()+" a joue. C'est maintenant le tour de "+((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getNomClient()+".");
                }
                      
                //Change le tour des clients
                ((Joueur)(tableauVirtuel.elementAt(joueurJoueAncien))).getClient().setJoueurTour(false);
                ((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getClient().setJoueurTour(true);
                
            }//Fin du cas de la case vide
            else{                           
                
                //Cas ou la case n est pas vide
                ((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getClient().afficherStringClient("EH!"+((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getNomClient()+" t' as perdu tes lunettes ou quoi... La case, elle n est pas vide !!!");
                return;
            }

                    
            //On regarde si il reste encore des cases libres sur l 'echiquier
            if(nbCoups==this.nbCases*this.nbCases){
            
                //Laisse un temps d attente
                Thread.sleep(8000);

                //Reinitialise les clients
                for(int i=0;i<this.nbClients;i++){
                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().setFinPartie("nul",7);
                }
            
                //reinitialise le serveur
                resetServeur();
                return;
            
            }//Fin du cas ou il ne reste pas de cases vides sur le damier
         
        }//Fin try

        catch (Exception e){
            e.printStackTrace();
            afficherStringServeur("Probleme de connection avec la methode getCasesLibres!");
        }
    
    }//Fin Methode de test des cases libres
    








    /** Methode mettant a jour les cases du tableau representant l'echiquier.
     * @param <code>caseX</code> numero de la colonne.
     * @param <code>caseY</code> numero de la ligne.
     */
 
    //Methode mettant a jour les cases du tableau PlanJeu
    public void updateTableauPlanJeu(int caseX,int caseY){

        this.tableauPlanJeu[caseX][caseY]=this.joueurJoue;
    }//Fin methode updateTableauPlanJeu








    /** Methode mettant a jour le joueur ayant la main*/

    //Methode mettant a jour le joueur qui joue
    public void updateJoueurJoue(){

        joueurJoueAncien=joueurJoue;
        
        if(joueurJoue==(this.nbClients-1)){
            
            joueurJoue=0;
            return;
        }
        else{
            joueurJoue=joueurJoue+1;
            return;
        }

    }//Fin de la methode updateJoueurJoue






    /** Methode traitant les pions du joueur.
     * @param <code>caseX</code> numero de la colonne.
     * @param <code>caseY</code> numero de la ligne.
     * @param <code>caseY</code> numero  correspondant au joueur.
     */

    //Methode de calcul du supermega algorithme
    public boolean algorithme(int caseX,int caseY,int joueurJoue){

            
            //Test de la diagonale gauche/haut bas/droit autour du pion
            if((tableauPlanJeu[caseX-1][caseY-1]==joueurJoue) && (tableauPlanJeu[caseX+1][caseY+1]==joueurJoue))
                return true;
            
            //Test de la diagonale gauche/bas haut/droit autour du pion
            else if((tableauPlanJeu[caseX-1][caseY+1]==joueurJoue) && (tableauPlanJeu[caseX+1][caseY-1]==joueurJoue))
                return true;

            //Test de la diagonale largeur autour du pion
            else if ((tableauPlanJeu[caseX-1][caseY]==joueurJoue) && (tableauPlanJeu[caseX+1][caseY]==joueurJoue))
                return true;

            //Test de la verticale autour du pion
            else if ((tableauPlanJeu[caseX][caseY-1]==joueurJoue) && (tableauPlanJeu[caseX][caseY+1]==joueurJoue))
                return true;
            
            //Test de la diagonale gauche/haut avant le pion
            else if ((tableauPlanJeu[caseX-1][caseY-1]==joueurJoue) && (tableauPlanJeu[caseX-2][caseY-2]==joueurJoue))
                return true;
            
            //Test de la diagonale gauche/bas avant le pion
            else if((tableauPlanJeu[caseX-1][caseY+1]==joueurJoue) && (tableauPlanJeu[caseX-2][caseY+2]==joueurJoue))
                return true;
            
            //Test de la diagonale droite/haut apres le pion
            else if ((tableauPlanJeu[caseX+1][caseY-1]==joueurJoue) && (tableauPlanJeu[caseX+2][caseY-2]==joueurJoue))
                return true;

            //Test de la diagonale droite/bas apres le pion
            else if ((tableauPlanJeu[caseX+1][caseY+1]==joueurJoue) && (tableauPlanJeu[caseX+2][caseY+2]==joueurJoue))
                return true;

            //Test de la largeur avant le pion
            else if ((tableauPlanJeu[caseX-1][caseY]==joueurJoue) && (tableauPlanJeu[caseX-2][caseY]==joueurJoue))
                return true;
            
            //Test de la largeur apres le pion
            else if((tableauPlanJeu[caseX+1][caseY]==joueurJoue) && (tableauPlanJeu[caseX+2][caseY]==joueurJoue))
                return true;
            
            //Test de la verticale avant le pion
            else if ((tableauPlanJeu[caseX][caseY-1]==joueurJoue) && (tableauPlanJeu[caseX][caseY-2]==joueurJoue))
                return true;
            
            //Test de la verticale apres le pion
            else if((tableauPlanJeu[caseX][caseY+1]==joueurJoue) && (tableauPlanJeu[caseX][caseY+2]==joueurJoue))
                return true;
            else
                return false;

    }//Fin de l algorithme de fou....








    /** Methode ajoutant un client dans un tableau vector
     * @param <code>client</code> correspond a un client distant.
     * @param <code>nomClient</code> correspond au nom du client distant.
     * @param <code>numeroPion</code> correspond au numero de pion du client distant.
    */

    //Methode qui ajoute un client
    public void ajouterClientVector(ClientDistant client,String nomClient,int numeroPion){

        //Ajout dans le tableauVirtuel d une nouvelle instance de client
        tableauVirtuel.addElement(new Joueur(client, nomClient,numeroPion));

        //Reinitialise la variable nbClients
        setNbClients();
        
        //Test
        System.out.println("Le client "+nomClient+" vient d'etre ajouter.");
        afficherStringServeur("Le jeu contient desormais ",this.nbClients," joueurs.");
        afficherStringServeur("Nous attendons d autres joueurs.");
        
    }//Fin de la methode ajouterClients
    
    
    


    
    /** Methode traitant un client gagnant.
     * @param <code>joueurJoue</code> numero du joueur ayant gagne.
     * @param <code>caseX</code> numero de la colonne.
     * @param <code>caseY</code> numero de la ligne.
     */

    //Methode permettant d afficher chez le client la fin de la partie
    public void gagnerClient(int joueurJoue,int caseX,int caseY){
        
        try{

        //Affiche le message de fin de partie et reinitialise le client et le serveur
            for(int i=0;i<this.nbClients;i++){
                ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherMorPion(((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getNumeroClient(),caseX-3,caseY-3);
            }

            //Fait attendre la fonction
            Thread.sleep(3000);
            
            //Reinitialise le client
            for(int i=0;i<this.nbClients;i++){
                ((Joueur)(tableauVirtuel.elementAt(i))).getClient().setFinPartie(((Joueur)(tableauVirtuel.elementAt(joueurJoue))).getNomClient(),joueurJoue);                
            }

        //Reinitialise le serveur
        resetServeur();

        }//Fin du try

        catch (Exception e){
            e.printStackTrace();
            afficherStringServeur("Probleme de connection avec la methode enregistrerClient!");
        }
        
    }//Fin de la methode gagnerClient








    /** Methode reinitialisant les variables du serveur*/

    //Methode de reinitialisation
    public void resetServeur(){

        //Reinitialise les variables
        tableauVirtuel=new Vector();

        nbClients=0;
        nbJoueurs=0;
        joueurJoue=0;
        joueurJoueAncien=0;
        nbCases=0;
        nbCoups=0;
        nbCasesVirtuelles=0;
          
    }//Fin methode reset






   /** Methode definissant le joueur qui debute*/

    //Methode definissant quel joueur debute le jeu
    public int setJoueurDebut(){
     
        return (int)(Math.random()*this.nbJoueurs);
    
    }//Fin de la methode setJoueurDebut







 /** Methode permettant de diffuser des messages d'un client vers tous les joueurs.
  * @param <code>nomClient</code> nom du client d'ou provient le message.
  * @param <code>messageclient</code> message a transmettre.
  */

  //Methode permettant de diffuser des messages
  public void chat(String nomClient,String messageClient){

    try{
     //Affiche le message choisi
        for(int i=0;i<this.nbClients;i++){
            ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherStringClient(nomClient+">>",messageClient);
        }
    }catch (Exception e){}

  }//Fin de la methode chat






    /** Methode permettant de deconnecter un client du jeu.
     * @param <code>nomClient</code> nom du client a deconnecter.
     */
    
    //Methode permettant de deconnecter un client
    public void deconnecterClient(String nomClient){

        try{

            //Declaration d une variable temporaire permettant ensuite de changer le tour
            int numeroTourTemp=7;
            
            //cas ou il n y a plus qu'un client
            if (this.nbClients==1){

                //On reinitialise le client
                for(int i=0;i<this.nbClients;i++){
                   
                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().setFinPartie();
                }//Fin boucle for
                

                //On reinitialise le serveur
                resetServeur();
            }//Fin du cas ou il n'y a qu'un client 

            //cas ou il n y a plus que deux clients
            else if (this.nbClients==2){
               
                //Fait attendre la fonction
                Thread.sleep(5000);
    
                
                //On reinitialise le client
                for(int i=0;i<this.nbClients;i++){
                   
                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().setFinPartie("nul",8);
                }//Fin boucle for
                

                //On reinitialise le serveur
                resetServeur();
                

            }
            else{
  
                //On recherche le client actuel dans le tableau de Vector
                for(int i=0;i<this.nbClients;i++){
                    
                    ((Joueur)(tableauVirtuel.elementAt(i))).getClient().afficherStringClient("Deconnection du joueur ",nomClient);
                    
                        if ((((Joueur)(tableauVirtuel.elementAt(i))).getNomClient()).equals(nomClient)){
                          numeroTourTemp=i;
                    }
                }//Fin boucle for
                
                //On efface l element correspondant
                if( numeroTourTemp!=7){
                    tableauVirtuel.removeElementAt(numeroTourTemp);
                }

                //Reinitialisation du nombre d elements dans le tableau
                setNbClients();
                
                //Modification des variables pour le tour de jeu
                this.nbJoueurs=this.nbJoueurs-1;

            }//Fin else
        }//Fin try
        catch(Exception e){
            System.out.println("Probleme avec la methode deconnecterClient");
        }
                 
    }//Fin de la methode deconnecterClient
            







    /** Methode traitant un probleme de connexion*/

    //Methode affichant un message et reinitialisant les clients
    public void problemeConnexion(){

        //Affiche un message pour l'utilisateur
        afficherStringServeur("Oups,...Un probleme de connexion....");

        //Reinitialise les variables
        System.exit(0);
    
    }//Fin de la methode problemeConnexion





    ////////////////////////////////////////////////////////////////////////
    //
    // Accesseurs
    //
    ////////////////////////////////////////////////////////////////////////



    /** Methode retournant le nombre de clients dans le tableau de vector*/

    //Methode retournant le nombre de clients dans le tableau vector
    public void setNbClients(){
        
        this.nbClients=tableauVirtuel.size();
    
    }//Fin de la methode setNbClients




}//Fin de la classe Morpion









