/**
*Programme cree par The Clusters
*Classe Plateau du jeu "Chkoba"
* Les attributs sont en statique pour forcer le fait que chaque plateau n'ait qu'une seul de ses attributs.
* Ce qui donne une assurance notemment lors du jeu en reseau.
*@author The Clusters
*@version 1.0
**/

package chkoba;
import chkoba.*;
import java.util.ArrayList;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.Naming;
import java.util.Properties;
import java.io.FileReader;
import java.util.Scanner;

/**
*	Classe Plateau
*/

public class PlateauImpl extends UnicastRemoteObject implements IPlateau
{
	/** La pioche du plateau */
	protected static IPioche p;											
	/** Le tapis du plateau */
	private static ITapis tapis;												
	/** Variable contenant les proprietes de la machine */
	private static Properties pro;		
	/** Attribut permettant de generer le fichier configuration.properties */
	private static PropertiesPerso propers;									
	/** La liste de tous les joueurs */
	private static ArrayList<IJoueur> joueurs=new ArrayList<IJoueur>();	
	/** La liste de tous les joueurs automatiques*/	
	private static ArrayList<IJoueurA> joueursA=new ArrayList<IJoueurA>();	
	/** La liste de tous les joueurs manuels*/
	private static ArrayList<IJoueurM> joueursM=new ArrayList<IJoueurM>();
	/** Le joueur courant */
	private static IJoueur joueurCourant;
	/** La liste contenant les numeros des joueurs allie dans la premiere equipe */
	private static ArrayList<IJoueur> team1=new ArrayList<IJoueur>();
	/** La liste contenant les numeros des joueurs allie dans la seconde equipe */
	private static ArrayList<IJoueur> team2=new ArrayList<IJoueur>();	
	/** Le nombre de joueurs manuels */
	private static int nbJoueursMan;
	/** Le nombre de joueurs automatiques */
	private static int nbJoueursIA;
	/** Une liste contenant les dernieres cartes prises */
	private ArrayList<ICarte> dernierPlis=new ArrayList<ICarte>();
	
	/**
	* CONSTRUCTEUR
	**/
	/**
	*Constructeur initialisant un plateau : <br/>
	*Creation d'une pioche<br/>
	*Creation d'un tapis<br/>
	*Initialisation "fausse" du dernier plis
	*/
	public PlateauImpl() throws RemoteException
	{		
		p=new PiocheImpl();
		tapis=new TapisImpl(p);
		dernierPlis.add(new CarteImpl());
	}
	
	/**
	* METHODES
	**/
	
	/**
	* Permet d'obtenir le dernier pli obtenu. Utile lors du jeu en reseau
	*/
	public ArrayList<ICarte> getDernierPlis() throws RemoteException
	{
		return dernierPlis;
	}
	
	/**
	*  Permet de memorise le dernier plis de chaque joueur pour l'affiche au autre. Utile lors du jeu en reseau
	*/
	public void majDernierPli(ICarte cMain,ArrayList<ICarte> cTapis) throws RemoteException
	{
		dernierPlis = new ArrayList<ICarte> ();
		dernierPlis.add(cMain);
		for ( int i = 0 ; i != cTapis.size() ; i++)
			dernierPlis.add(cTapis.get(i));
	}
	
	/**
	*  Permet d'effacer le dernier plis odnc utilise quand un joueur se defausse. Utile lors du jeu en reseau
	*/
	public void effacerPlis() throws RemoteException
	{
		dernierPlis = new ArrayList<ICarte> ();
	}

	/**
	* Methode pour ajouter un joueur lors d'un jeu en reseau e deux joueurs
	*/
	public void ajouteJoueur(String nom) throws RemoteException
	{
		try
		{
			joueursM.add(new JoueurMImpl(nom));
			joueurs.add( (IJoueur) joueursM.get(joueursM.size()-1));
			
			if ( joueurCourant == null )
				joueurCourant = joueurs.get(0);
			
			Naming.rebind("joueur"+(joueurs.size()-1)+"",joueurs.get(joueurs.size()-1));
		} 
		catch ( Exception e)
		{
			System.out.println("PlateauImpl, methode ajouteJoueur : \n");
			e.printStackTrace();
		}
	}
	
