package de.uni.karlsruhe.aifb.prog2.solutions;

import de.uni.karlsruhe.aifb.prog2.chess.Chessboard;
import de.uni.karlsruhe.aifb.prog2.chess.Chessman;
import de.uni.karlsruhe.aifb.prog2.chess.LinearChessman;
import de.uni.karlsruhe.aifb.prog2.chess.Knight;


/** Dieses beschnittene Schachbrett stellt
 *  das Springer-Problem aus dem Buch dar.
 */
public class BoardOfKnights extends Chessboard
  implements Scenario, Cloneable{

  /** Innere Klasse; repraesentiert das Pferd */
  private static class MyKnight extends Knight {

    /** Konstruktor */
    public MyKnight(boolean isBlack)
    {
      super(isBlack);
    }

    /** Testet, ob diese Figur eine andere Figur
      * theoretisch zu schlagen in der Lage ist. Im Gegensatz
      * zum normalen Springer kann dieser keine
      * wie auch immer gearteten Figuren schlagen.
      **/
    public boolean canTake(Chessman chessman) {
      return false;
    }
  }

  /** Innere Klasse; repraesentiert ein blockiertes Feld */
  private static class Block extends LinearChessman {

    /** Konstruktor */
    public Block() {
      super(true,'X',false,false,0);
    }

    /** Testet, ob diese Figur eine andere Figur
      * theoretisch zu schlagen
      * in der Lage ist. Ein Block kann natuerlich
      * keine andere Figur schlagen.
      **/
    public boolean canTake(Chessman chessman) {
      return false;
    }
  }

  /** Dies Konstante steht fuer den schwarzen Springer */
  public final static Chessman BLACK = new MyKnight(true);

  /** Diese Konstante steht fuer den weissen Springer */
  public final static Chessman WHITE = new MyKnight(false);

  /** Diese Konstante steht fuer ein blockiertes Feld */
  public final static Chessman BLOCK = new Block();

  /** Der Startaufbau des Feldes (ohne Springer)
   *  false steht fuer ein leeres Feld,
   *  true fuer ein blockiertes.
   */
  public final static boolean[][] BOARD_AT_START =
  {
    {false,true,true,true},
    {false,false,false,false},
    {false,false,false,true},
    {false,false,true,true}
  };

  /** Die Anfangs-Positionen der schwarzen Springer */
  public final static int[][] BLACK_POS = {{1,3},{2,1}};

  /** Die Anfangs-Positionen der weissen Springer */
  public final static int[][] WHITE_POS = {{2,0},{2,2}};

  /** Konstruktor. Erzeugt ein leeres Brett, ohne Springer */
  public BoardOfKnights() {
    super(4,4);
    for (int i = 0; i < 4; i++)
      for (int j = 0; j < 4; j++)
        if (BOARD_AT_START[i][j])
          this.setContent(i,j,BLOCK);
  }

  /** Erzeugt die Anfangskonstellation des Problems. */
  public static BoardOfKnights createInitial() {
    BoardOfKnights res = new BoardOfKnights();
    res.setContent(WHITE_POS[0][0],WHITE_POS[0][1],WHITE);
    res.setContent(WHITE_POS[1][0],WHITE_POS[1][1],WHITE);
    res.setContent(BLACK_POS[0][0],BLACK_POS[0][1],BLACK);
    res.setContent(BLACK_POS[1][0],BLACK_POS[1][1],BLACK);
    return res;
  }

  /** Stellt das Scenario bereits die Loesung dar ? */
  public boolean isSolution() {
    Chessman black1 = getContent(WHITE_POS[0][0],WHITE_POS[0][1]);
    Chessman black2 = getContent(WHITE_POS[1][0],WHITE_POS[1][1]);
    Chessman white1 = getContent(BLACK_POS[0][0],BLACK_POS[0][1]);
    Chessman white2 = getContent(BLACK_POS[1][0],BLACK_POS[1][1]);
    if (black1 == null || black2 == null ||
        white1 == null || white2 == null)
      return false;
    return
      black1.equals(BLACK) &&
      black2.equals(BLACK) &&
      white1.equals(WHITE) &&
      white2.equals(WHITE);
  }

  /** Klont dieses Objekt */
  public Object clone() {
    BoardOfKnights res = new BoardOfKnights();
    for (int i = 0; i < 4; i++)
      for (int j = 0; j < 4; j++)
        res.setContent(i,j,getContent(i,j));
    return res;
  }

  /** Liefert alle Szenarien, zu denen man sich von diesem
    * Szenario aus bewegen kann.
    **/
  public Scenario[] getNextSteps() {
    // Theoretisch kann jeder Springer in
    // bis zu acht Positionen wandern.
    // Wir reservieren einfach einmal genug Platz :-)
    Scenario[] next = new Scenario[32];
    int counter = 0;
    // Nun wandern wir ueber das Schachbrett
    // und suchen unsere Springer
    for (int i = 0; i < 4; i++)
      for (int j = 0; j < 4; j++) {
        Chessman current = getContent(i,j);
        if (current != null && current instanceof MyKnight) {
          boolean[][] destinations
            = current.getDestinations(this,i,j);
          for (int x = 0; x < 4; x++)
            for (int y = 0; y < 4; y++)
              if (destinations[x][y]) {
                BoardOfKnights board = (BoardOfKnights) clone();
                board.move(i,j,x,y);
                next[counter] = board;
                counter++;
              }
        }
      }
    // Jetzt kopieren wir alles in ein auf
    // die richtige Groesse zugeschnittenes Feld
    Scenario[] res = new Scenario[counter];
    System.arraycopy(next,0,res,0,counter);
    return res;
  }

}