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<CollectionScope> scopes;

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

  public NewConsoleReporter(PrintStream output) {
    this.output = output;
    this.scopes = new ArrayDeque<>();
    this.hasPrintedAnyLines = false;
    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) {
    CollectionScope containingScope = this.scopes.peekLast();
    if(containingScope.hasPrintedAnything())
      this.output.println();

    containingScope.beginCollection(collection);
    this.hasPrintedAnyLines = true;
    this.numCollectionsInScope += 1;

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

    CollectionScope newScope = CollectionScope.forCollection(collection, containingScope);
    this.scopes.addLast(newScope);
  }

  @Override
  public void endCollection(SpecCollection collection) {
    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(CollectionScope.forRoot(this.output));
  }

  @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 CollectionScope {
    private final PrintStream output;
    private final String collectionIndentation;
    private int numItemsPrinted;

    public static CollectionScope forCollection(SpecCollection collection, CollectionScope parentScope) {
      return new CollectionScope(parentScope.output, parentScope.collectionIndentation + "  ");
    }

    public static CollectionScope forRoot(PrintStream output) {
      return new CollectionScope(output, "");
    }

    private CollectionScope(PrintStream output, String collectionIndentation) {
      this.output = output;
      this.collectionIndentation = collectionIndentation;
      this.numItemsPrinted = 0;
    }

    public void beginCollection(SpecCollection collection) {
      this.output.println(this.collectionIndentation + collection.description());
      somethingPrinted();
    }

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

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

@@ -373,79 +373,8 @@
 nes;
-%0A  private String specIndentation;%0A  private int numCollectionsInScope;
 %0A%0A  
@@ -526,75 +526,8 @@
 se;%0A
-    this.specIndentation = %22%22;%0A    this.numCollectionsInScope = 0;%0A
   %7D%0A
@@ -973,121 +973,8 @@
 rue;
-%0A    this.numCollectionsInScope += 1;%0A%0A    if(this.numCollectionsInScope %3E 1)%0A      this.specIndentation += %22  %22;
 %0A%0A  
@@ -1175,155 +1175,8 @@
 ) %7B%0A
-    this.specIndentation = this.specIndentation.substring(0, Math.max(0, this.specIndentation.length() - 2));%0A    this.numCollectionsInScope -= 1;%0A
     
@@ -1664,79 +1664,83 @@
     
-this.output.print(this.specIndentation + %22- %22 + spec.intendedBehavior()
+CollectionScope scope = this.scopes.peekLast();%0A    scope.specStarting(spec
 );%0A 
@@ -2042,16 +2042,50 @@
 Scope %7B%0A
+    private final boolean isRoot;%0A
     priv
@@ -2151,29 +2151,61 @@
 ectionIndent
-ation
+;%0A    private final String specIndent
 ;%0A    privat
@@ -2329,16 +2329,84 @@
 rent
-Scope) %7B
+) %7B%0A      String specIndent = parent.isRoot ? %22%22 : parent.specIndent + %22  %22;
 %0A   
@@ -2439,27 +2439,46 @@
 ope(
+%0A        false,%0A        
 parent
-Scope
 .output,
  par
@@ -2477,20 +2477,23 @@
 put,
+%0A       
  parent
-Scope
 .col
@@ -2509,20 +2509,42 @@
 dent
-ation
  + %22  %22
+,%0A        specIndent%0A      
 );%0A 
@@ -2650,18 +2650,68 @@
 ope(
-output, %22%22
+%0A        true,%0A        output,%0A        %22%22,%0A        %22%22%0A      
 );%0A 
@@ -2744,16 +2744,32 @@
 onScope(
+boolean isRoot, 
 PrintStr
@@ -2807,16 +2807,58 @@
 dent
-ation) %7B
+, String specIndent) %7B%0A      this.isRoot = isRoot;
 %0A   
@@ -2909,21 +2909,16 @@
 onIndent
-ation
  = colle
@@ -2928,21 +2928,52 @@
 onIndent
-ation
+;%0A      this.specIndent = specIndent
 ;%0A      
@@ -3113,21 +3113,16 @@
 onIndent
-ation
  + colle
@@ -3336,15 +3336,139 @@
 +;%0A    %7D
+%0A%0A    public void specStarting(Spec spec) %7B%0A      this.output.print(this.specIndent + %22- %22 + spec.intendedBehavior());%0A    %7D
 %0A  %7D%0A%7D%0A

Move spec indentation to CollectionScope