package ic

import java.io.PrintStream

/**
 * A class to print with tabbing.
 *
 * To use:
 *
 *  - Create a new IndentingPrintStream "out" using System.out as
 *    the constructor parameter.
 *
 *  - Whenever you enter a scope and wish to indent output more,
 *    call out.indentMore().
 *
 *  - Whenever you exit a scope and wish to indent output less,
 *    call out.indentLess().
 *
 *  - Don't print to the underlying print stream directly.
 */
class IndentingPrintStream(out: PrintStream) {

  /** The number of levels we wish to indent the next line */
  private var indentLevel = 0

  /** Was the last thing printed a new line? */
  private var atStartOfLine = true

  /**
   * Indicate that you would like to indent all subsequent lines
   * one more tab level than the current line.
   */
  def indentMore(): Unit = { indentLevel += 1 }

  /**
   * Indicate that you would like to indent all subsequent lines
   * one less tab level than the current line.
   */
  def indentLess(): Unit = { indentLevel -= 1 }

  /**
   * Split a String into pieces with no internal \n characters, and print
   * each one, separating them with the right amount of padding.
   */
  private def printWithTabbing(s: String): Unit = {
    val lines = s.split("\n", -1)
    if (atStartOfLine) pad()
    for (i <- 0 until lines.length - 1) {
      out.println(lines(i))
      pad()
    }
    out.print(lines(lines.length - 1))
  }

  /**
   * Insert spaces proportional to the current depth of tabbing.  This
   * is called whenever we start printing after a new line.
   */
  private def pad(): Unit = {
    for (_ <- 0 until indentLevel) {
      out.print("|  ")
    }
    atStartOfLine = false
  }

  /** A variant of printf that is aware of line starts and indentation. */
  def printf(s: String, args: Any*): Unit = {
    printWithTabbing(s.format(args*))
  }

  /** A variant of print that is aware of line starts and indentation. */
  def print(s: String): Unit = { printWithTabbing(s) }

  /** A variant of println that is aware of line starts and indentation. */
  def println(s: String): Unit = {
    printWithTabbing(s)
    println()
  }

  /** A variant of println that is aware of line starts and indentation. */
  def println(): Unit = {
    out.println()
    atStartOfLine = true
  }

  // All the other expected methods to print common built-in types
  def print(x: Any): Unit = { printWithTabbing(x.toString) }
  def println(x: Any): Unit = { printWithTabbing(x.toString); println() }
}