	/**
	*Ajoute les joueurs automatiques
	*jusuq'a ce qu'il n'y ait plus de place
	*/
	public void ajouteJoueursIA() throws RemoteException
	{
		String nom;
		try
		{
			FileReader fr = new FileReader("config.txt");
			Scanner scan = new Scanner(fr);
			nom=scan.next();
			
			for ( int i=joueursM.size(); i<4;i++ )
			{
				nom=scan.next();
				joueursA.add(new JoueurAImpl(nom));
				joueurs.add( (IJoueur) joueursA.get(joueursA.size()-1));
				Naming.rebind("joueur"+(joueurs.size()-1)+"",joueurs.get(joueurs.size()-1));
			}
			
			scan.close();
			
		} 
		catch ( Exception e)
		{
			System.out.println("PlateauImpl, methode ajouteJoueursIA : \n");
			e.printStackTrace();
		}
	}
	
	
	/**
	* Methode pour recuperer tous les objets sur le reseau
	*/
	public static IPlateau lookup() throws RemoteException
	{
		IPlateau pl;
		
		try
		{
			propers=new PropertiesPerso();
			pro=propers.loadProperties();
			
			String rmiHost=pro.getProperty("rmi.host");
			pl = ( IPlateau ) Naming.lookup("rmi://"+rmiHost+"/plateau");
			System.out.println(pl.affiche());
			
			pl.setPioche( ( (IPioche) Naming.lookup("rmi://"+rmiHost+"/pioche") ) );
			System.out.println(pl.getPioche().affiche());
			
			pl.setTapis( (ITapis) Naming.lookup("rmi://"+rmiHost+"/tapis") );
			System.out.println(pl.getTapis().affiche());
			
			return pl;
		}
		catch ( Exception e)
		{
			System.out.println("PlateauImpl, methode lookup : \n");
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	* Methode qui permet de prendre une carte passe en premier parametre
	* Avec la ou les cartes passe en parametre
	* Selon les cas, l'araylist contiendra une ou plusieurs cartes
	*/
	public void prendre( ICarte main, ArrayList<ICarte> tapisenl) throws RemoteException
	{
		try
		{
			if ( this.tapis.removeAll(tapisenl) )
				joueurCourant.chkoba();
			joueurCourant.getMain().remove(main);
			tapisenl.add(main);
			joueurCourant.ajoutePlis(tapisenl);
		}
		catch ( Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	/**
	* Methode qui initialise les joueurs dans un jeu a deux joueurs.
	* Cette initialisation doit se faire en differe notemment lorsque le jeu doit se faire en serveur.
	*/
	public void jeuADeux( ArrayList<Integer> manuels ) throws RemoteException
	{
		// Au cas ou l'utilisateur aurait fait fichier -> nouveau
		// on vide pour recommencer
		while ( joueurs.size() != 0 )
			joueurs.remove(0);
		while ( joueursM.size() != 0 )
			joueursM.remove(0);
		while ( joueursA.size() != 0 )
			joueursA.remove(0);
		
		if ( manuels.get(0) == 1 )
		{
			joueursM.add( new JoueurMImpl() );
			if ( manuels.size()> 1 && manuels.get(1) == 2 )
				joueursM.add( new JoueurMImpl() );
			else
				joueursA.add( new JoueurAImpl() );
		}
		else
		{
			joueursA.add( new JoueurAImpl() );
			if ( manuels.get(0) == 2 )
				joueursM.add( new JoueurMImpl() );
			else
				joueursA.add( new JoueurAImpl() );
		}
		
		for(int i=0;i<joueursM.size();i++)
			joueurs.add( (IJoueur) joueursM.get(i));
		for(int i=0;i<joueursA.size();i++)
			joueurs.add( (IJoueur) joueursA.get(i));
		joueurCourant=joueurs.get(0);
	}
	
	
	/**
	* Idem que la methode au dessus mais pour 4 joueurs
	*/
	public void jeuAQuatre( ArrayList<Integer> tm1, ArrayList<Integer> tm2, ArrayList<Integer> manuels) throws RemoteException
	{	
		// Au cas ou l'utilisateur aurait fait fichier -> nouveau
		// on vide pour recommencer
		while ( joueurs.size() != 0 )
			joueurs.remove(0);
		while ( joueursM.size() != 0 )
			joueursM.remove(0);
		while ( joueursA.size() != 0 )
			joueursA.remove(0);
		while ( team1.size() != 0 )
			team1.remove(0);
		while ( team2.size() != 0 )
			team2.remove(0);
		
		int tmp;
		//On ajoute tous les joueurs aux arrayList respectives
		//Sachant qu'un jeu a 4 joueurs comporte forcement 2 equipes de 2 joueurs
		for (int i=0; i<2; i++)
		{
			//On verifie si le joueur n°i de la premiere equipe 
			//est un joueur manuel ou un joueur automatique
			tmp=tm1.get(i);
			if ( manuels.indexOf(tmp) !=-1 )
			{
				joueursM.add( new JoueurMImpl());
				this.team1.add( (IJoueur) joueursM.get(joueursM.size()-1) );
			}
			else
			{
				joueursA.add( new JoueurAImpl() );
				this.team1.add( (IJoueur) joueursA.get(joueursA.size()-1) );
			}
			
			//On verifie si le joueur n°i de la seconde equipe 
			//est un joueur manuel ou un joueur automatique
			tmp=tm2.get(i);
			if ( manuels.indexOf(tmp) != -1 )
			{
				joueursM.add( new JoueurMImpl() );
				this.team2.add( (IJoueur) joueursM.get(joueursM.size()-1) );	
			}
			else
			{
				joueursA.add( new JoueurAImpl() );
				this.team2.add( (IJoueur) joueursA.get(joueursA.size()-1));
			}
		}
		
		joueurs.add( team1.get(0));
		joueurs.add( team2.get(0));
		joueurs.add( team1.get(1));
		joueurs.add( team2.get(1));
		
		joueurCourant=joueurs.get(0);
	}
	
	
	/**
	* Methode qui permet de faire piocher une carte de la pioche
	*/
	public ICarte piocher() throws RemoteException
	{	
		return p.piocher();
	}
	
	
	/**
	* Methode qui permet de faire poser une carte sur le tapis
	* Intervient notemment lorsque le joueur ne peut pas jouer.
	*/
	public void poser(ICarte c) throws RemoteException
	{
		try
		{
			tapis.add(c);
			joueurCourant.getMain().remove(c);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	* Methode qui calcul le nombre de cartes dans les mains et/ou dans la pioche
	* En effet, la condition d'arret du jeu etant qu'il n'y ait plus de cartes ni dans les mains, ni dans la pioche
	*/
	public boolean resteCartes() throws RemoteException
	{
		int total=0;
		
		for ( int i=0; i<joueurs.size(); i++ )
			total+=joueurs.get(i).getMain().size();
		total+=p.size();
		
		if ( total != 0 )
			return true;
		else
			return false;
	}
	
	
	/**
	* METHODE D'AFFICHAGE
	**/
	
	/** Affiche le plateau :<br/> 
	*La pioche<br/>
	*Le tapis<br/>
	*Le jouer courant<br/>
	*Les joueurs<br/>
	*/
	public String affiche() throws RemoteException
	{
		String s="";
		for(int i =0;i<1;i++)
		{
			s+="\nSur le plateau il y a : \n\n";
			s+="\tUne pioche : \n\n"+p.affiche()+"\n";
			s+="\tLe joueur courant est : "+joueurCourant.affiche()+"\n";
			for ( int j=0; j<joueurs.size();j++)
				s+="\tLe joueur n°"+j+" : "+joueurs.get(j).affiche()+"\n";
			s+="\tLe tapis : "+tapis.affiche()+"\n";          
		}
		return s;
	}
	
	
	/**
	* ACCESSEURS
	**/
	
	/**
	* Methode qui determine automatiquement, en fonction du nombre de joueurs dans la partie, quel sera le prochain joueur courant.
	* Le premier joueur courant de la partie est le joueur le premier ajouter dans l'arrayList joueurs
	* En mode campagne, c'est le joueur derriere l'ordinateur
	* En mode serveur, c'est celui qui cre le serveur.
	*/
	public void setJoueurCourant() throws RemoteException
	{
		int i=joueurs.indexOf(joueurCourant);
		
		if (joueurs.size() == 2)
		{
			//traitement pour jeu a 2 joueurs
			if(i==0)				
				i=1;
			
			else
				i=0;
		}
		else
		{
			//traitement pour jeu a 4 joueurs
			if(i<3)
				i=i+1;
			else
				i=0;
		}
		joueurCourant=joueurs.get(i);
	}
	
	/** Affecte une nouvelle pioche au plateau*/
	public void setPioche ( IPioche pioche ) throws RemoteException { this.p=pioche; }
	/** Affecte un nouveau tapis au plateau */
	public void setTapis( ITapis tapis ) throws RemoteException { this.tapis=tapis; }
	/** Affecte le nombre de joueurs manuels du jeu */
	public void setNbJoueursMan( int n ) throws RemoteException { this.nbJoueursMan=n; }
	/** Reorganise les equipes et les joueurs en fonctions */
	public void setEquipes( ArrayList<Integer> list) throws RemoteException 
	{ 
		ArrayList<Integer> tmp = new ArrayList<Integer>();
		tmp.add(1); tmp.add(2); tmp.add(3); tmp.add(4);
		
		team1.add(joueurs.get(list.get(0))); 
		tmp.remove(list.get(0));
		team1.add(joueurs.get(list.get(1))); 
		tmp.remove(list.get(0));
		
		team2.add(joueurs.get(tmp.get(0)));
		team2.add(joueurs.get(tmp.get(0)));
	}
	/** Retourne la pioche */
	public IPioche getPioche() throws RemoteException{ return p;}
	/** Retourne le nombre de joueurs manuels */
	public int getNbJoueursMan() throws RemoteException{ return nbJoueursMan;}
	/** Retourne la liste de joueurs */
	public ArrayList<IJoueur> getJoueurs() throws RemoteException{ return joueurs; } 
	/** Retourne la liste de joueurs automatiques*/
	public ArrayList<IJoueurA> getJoueursA() throws RemoteException{ return joueursA; } 
	/** Retourne la liste de joueurs manuels*/
	public ArrayList<IJoueurM> getJoueursM() throws RemoteException{ return joueursM; } 
	/** Retourne le tapis */
	public ITapis getTapis() throws RemoteException{ return tapis; }
	/** Retourne le joueur courant */
	public IJoueur getJoueurCourant() throws RemoteException{ return joueurCourant; }
	/** Retourne la liste symbolisant l'equipe 1 */
	public ArrayList<IJoueur> getEquipe1() throws RemoteException { return team1; }
	/** Retourne la liste symbolisant l'equipe 2 */
	public ArrayList<IJoueur> getEquipe2() throws RemoteException { return team2; }
}
