/* sbt -- Simple Build Tool
 * Copyright 2008, 2009, 2010 Mark Harrah
 */
package sbt

	import scala.collection.mutable.ListBuffer

/** A logger that can buffer the logging done on it and then can flush the buffer
* to the delegate logger provided in the constructor.  Use 'startRecording' to
* start buffering and then 'play' from to flush the buffer to the backing logger.
*  The logging level set at the time a message is originally logged is used, not
* the level at the time 'play' is called.
*
* This class assumes that it is the only client of the delegate logger.
* */
class BufferedLogger(delegate: AbstractLogger) extends BasicLogger
{
	private[this] val buffer = new ListBuffer[LogEvent]
	private[this] var recording = false

	/** Enables buffering. */
	def record() = synchronized { recording = true }
	def buffer[T](f: => T): T = {
		record()
		try { f }
		finally { stopQuietly() }
	}
	def bufferQuietly[T](f: => T): T = {
		record()
		try
		{
			val result = f
			clear()
			result
		}
		catch { case e: Throwable => stopQuietly(); throw e }
	}
	def stopQuietly() = synchronized { try { stop() } catch { case e: Exception => () } }

	/** Flushes the buffer to the delegate logger.  This method calls logAll on the delegate
	* so that the messages are written consecutively. The buffer is cleared in the process. */
	def play(): Unit = synchronized { delegate.logAll(buffer.readOnly); buffer.clear() }
	/** Clears buffered events and disables buffering. */
	def clear(): Unit = synchronized { buffer.clear(); recording = false }
	/** Plays buffered events and disables buffering. */
	def stop(): Unit = synchronized { play(); clear() }

	override def ansiCodesSupported = delegate.ansiCodesSupported
	override def setLevel(newLevel: Level.Value): Unit = synchronized {
		super.setLevel(newLevel)
		if(recording)
			buffer += new SetLevel(newLevel)
		else
			delegate.setLevel(newLevel)
	}
	override def setSuccessEnabled(flag: Boolean): Unit = synchronized {
		super.setSuccessEnabled(flag)
		if(recording)
			buffer += new SetSuccess(flag)
		else
			delegate.setSuccessEnabled(flag)
	}
	override def setTrace(level: Int): Unit = synchronized {
		super.setTrace(level)
		if(recording)
			buffer += new SetTrace(level)
		else
			delegate.setTrace(level)
	}

	def trace(t: => Throwable): Unit =
		doBufferableIf(traceEnabled, new Trace(t), _.trace(t))
	def success(message: => String): Unit =
		doBufferable(Level.Info, new Success(message), _.success(message))
	def log(level: Level.Value, message: => String): Unit =
		doBufferable(level, new Log(level, message), _.log(level, message))
	def logAll(events: Seq[LogEvent]): Unit = synchronized {
		if(recording)
			buffer ++= events
		else
			delegate.logAll(events)
	}
	def control(event: ControlEvent.Value, message: => String): Unit =
		doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
	private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
		doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
	private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit = synchronized {
		if(condition)
		{
			if(recording)
				buffer += appendIfBuffered
			else
				doUnbuffered(delegate)
		}
	}
}