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.Queen;

/** Die Klasse BoardOfQueens repraesentiert eine (Teil-)
 *  Loesung des Achtdamen-Problems.
 */
public class BoardOfQueens extends Chessboard implements Scenario {

  /** Die Damen-Figur, die wir in unserem Spiel benutzen. */
  private static class MyQueen extends Queen {

    /** Konstruktor */
    public MyQueen() {
      super(false);
    }

    /** Testet, ob diese Figur eine andere Figur theoretisch
      * zu schlagen in der Lage ist. Im Gegensatz zur normalen
      * Dame kann diese Dame auch Figurn der eigenen
      * Farbe schlagen.
      **/
    public boolean canTake(Chessman chessman) {
      return true;
    }
  }

  /** Dies Konstante verweist auf die Schachfigur, die
   *  auf diesem Brett verwendet wird
   */
  public final static Chessman QUEEN = new MyQueen();

  /** Diese Variable besagt, in welche Spalte
   *  beim Konstruktionsvorgang die letzte Dame eingefuegt wurde.
   *  -1 steht hierbei fuer ein leeres Brett.
   */
  private int row = -1;

  /** Diese Variable besagt, in welche Zeile beim
   *  Konstruktionsvorgang die letzte Dame eingefuegt wurde.
   *  -1 steht hierbei fuer ein leeres Brett.
   */
  private int column = -1;

  /** Kann die beim Konstruktionsvorgang eingefuegte Dame eine
   *  andere schlagen?
   */
  private boolean canTake = false;

  /** Konstruktor; erzeugt ein leeres Schachbrett */
  public BoardOfQueens() {
    super(8,8);
  }

  /** Erzeugt ein neues Schachbrett aus einem alten.
   *  Fuegt in die naechste zu besetzende Spalte unter der
   *  gegebenen Zeilennummer eine neue Dame ein.
   *  @param oldboard das Board, von dem ausgegangen wird.
   *  @param column die Spalte, in der die Dame eingefuegt
   *    werden soll.
   */
  public BoardOfQueens(BoardOfQueens oldboard, int column) {
    // Kopiere den Inhalt
    super(8,8);
    for (int i = 0; i < 8; i++)
      for (int j = 0; j < 8; j++)
        content[i][j] = oldboard.content[i][j];
    // Setze Zeile und Spalte
    row = oldboard.row + 1;
    this.column = column;
    // Setze eine neue Dame in die gegebene Zeile
    setContent(row,column,QUEEN);
    // Pruefe, ob geschlagen werden kann
    canTake = canTake();
  }

  /** Pruefe, ob die zuletzt eingefuegte Dame
   *  eine andere Dame schlagen kann
   */
  private boolean canTake() {
    // Haben wir ein leeres Brett?
    if (row == -1 || column == -1)
      return false;
    // Andernfalls berechne alle moeglichen Zuege.
    boolean moves[][] = QUEEN.getDestinations(this,row,column);
    for (int i = 0; i < moves.length; i++)
      for (int j = 0; j < moves[i].length; j++)
        if (moves[i][j] && getContent(i,j) != null)
          return true;
    // Wenn wir den Check ueberstanden haben,
    // gibt es keine Schlagmoeglichkeiten
    return false;
  }

  /** Stellt dieses Szenario bereits die Problemloesung dar? */
  public boolean isSolution() {
    return row == 7 && !canTake;
  }

  /** Liefert alle Szenarien, zu denen man sich von diesem
    * Szenario aus bewegen kann.
    **/
  public Scenario[] getNextSteps() {
    // Falls wir am Ende angekommen sind,
    // koennen wir keine Felder mehr erzeugen
    if (row == 7)
      return new Scenario[0];
    // Wir speichern die moeglichen naechsten
    // Schritte in einem Feld
    BoardOfQueens[] next = new BoardOfQueens[8];
    // Wir belegen das Feld mit jeder
    // moeglichen naechsten Kombination.
    // Dann zaehlen wir die wirklich moeglichen
    // naechsten Schritte.
    int matches = 0;
    for (int i = 0; i < 8; i++) {
      next[i] = new BoardOfQueens(this,i);
      if (!next[i].canTake)
        matches++;
    }
    // Kopiere die Treffer in ein kleineres Array
    BoardOfQueens[] res = new BoardOfQueens[matches];
    matches = 0;
    for (int i = 0; i < 8; i++)
      if (!next[i].canTake) {
        res[matches] = next[i];
        matches++;
      }
    // Gib das Ergebnis zurueck
    return res;
  }

}