package como.commlet.webcheckers;

import java.awt.*;
import como.awt.*;
import java.applet.AudioClip;

public class Damier extends Panel{

  //MEMBRES

private static final int ROWS = 10;
private static final int COLS = 10;
private Case [][] grid;
private Case Source, Dest, Dead;
private Joueur Player1;
private Joueur Player2;
private Joueur Turn, Other;
private boolean Eating = false;
private Label My_Label;
private AudioClip sounds[] = null;
private int nbsounds;

  //CONCTRUCTEUR

public Damier(Joueur J1, Joueur J2,Label label){
  int i, j;
  
  My_Label = label;
  Turn = Player1 = J1;
  Other = Player2 = J2;

  grid = new Case[ROWS][COLS];
  setLayout(new GridLayout(ROWS,COLS));
  
  for(j=0; j<ROWS; j++)            // creation de la grille
    for(i=0; i<COLS; i++){
      grid[i][j] = Init_Case(i,j); // initialisation de la case
      add(grid[i][j]);
    }
  Source = null;
  Dest = null;
  Dead = null;
  validate();
}

  //METHODES

public void Restart(){
  // reinitialisation de la grille (newgame)
  int i, j;

  Turn = Player1;
  Other = Player2;

  Player1.Reset_Pion();
  Player2.Reset_Pion();
  
  removeAll();
  setLayout(new GridLayout(ROWS,COLS));

  for(j=0; j<ROWS; j++)            // initialisation de la grille
    for(i=0; i<COLS; i++){
      grid[i][j].Set_Empty();
      grid[i][j] = Init_Case(i,j); // initialisation de la case
      add(grid[i][j]);
    }
  Source = null;
  Dest = null;
  Dead = null;
  validate();
}

public void SetSounds(AudioClip sounds[],int nb){ 
  // Recuperation des sons pour le damier.
  this.sounds = sounds;
  nbsounds = nb;
}

private void PlaySound(int i){
  if (i < nbsounds)
    if (sounds[i] != null) sounds[i].play();
}
  
public Case Init_Case(int x,int y){
  // initialisation des cases
  // creation des pions
  if ((x%2) != (y%2)) return new Case(Case.BLACK,x,y);
  if (y < 4) return new Case(new Pion(Player1),x,y);
  if (y > (ROWS - 5)) return new Case(new Pion(Player2),x,y);
  return new Case(Case.WHITE,x,y);
}

private void setText(String msg){
  My_Label.setText(msg);
}

public void Change_Turn(){
  // On change de tour
  if (Turn.Side == Player1.Side){
    Turn = Player2;
    Other = Player1;
  }
  else{
    Turn = Player1;
    Other = Player2;
  }
  setText("C'est aux "+Turn.Get_Color_String()+"s de jouer.");
}

public boolean Source_Set(){
  // la case de depart du mouvement est-elle fixee ?
  return (Source != null);
}

public Case Get_Source(){
  // retourne la case de depart du mouvement
  return Source;
}

public boolean Set_Source(Case S){
  // fixe la case de depart du mouvement
  if (S.Is_Empty()) return false;
  if (Turn.Side == (S.Get_Pion()).Player.Side){
    Source = S;
    S.HighLight(true);
    return true;
  }
  else return false;
}

public void Unset_Source(){
  // l'inverse de Set_Source
  if (Source != null){
    Source.HighLight(false);
    Source = null;
  }
}

public Case Get_Dest(){
  // retourne la case destination
  return Dest;
}

public boolean Set_Dest(Case S){
  // fixe la case destination
  // verifie si le mouvement est autorise'
  // recupere le pion mange' si y'en a un

  Dead = null;
  if (Source == null) return false;
  if (Source == S){ // click 2 fois sur la meme case
    Unset_Source();
    return false;
  }
  
  if ((Source.Get_Pion()).Move_To(S,Eating)){ 
    // Le pion a le droit theorique de faire le deplacement
    int dx = Math.abs(S.X() - Source.X());
    if (dx != 1){
      if(dx > 2){ 
	// C'est une Dame

	int Cx = Source.X(); int Cy = Source.Y();
	int PasX = 1; int PasY = 1;
	if (Cx > S.X()) PasX = -1;
	if (Cy > S.Y()) PasY = -1;

	// on recherche le nombre de pions que la dame
	// survole, il doit y'en avoir un au maximum et
	// ce doit etre un ennemi.
	do{
	  Cx += PasX; Cy += PasY;
	  if (!(grid[Cx][Cy]).Is_Empty()){ 
	    if (!(grid[Cx][Cy]).Is_Ennemy(Source)) return false;
	    if (Dead != null) {
	      // on en a deja survole' un
	      setText("Mouvement interdit!");
	      PlaySound(4);
	      Dead = null;
	      return false;
	    }
	    Dead = grid[Cx][Cy];
	  }
	} while(grid[Cx+PasX][Cy+PasY] != S);

	if ((Dead == null) && Can_Eat()) {
	  // si on ne mange pas de pion et qu'il y'en a un en prise
	  setText("Il ya un pion "+Other.Get_Color_String()+" en prise!");
	  PlaySound(2);
	  return false;
	}
	Dest = S;
	return true;
      }
      else{ 
	// deplacement de 2 cases
	// dead est le pion mange' s'il existe
	Dead = grid[(Source.X()+S.X())/2][(Source.Y()+S.Y())/2];
	if (!(Source.Get_Pion()).Is_Dame()){ 
	  // c'est un pion donc on doit manger
	  if (!Dead.Is_Ennemy(Source)){
	    // je peux pas manger un copain
	    setText("Mouvement interdit!");
	    PlaySound(4);
	    Dead = null;
	    return false;
	  }
	}
	else if (Dead.Is_Empty()){
	  // c'est une dame
	  if (Can_Eat()) {
	    // je ne mange pas et je peux manger
	    setText("Il ya un pion "+Other.Get_Color_String()+" en prise!");
	    PlaySound(2);
	    Dead = null;
	    return false;
	  }
	  Dead = null;
	}
	else if (!Dead.Is_Ennemy(Source)){
	  // on ne mange pas ces copains !!
	  setText("Mouvement interdit!");
	  PlaySound(4);
	  Dead = null;
	  return false;
	}
	Dest = S;
	return true;
      }
    }
    // dx == 1
    if(Can_Eat()){
      // deplacement de 1
      // et je peux manger
      setText("Il ya un pion "+Other.Get_Color_String()+" en prise!");
      PlaySound(2);
      return false;
    }
    Dest = S;
    return true;
  }
  else{
    // deplacement illicite
    setText("Mouvement interdit!");
    PlaySound(4);
    return false;
  }
}

public boolean Can_Eat(){ 
  // retourne vrai si le joueur a la possibilite de manger
  // parcours la grille, recherche les pions du joueur,
  // et verifie s'il on un pion ennemie en prise

  int i,j;
   for(j=0; j<ROWS; j++)
    for(i=0; i<COLS; i++){
      if (!(grid[i][j]).Is_Empty())
	if (((grid[i][j]).Get_Pion()).Player.Side == Turn.Side)
	  if (!No_Eatable(grid[i][j])) return true;
    }
   return false;
}

public boolean No_Eatable(Case C){
  // verifie que le pion de la case C n'a pas un pion ennemi
  // en prise

  int X = C.X();
  int Y = C.Y();

  if (!(C.Get_Pion().Is_Dame())){
    // c'est un pion je regarde en Y- ou Y+ suivant le joueur
    // sauf si le pion vient de manger

    if (Eating || (Turn.Side == Joueur.DOWN)){
      if (X >= 2 && Y >= 2)
	if ((grid[X-1][Y-1]).Is_Ennemy(C))
	  if ((grid[X-2][Y-2]).Is_Empty()) return false;
      if (X < (COLS-2) && Y >= 2)
	if ((grid[X+1][Y-1]).Is_Ennemy(C))
	  if ((grid[X+2][Y-2]).Is_Empty()) return false;
    }

    if (Eating || (Turn.Side == Joueur.UP)){
      if (X < (COLS-2) && Y < (ROWS-2))
	if ((grid[X+1][Y+1]).Is_Ennemy(C))
	  if ((grid[X+2][Y+2]).Is_Empty()) return false;
      if (X >= 2 && Y < (ROWS-2))
	if ((grid[X-1][Y+1]).Is_Ennemy(C))
	  if ((grid[X-2][Y+2]).Is_Empty()) return false;
    }
    return true;
  }
  else{ 
    // C'est une dame donc on verifie sur les quatre diagonales

    int Cx = X; int Cy = Y;

    // Premiere diagonale
    while ((++Cx < COLS) && (++Cy < ROWS))
      if (!(grid[Cx][Cy]).Is_Empty()) break;
    if (Cx < COLS-1 && Cy < ROWS-1)
      if ((grid[Cx][Cy]).Is_Ennemy(C)) 
	if ((grid[Cx+1][Cy+1]).Is_Empty()) return false;

    // Deuxieme diagonale
    Cx = X; Cy = Y;
    while ((--Cx >= 0) && (--Cy >= 0))
      if (!(grid[Cx][Cy]).Is_Empty()) break;
    if (Cx > 0 && Cy > 0)
      if ((grid[Cx][Cy]).Is_Ennemy(C)) 
	if ((grid[Cx-1][Cy-1]).Is_Empty()) return false;

   // Troisieme diagonale
    Cx = X; Cy = Y;
    while ((++Cx < COLS) && (--Cy >= 0))
      if (!(grid[Cx][Cy]).Is_Empty()) break;
    if (Cx < COLS-1 && Cy > 0)
      if ((grid[Cx][Cy]).Is_Ennemy(C))
	if ((grid[Cx+1][Cy-1]).Is_Empty()) return false;

   // Quatrieme diagonale
    Cx = X; Cy = Y;
    while ((--Cx >= 0) && (++Cy < ROWS))
      if (!(grid[Cx][Cy]).Is_Empty()) break;
    if (Cx > 0 && Cy < ROWS-1) 
      if ((grid[Cx][Cy]).Is_Ennemy(C))
	if ((grid[Cx-1][Cy+1]).Is_Empty()) return false;

    return true; 
  }
}

public boolean Can_I_Move(){
  // retourne vrai si le joueur peux deplacer un de ces pions
  // retourne vrai s'il n'a plus de pion.
  int i,j;
  if (Turn.Loose()) return true;
  for(j=0; j<ROWS; j++)
    for(i=0; i<COLS; i++){
      if (!(grid[i][j]).Is_Empty())
	if (((grid[i][j]).Get_Pion()).Player.Side == Turn.Side)
	  if (Can_Move(grid[i][j])) return true;
    }
   return false;
}

public boolean Can_Move(Case C){
  
  // verifie que le pion de la case C peut bouger

  int X = C.X();
  int Y = C.Y();

  if (C.Get_Pion().Is_Dame() || Eating || (Turn.Side == Joueur.DOWN)){
      // deplacement simple
    if ((X >= 1) && (Y >= 1))
      if ((grid[X-1][Y-1]).Is_Empty()) return true;
    if ((X < COLS-1) && (Y >= 1))
      if ((grid[X+1][Y-1]).Is_Empty()) return true;
    // manger?
      if (X >= 2 && Y >= 2)
	if ((grid[X-1][Y-1]).Is_Ennemy(C))
	  if ((grid[X-2][Y-2]).Is_Empty()) return true;
      if (X < (COLS-2) && Y >= 2)
	if ((grid[X+1][Y-1]).Is_Ennemy(C))
	  if ((grid[X+2][Y-2]).Is_Empty()) return true;
  }
  
  if (C.Get_Pion().Is_Dame() || Eating || (Turn.Side == Joueur.UP)){
      // deplacement simple
    if ((X < COLS-1) && (Y < ROWS-1))
      if ((grid[X+1][Y+1]).Is_Empty()) return true;
    if ((X >= 1) && (Y < ROWS-1))
      if ((grid[X-1][Y+1]).Is_Empty()) return true;
    // manger?
    if (X < (COLS-2) && Y < (ROWS-2))
	if ((grid[X+1][Y+1]).Is_Ennemy(C))
	  if ((grid[X+2][Y+2]).Is_Empty()) return true;
    if (X >= 2 && Y < (ROWS-2))
      if ((grid[X-1][Y+1]).Is_Ennemy(C))
	if ((grid[X-2][Y+2]).Is_Empty()) return true;
    }
  return false;
}
  
public String MoveStr(){
  // ecrit sous forme de chaine le mouvement courant

  StringBuffer result = new StringBuffer();
  if ((Source != null) && (Dest != null)){
    result.append(Source.X()).append(Source.Y()).append(Dest.X()).append(Dest.Y());
    return result.toString();
  }
  return ""; // Exception mais pas le temps !!
}


public void MakeMoveStr(String MoveStr){
  // repete le mouvement de l'autre joueur

  char [] move = MoveStr.toCharArray();

  Set_Source(grid[Character.digit(move[0],10)][Character.digit(move[1],10)]);
  if (Source_Set()){
    if (Set_Dest(grid[Character.digit(move[2],10)][Character.digit(move[3],10)])){
      if (Move().Finished == 0){
	if (Player1.Loose()){
	  SmartFrame MSG = 
	    new SmartFrame("Victoire des "+Player2.Get_Color_String()+"s !");
	  setText("Victoire des "+Player2.Get_Color_String()+"s !!");
	   MSG.show();
	}
	else {
	  SmartFrame MSG = 
	    new SmartFrame("Victoire des "+Player1.Get_Color_String()+"s !!");
	  setText("Victoire des "+Player1.Get_Color_String()+"s !!");
	  MSG.show();
	}
      }
    }
    else System.out.println("Erreur de coherence Set_Dest");
  }
  else System.out.println("Erreur de coherence Set_Source");
}


public Move Move(){
  // effectue le mouvement en fonction de source, dest et dead

  Move move;
  int eat;

  if ((Turn.Side == Joueur.UP) && (Dest.Y() == (ROWS -1)) && (!Source.Get_Pion().Is_Dame())){
    Dest.Make_King(Source.Get_Pion());
    PlaySound(1);
  }
  else if ((Turn.Side == Joueur.DOWN) && (Dest.Y() == 0) && (!Source.Get_Pion().Is_Dame())){
    Dest.Make_King(Source.Get_Pion());
    PlaySound(1);
  }
  else Dest.Set_Pion(Source.Get_Pion());
  Source.Set_Empty();
  Source.HighLight(false);
  Dest.HighLight(false);
  if (Dead == null) PlaySound(5);
  else {
    Dead.Eaten();
    PlaySound(3);
    Dead.HighLight(false);
    // Je viens de manger, est-ce-que ce pion peut encore manger?
    Eating = true;

    if (!No_Eatable(Dest)){ 
      // oui il peut
      if (Eating) eat = 1;
      else eat = 0;
      move = new Move(Source.X(),Source.Y(),Dest.X(),Dest.Y(),eat,Other.Nb_Pion());
      Source = Dest;
      Dest = null;
      Source.HighLight(true);
      Dead = null;
      return move;
    }
  }

  Eating = false;
  move = new Move(Source.X(),Source.Y(),Dest.X(),Dest.Y(),0,Other.Nb_Pion());
  Change_Turn(); 
  Source = null;
  Dest = null;
  Dead = null;
  // Transmettre le mouvement a l'autre applet et rendre la main
  return move;
}

}
