/*
 * 
 * Datei:   Matrix.java
 * 
 * ---------------------------------
 * 
 * Datum:           $Date: 2004/05/13 10:12:38 $
 * Autor:           Simon Tiffert 
 * Prüfungsnummer:  40
 * Firma:           T-Systems
 * eMail-Adresse:   simon.tiffert@t-systems.com
 * Version:         $Revision: 1.15 $
 * 
 * ---------------------------------
 * 
 */
package main.verarbeitung;

import main.Main;

/**
 * Klasse, die die Verwaltung der Matrix übernimmt.
 * Dabei befindet sich die Matrix selber als zentrales Element in 
 * dieser Klasse.<br> 
 * Alle Operationen, die die Matrix verändern, werden
 * durch setter zur Verfügung gestellt, die direkt Konsistenzprüfungen
 * durchführen.<br>
 * Alle Operationen, die Daten aus der Matrix abfragen sind über getter
 * definiert, die die Daten aufbereitet zur Verfügung stellen.<br> 
 * Zudem gibt es noch Prüfungen, die auch direkt in dieser Klasse
 * definiert sind und boolesche Ergebnisse zurückliefern.
 * Die Schnittstellen dieser Klasse sind möglichst vielfältig, um
 * alle nötigen Eingaben zu ermöglichen. Um die Algorithmen klein zu
 * halten, wurde hier ein Großteil der Funktionalität für die
 * Matrix implementiert.
 * 
 * @version $Revision: 1.15 $
 * @author Simon Tiffert
 */
public class Matrix
{

  /** Variable die anzeigt, ob die Matrix initialisiert wurde */
  private boolean isInitialisiert = false;

  /** zweidimensionales Feld als zentrales Element */
  private int[][] matrix;

  /** Anzahl der Sprungfelder */
  private int sperrFeldAnzahl = 0;

  /** Startpunkt der Sprungfolge */
  private Punkt startpunkt = null;

  /** Weg gefunden? */
  private boolean wegGefunden = false;

  /**
   * Diese Funktion gibt zurück, wieviele Felder besuchbar sind.
   * Dabei wird von der Anzahl der Felder die Anzahl der Sperrfelder
   * abgezogen
   * 
   * @return Feldanzahl (ohne Sperrfelder)
   */
  public int getFeldAnzahl()
  {
    return getXDimension() * getYDimension() - sperrFeldAnzahl;
  }

  /**
   * Diese Funktion gibt den Startpunkt als Punkt zurück
   * 
   * @return Startpunkt
   */
  public Punkt getStartpunkt()
  {
    return startpunkt;
  }

  /**
   * Diese Funktion gibt einen Wert in der Matrix zurück
   * 
   * @param y y-Koordinate des Punktes
   * @param x x-Koordinate des Punktes
   * @return den Wert der Matrix an der Stelle
   */
  public int getWert(int y, int x)
  {
    return matrix[y][x];
  }

  /**
   * Diese Funktion gibt die X-Dimension der Matrix zurück
   * 
   * @return X-Dimension der Matrix
   */
  public int getXDimension()
  {
    return matrix[0].length;
  }

  /**
   * Diese Funktion gibt die Y-Dimension der Matrix zurück
   * 
   * @return Y-Dimension der Matrix
   */
  public int getYDimension()
  {
    return matrix.length;
  }

  /**
   * Funktion, die angibt, ob die Matrix schon initialisiert ist
   * 
   * @return false: nicht initialisiert, true initialisiert
   */
  public boolean isInitialisiert()
  {
    return isInitialisiert;
  }

  /**
   * Funktion, die überprüft, ob ein Wert innerhalb der Matrix liegt.
   * Dabei werden folgende Möglichkeiten überprüft:
   * <ul>
   *  <li>y-Koordinate kleiner als 0</li>
   *  <li>x-Koordinate kleiner als 0</li>
   *  <li>y-Koordinate größer als y-Dimension der Matrix</li>
   *  <li>x-Koordinate größer als x-Dimension der Matrix</li>
   * </ul>
   * 
   * Ist keiner dieser Fälle eingetreten, so befindet sich der Punkt
   * in der Matrix
   * 
   * @param y y-Koordinate des Punktes
   * @param x x-Koordinate des Punktes
   * @return Ob der Punkt innerhalb der Matrix liegt
   */
  public boolean isInMatrix(int y, int x)
  {
    // wenn die y-Koordinate kleiner als 0 ist
    if (y < 0)
    {
      return false;
    }

    // wenn die x-Koordinate kleiner als 0 ist
    if (x < 0)
    {
      return false;
    }

    // wenn die y-Koordinate größer als die Y-Dimension der Matrix ist
    if (y >= this.getYDimension())
    {
      return false;
    }

    // wenn die x-Koordinate größer als die X-Dimension der Matrix ist
    if (x >= this.getXDimension())
    {
      return false;
    }

    // Punkt ist in Matrix
    return true;
  }

  /**
   * Funktion, die überprüft, ob das übergebene Feld ein Sperrfeld ist.
   * Dabei wird einfach überprüft, ob der Wert in der Matrix kleiner 
   * als 0 ist. 
   * 
   * @param y y-Koordinate des Punktes
   * @param x x-Koordinate des Punktes
   * @return Ob der Punkt schon ein Sperrfeld ist
   */
  public boolean isSperrfeld(int y, int x)
  {
    // ist der Punkt ein Sperrfeld, gib falsch zurück
    if (this.getWert(y, x) < 0)
    {
      return true;
    }

    // Punkt ist kein Sperrfeld 
    return false;
  }

