package info.javaspec.console;

import info.javaspec.Spec;
import info.javaspec.SpecCollection;

import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

final class NewConsoleReporter implements Reporter {
  private final PrintStream output;
  private final Deque<Scope> scopes;

  private boolean hasPrintedAnyLines;
  private String collectionIndentation;
  private String specIndentation;
  private int numCollectionsInScope;

  public NewConsoleReporter(PrintStream output) {
    this.output = output;
    this.scopes = new ArrayDeque<>();
    this.hasPrintedAnyLines = false;
    this.collectionIndentation = "";
    this.specIndentation = "";
    this.numCollectionsInScope = 0;
  }

  /* HelpObserver */

  @Override
  public void writeMessage(List<String> lines) {
    lines.forEach(this.output::println);
  }

  /* RunObserver */

  @Override
  public void beginCollection(SpecCollection collection) {
    if(this.scopes.peekLast().hasPrintedAnything())
      this.output.println();

    this.output.println(this.collectionIndentation + collection.description());
    this.scopes.peekLast().somethingPrinted();
    this.hasPrintedAnyLines = true;
    this.numCollectionsInScope += 1;

    this.collectionIndentation += "  ";
    if(this.numCollectionsInScope > 1)
      this.specIndentation += "  ";

    this.scopes.addLast(Scope.forCollection(collection));
  }

  @Override
  public void endCollection(SpecCollection collection) {
    this.collectionIndentation = this.collectionIndentation.substring(0, this.collectionIndentation.length() - 2);
    this.specIndentation = this.specIndentation.substring(0, Math.max(0, this.specIndentation.length() - 2));
    this.numCollectionsInScope -= 1;
    this.scopes.removeLast();
  }

  @Override
  public boolean hasFailingSpecs() {
    throw new UnsupportedOperationException();
  }

  @Override
  public void runStarting() {
    this.scopes.addLast(Scope.forRoot());
  }

  @Override
  public void runFinished() {
    if(this.hasPrintedAnyLines)
      this.output.println();

    this.output.println("[Testing complete] Passed: 0, Failed: 0, Total: 0");
  }

  @Override
  public void specStarting(Spec spec) {
    this.output.print(this.specIndentation + "- " + spec.intendedBehavior());
  }

  @Override
  public void specFailed(Spec spec) {
    this.output.println(": FAIL");
    this.hasPrintedAnyLines = true;
  }

  @Override
  public void specPassed(Spec spec) {
    this.output.println(": PASS");
    this.hasPrintedAnyLines = true;
  }

  private static final class Scope {
    private final String source;
    private int numItemsPrinted;

    public static Scope forRoot() {
      return new Scope("root");
    }

    public static Scope forCollection(SpecCollection collection) {
      return new Scope(collection.description());
    }

    private Scope(String source) {
      this.source = source;
      this.numItemsPrinted = 0;
    }

    public boolean hasPrintedAnything() {
      return this.numItemsPrinted > 0;
    }

    public void somethingPrinted() {
      this.numItemsPrinted++;
    }
  }
}

@@ -306,16 +306,26 @@
 l Deque%3C
+Collection
 Scope%3E s
@@ -971,33 +971,88 @@
     
-if(this.scopes.peekLast()
+CollectionScope containingScope = this.scopes.peekLast();%0A    if(containingScope
 .has
@@ -1184,38 +1184,31 @@
 ));%0A    
-this.scopes.peekLast()
+containingScope
 .somethi
@@ -1426,32 +1426,42 @@
 .scopes.addLast(
+Collection
 Scope.forCollect
@@ -2016,16 +2016,26 @@
 addLast(
+Collection
 Scope.fo
@@ -2652,16 +2652,26 @@
 l class 
+Collection
 Scope %7B%0A
@@ -2747,32 +2747,42 @@
   public static 
+Collection
 Scope forRoot() 
@@ -2796,24 +2796,34 @@
  return new 
+Collection
 Scope(%22root%22
@@ -2850,16 +2850,26 @@
  static 
+Collection
 Scope fo
@@ -2926,16 +2926,26 @@
 urn new 
+Collection
 Scope(co
@@ -2988,16 +2988,26 @@
 private 
+Collection
 Scope(St

Introduce CollectionScope