package classes_metiers;

import java.util.ArrayList ;

import modele.Observateur;


/**
 * Classes métiers du jeu de Morpion
 * @author Dominique Bouthinon, révisé par B. KHAFIF
 * 
 */
public class Morpion 
{
	public static final int TAILLE_GRILLE = 3;

	private Symbole[][] grille ;

	private Coup coupPropose ;

	private Joueur joueurActif ;

	private boolean partieFinie ;


	/**
	 * Constructeur
	 * Instancie un jeu de morpion
	 */
	public Morpion()
	{
		this.initGrille(Morpion.TAILLE_GRILLE);
		this.joueurActif = Joueur.JOUEUR_CROIX ;
	}


	/**
	 * Retourne la grille de jeu
	 * @return : (Symbole[][]) grille de jeu de morpion
	 */
	public Symbole[][] getGrille()
	{
		return this.grille ;
	}



	/**
	 * Remplit la grille de vides
	 * @param n : (int) Nombre de case du côté du morpion
	 */
	private void initGrille(int n)
	{
		this.grille = new Symbole[n][n] ;

		for (int ligne = 0 ; ligne < this.grille.length ; ligne++)
		{
			for (int colonne = 0 ; colonne < this.grille[ligne].length ; colonne++)
			{
				this.grille[ligne][colonne] = Symbole.VIDE ;
			}
		}
	}




	/**
	 * Retourne le joueur en cours (ROND ou CROIX)
	 * @return : (Joueur) Joueur actif
	 */
	public Joueur getJoueurActif()
	{
		return this.joueurActif ;
	}

	/**
	 * Retourne si la partie est finie ou pas
	 * @return : (boolean) true si la partie est finie, false sinon
	 */
	public boolean partieFinie()
	{
		return this.partieFinie ;
	}


	/**
	 * Retourne le Coup qui vient d'etre joue 
	 * @return : (Coup) dernier coup qui a été joué
	 */
	public Coup getCoupPropose()
	{
		return this.coupPropose ;
	}



	/**
	 * Retourne le contenu de la case (ligne, colonne)
	 * @param ligne : (int) ligne de la case
	 * @param colonne : (int) colonne de la case
	 * @return : (Symbole) contenu de la case
	 */
	private Symbole valeurCase(int ligne, int colonne)
	{
		return this.getGrille()[ligne][colonne] ;
	}


	/**
	 *  Joue le Coup proposé par le dernier joueur
	 * @param ligne : (int) ligne du dernier du coup joué
	 * @param colonne : (int) colonne du dernier coup joué 
	 * @throws Exception: coup non valide
	 */
	public void joueCoup(int ligne, int colonne) throws Exception
	{	
		
		if (this.joueurActif==Joueur.JOUEUR_CROIX){
			this.coupPropose = new Coup(ligne, colonne, Symbole.CROIX);	
		} else {
			this.coupPropose = new Coup(ligne, colonne, Symbole.ROND);	
		}
		
		
		this.coupValide();
		
		
		this.grille[this.getCoupPropose().getLigne()]
				[this.getCoupPropose().getColonne()] = this.getCoupPropose().getSymbole() ;

		this.partieFinie = this.verifierPartieFinie() ;
	}



	/**
	 *  Change de joueur actif
	 */
	public void changeJoueurActif()
	{
		if (this.getJoueurActif().equals(Joueur.JOUEUR_CROIX))
			this.joueurActif = Joueur.JOUEUR_ROND ;
		else
			this.joueurActif = Joueur.JOUEUR_CROIX ;
	}

	
	/**
	 * Vérifie si la partie est finie ou pas
	 * @return (boolean) : true si la partie est finie, false sinon
	 */
	public boolean verifierPartieFinie()
	{
		return
				this.ligneRemplie()   ||
				this.colonneRemplie() ||
				this.diagonaleRemplie()
				;
	}


	/**
	 * Vérifie si la ligne du dernier coup joue est remplie
	 * @return (boolean) : true si la ligne est remplie, false sinon
	 */
	private boolean ligneRemplie()
	{
		boolean remplie = true ;

		int ligne       = this.getCoupPropose().getLigne()  ; 
		Symbole symboleJoue = this.getCoupPropose().getSymbole() ; 

		int colonne = 0 ;
		while (colonne < this.getGrille().length && remplie)
		{
			if (this.valeurCase(ligne, colonne) == symboleJoue) 
				colonne++ ;
			else
				remplie = false ;
		}

		return remplie ;
	}


	/**
	 * Vérifie si la colonne du dernier coup joue est remplie
	 * @return (boolean) : true si la colonne est remplie, false sinon
	 */
	private boolean colonneRemplie()
	{
		boolean remplie = true ;

		int colonne         = this.getCoupPropose().getColonne()  ; 
		Symbole symboleJoue = this.getCoupPropose().getSymbole() ; 

		int ligne = 0 ;
		while (ligne < this.getGrille().length && remplie)
		{
			if (this.valeurCase(ligne, colonne) == symboleJoue) 
				ligne++ ;
			else
				remplie = false ;
		}

		return remplie ;
	}


	/**
	 * Vérifie si le dernier coup joué est sur une diagonale,
	 * <BR/> et si oui, si elle est complèetement remplie
	 * @return (boolean) : true si dernier coup joué est sur une diagonale et 
	 * <BR/>la diagonale est complètement remplie, false sinon
	 */
	private boolean diagonaleRemplie()
	{

		int ligne       = this.getCoupPropose().getLigne();
		int colonne     = this.getCoupPropose().getColonne()  ; 

		if (ligne != colonne)
			return false ;						// on n'est pas sur une diagonale

		// verifie si diagonale remplie 

		boolean remplie     = true ;                // suppose diagonale remplie
		Symbole symboleJoue = this.getCoupPropose().getSymbole() ; 


		// on explore la diagonale
		int i  = 0 ;
		while (i < this.getGrille().length && remplie)
		{
			if (this.valeurCase(i, i) == symboleJoue) 
				i++ ;
			else
				remplie = false ;
		}

		return remplie ;
	}


	/**
	 * Vérifie si le dernier Coup joué est valdide ou pas
	 * @throws Exception : si le coup n'est pas valide
	 */
	private void coupValide() throws Exception
	{
		this.coupProposeDansGrille();
		this.coupProposeCaseVide();
	}


	/**
	 * Vérifie si le coup joue est dans la grille
	 * @throws Exception : si le coup n'est pas dans la grille
	 */
	private void coupProposeDansGrille() throws Exception
	{
		int ligne   = this.getCoupPropose().getLigne() ;
		int colonne = this.getCoupPropose().getColonne() ;

		if (!( 0 <= ligne   && ligne < this.getGrille().length &&
				0 <= colonne &&  colonne < this.getGrille().length)) 
			throw new Exception("Coup non valide !");	
	}



	/** 
	 * Vérifie que la case visée par le coup proposé est bien vide
	 * @throws Exception : si le coup n'est pas dans une case vide
	 */
	private void coupProposeCaseVide() throws Exception
	{
		if (!( this.getGrille()[this.getCoupPropose().getLigne()]
				[this.getCoupPropose().getColonne()] == Symbole.VIDE))
			throw new Exception("Coup non valide !");	
	}
}
// fin class ModelMorpion