  /**
   * Die Funktion überprüft, ob das übergebene Feld der Startpunkt ist.
   * Geprüft wird, ob der übergebene Punkt mit dem gespeicherten
   * Startpunkt übereinstimmt
   * 
   * @param y y-Koordinate des Punktes
   * @param x x-Koordinate des Punktes
   * @return Ob der Punkt der Startpunkt ist
   */
  public boolean isStartpunkt(int y, int x)
  {
    // ist der Punkt der Startpunkt, gib wahr zurück
    if (y == this.startpunkt.getY() && x == this.startpunkt.getX())
    {
      return true;
    }

    // Punkt ist nicht der Startpunkt
    return false;
  }

  /**
   * Funktion, die angibt, ob ein Weg gefunden wurde.
   * 
   * @return <code>false</code>: kein Weg, 
   *         <code>true</code>: Weg gefunden
   */
  public boolean isWegGefunden()
  {
    return wegGefunden;
  }

  /**
   * Diese Funktion erstellt die Matrix mit den übergebenen 
   * Dimensionen.
   * Dabei wird direkt die Konsistenzprüfung durchgeführt, ob die 
   * angegebenen Dimensionen übereinstimmen.
   * Prüfungen sind hier: 
   * <ul>
   *  <li>angegebene Dimension kleiner als 0</li>
   *  <li>angegebene Dimension größer als maximale Dimension</li>
   * </ul>
   * 
   * Liegen die angebenen Dimensionen innerhalb dieser Rahmen, so wird
   * die Matrix mit den Dimensionsangaben angelegt. 
   * 
   * @param m y-Dimension der Matrix
   * @param n x-Dimension der Matrix
   * @throws WrongDimensionException Falsche Dimensionsangabe
   */
  public void setMatrix(int m, int n) throws WrongDimensionException
  {
    if (m <= 0
      || n <= 0
      || m > Main.maxDimension
      || n > Main.maxDimension)
    {
      throw new WrongDimensionException();
    }

    matrix = new int[m][n];
  }

  /**
   * Diese Funktion setzt ein Sperrfeld in der Matrix. Dabei werden 
   * auch die Konsistenzprüfungen durchgeführt.
   * Geprüft wird:
   * <ul>
   *  <li>ob das Sperrfeld überhaupt innerhalb der Matrix liegt</li>
   *  <li>ob das Sperrfeld schon eingetragen wurde</li>
   *  <li>ob das Sperrfeld auf dem Startpunkt liegt</li>
   * </ul>
   * Wenn diese Prüfungen erfüllt sind, so wird das Sperrfeld als 
   * negativer Punkt (-1) in der Matrix eingetragen.
   * 
   * @param y y-Koordinate des Sperrfeldes
   * @param x x-Koordinate des Sperrfeldes
   * @throws OutOfMatrixException Sperrfeld außerhalb der Matrix
   * @throws IsSperrfeldException Sperrfeld schon vorhanden
   * @throws IsStartpunktException Sperrfeld auf Startpunkt
   */
  public void setSperrfeld(int y, int x)
    throws 
      OutOfMatrixException, 
      IsSperrfeldException, 
      IsStartpunktException
  {
    // wenn das Sperrfeld nicht in der Matrix liegt
    if (!this.isInMatrix(y, x))
    {
      // werfe die Exception
      throw new OutOfMatrixException(
        "Sperrfeld X(" + (y + 1) + "," + (x + 1) + ")");
    }

    // wenn das Sperrfeld schon besetzt ist
    if (this.isSperrfeld(y, x))
    {
      // werfe die Exception
      throw new IsSperrfeldException(
        "(" + (y + 1) + "," + (x + 1) + ")");
    }

    // wenn das Sperrfeld der Startpunkt ist
    if (this.isStartpunkt(y, x))
    {
      // werfe die Exception
      throw new IsStartpunktException(
        "(" + (y + 1) + "," + (x + 1) + ")");
    }
  
    // erhöhe die Sperrfeldanzahl
    sperrFeldAnzahl++;

    // setze den Wert als negative Zahl (-1)
    this.setWert(y, x, -1);
  }

  /**
   * Diese Funktion speichert den Startpunkt für die Sprungfolge.
   * Dabei wird direkt überprüft, ob der Wert innerhalb der Matrix 
   * liegt. Ansonsten wird eine Exception geworfen
   * 
   * @param y y-Koordinate des Startpunktes
   * @param x x-Koordinate des Startpunktes
   * @exception OutOfMatrixException Startpunkt außerhalb der Matrix
   * @exception ReDefinedStartpunktException Mehrfacher Startpunkt
   */
  public void setStartpunkt(int y, int x)
    throws OutOfMatrixException, ReDefinedStartpunktException
  {
    // Wenn ein Startpunkt außerhalb der Matrix definiert wird
    if (!this.isInMatrix(y, x))
    {
      // werfe Exception
      throw new OutOfMatrixException(
        "Startpunkt S(" + (y + 1) + "," + (x + 1) + ")");
    }

    // Wenn ein Startpunkt nochmal definiert wird, was bei der
    // Verwendung als Unterprogramm passieren kann
    if (startpunkt != null)
    {
      // werde Exception
      throw new ReDefinedStartpunktException();
    }
    
    //  setze die Matrix auf den Zustand initialisiert
    isInitialisiert = true;
    
    // Der Startpunkt wird erstellt und gespeichert
    startpunkt = new Punkt(y, x);
  }

  /**
   * Funktion, die den Weg als gefunden markiert
   */
  protected void setWegGefunden()
  {
    wegGefunden = true;
  }

  /**
   * Diese Funktion setzt den Wert der Matrix an einer Stelle
   * 
   * @param y y-Koordinate des Punktes
   * @param x x-Koordinate des Punktes
   * @param wert Wert auf den der Punkt gesetzt werden soll
   */
  protected void setWert(int y, int x, int wert)
  {
    matrix[y][x] = wert;
  }

}
