package de.uni.karlsruhe.aifb.prog2.solutions;

/** Diese Klasse loest ein Problem, das durch ein
  * Anfangsszenario gegeben ist.
  **/
public class Solver {

  /** Innere Klasse, stellt einen Iterationszustand dar. */
  private static class Iteration
  {
    /** Das Scenario, um das es sich dreht. */
    private Scenario scenario;

    /** Die Schritte, zu denen man von dem Scenario aus
      * gehen kann.
      **/
    private Scenario[] next;

    /** Der aktuelle Schritt. */
    private int current;

    /** Konstruktor. */
    public Iteration(Scenario scenario)
    {
      this.scenario = scenario;
      next = scenario.getNextSteps();
      current = 0;
    }

    /** Gib das naechste, noch nicht bearbeitete Szenario
      * zurueck.
      **/
    public Scenario getNext()
    {
      if (current < next.length)
      {
        return next[current++];
      }
      return null;
    }

    /** Gib das Scenario zurueck, von dem ausgegangen wird. */
    public Scenario getScenario()
    {
      return scenario;
    }
  }

  /** Der Weg, der zur Loesung gegangen wird. */
  private Iteration[] solution;

  /** Beinhaltet das Objekt eine Loesung? */
  private boolean isSolution;

  /** Wie viele Schritte bis zur Loesung? */
  private int steps;

  /** Die Fehlschlaege auf dem Weg zur Loesung. */
  private CollectionOfFailures failures;

  /** Wollen wir Debug-Ausgaben haben? */
  private boolean isDebug;

  /** Constructor. Dem Objekt werden das zu loesende
    * Anfangsszenario sowie eine maximale Zahl
    * erlaubter Loesungsversuche uebergeben.
    **/
  public Solver(Scenario start, int maxSteps)
  {
    this(start,maxSteps,false);
  }

  /** Constructor. Dem Objekt werden das zu loesende
    * Anfangsszenario sowie eine maximale Zahl
    * erlaubter Loesungsversuche uebergeben.
    **/
  public Solver(Scenario start, int maxSteps, boolean isDebug)
  {
    failures = new CollectionOfFailures();
    solution = new Iteration[maxSteps + 1];
    solution[0] = new Iteration(start);
    isSolution = true;
    steps = 1;
    this.isDebug = isDebug;
    toNext();
  }

  /** Beinhaltet das Objekt momentan eine Loesung? */
  public boolean isSolution()
  {
    return isSolution;
  }

  /** Setzt den Solver auf die naechste moegliche Loesung,
    * falls noch eine existiert.
    **/
  public void toNext()
  {
    // Muessen wir nach einer Loesung suchen?
    if (!isSolution)
    {
      return;
    }
    // Beginne mit der Iteration.
    steps--;
    bigLoop: while
      (steps >= 0 && !solution[steps].getScenario().isSolution())
    {
      // Ueberpruefe den naechsten moeglichen Schritt.
      Scenario next = solution[steps].getNext();
      if (next != null)
      {
        // Haben wir diesen Zug zuvor schon einmal gemacht?
        for (int i = 0; i <= steps; i++)
          if (next.equals(solution[i].getScenario())) {
            if (isDebug)
              System.out.println("Move already made in step "
                 + i + ".");
            continue bigLoop;
          }
        // Ist das Ganze ein bereits bekannter Fehlschlag?
        if (failures.containsUntil(next,steps + 1) >= 0) {
          if (isDebug)
            System.out.println("Move already known as failure.");
          continue;
        }
        // Sind wir beim allerletzten Schritt und haben noch keine
        // Loesung?
        if (steps + 2 == solution.length && !next.isSolution())
        {
          if (isDebug)
            System.out.println("Out of steps.");
          continue;
        }
        // Andernfalls gehe in den naechsten Schritt.
        steps++;
        if (isDebug)
          System.out.println("steps == " + steps);
        solution[steps] = new Iteration(next);
        continue;
      }
      // Andernfalls, gehe einen Schritt zurueck.
      failures.addFailure(solution[steps].getScenario(),steps);
      steps--;
      if (isDebug)
        System.out.println("steps == " + steps);
      if (steps < 0)
      {
        isSolution = false;
        return;
      }
    }
    // Das war's :-)
  }

  /** Gib das Scenario an der entsprechenden Stelle zurueck. */
  public Scenario get(int index)
  {
    // Im Fall des Start-Szenarios gib immer etwas zurueck.
    if (index == 0)
    {
      return solution[0].getScenario();
    }
    // Falls wir ansonsten keine Loesung haben, gib null zurueck.
    if (!isSolution)
    {
      return null;
    }
    // Falls wir oberhalb des maximalen Indexes sind, ebenfalls.
    if (index > steps)
    {
      return null;
    }
    // Andernfalls liefere das Ergebnis.
    return solution[index].getScenario();
  }

  /** Gib die Anzahl der Schritte zur Loesung zurueck. */
  public int getSteps()
  {
    if (!isSolution)
    {
      return 0;
    }
    else
    {
      return steps;
    }
  }

}