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

	import xsbti.{Logger => xLogger, F0}
	import xsbti.{Maybe,Position,Problem,Severity}

	import java.io.File

abstract class AbstractLogger extends Logger
{
	def getLevel: Level.Value
	def setLevel(newLevel: Level.Value)
	def setTrace(flag: Int)
	def getTrace: Int
	final def traceEnabled = getTrace >= 0
	def successEnabled: Boolean
	def setSuccessEnabled(flag: Boolean): Unit

	def atLevel(level: Level.Value) = level.id >= getLevel.id
	def control(event: ControlEvent.Value, message: => String): Unit

	def logAll(events: Seq[LogEvent]): Unit
	/** Defined in terms of other methods in Logger and should not be called from them. */
	final def log(event: LogEvent)
	{
		event match
		{
			case s: Success => success(s.msg)
			case l: Log => log(l.level, l.msg)
			case t: Trace => trace(t.exception)
			case setL: SetLevel => setLevel(setL.newLevel)
			case setT: SetTrace => setTrace(setT.level)
			case setS: SetSuccess => setSuccessEnabled(setS.enabled)
			case c: ControlEvent => control(c.event, c.msg)
		}
	}
}

object Logger
{
	// make public in 0.13
	private[sbt] val Null: AbstractLogger = new AbstractLogger {
		def getLevel: Level.Value = Level.Error
		def setLevel(newLevel: Level.Value) {}
		def getTrace = 0
		def setTrace(flag: Int) {}
		def successEnabled = false
		def setSuccessEnabled(flag: Boolean) {}
		def control(event: ControlEvent.Value, message: => String) {}
		def logAll(events: Seq[LogEvent]) {}
		def trace(t: => Throwable) {}
		def success(message: => String) {}
		def log(level: Level.Value, message: => String) {}
	}

	implicit def absLog2PLog(log: AbstractLogger): ProcessLogger = new BufferedLogger(log) with ProcessLogger
	implicit def log2PLog(log: Logger): ProcessLogger = absLog2PLog(new FullLogger(log))
	implicit def xlog2Log(lg: xLogger): Logger = new Logger {
		override def debug(msg: F0[String]): Unit = lg.debug(msg)
		override def warn(msg: F0[String]): Unit = lg.warn(msg)
		override def info(msg: F0[String]): Unit = lg.info(msg)
		override def error(msg: F0[String]): Unit = lg.error(msg)
		override def trace(msg: F0[Throwable]) = lg.trace(msg)
		override def log(level: Level.Value, msg: F0[String]) = lg.log(level, msg)
		def trace(t: => Throwable) = trace(f0(t))
		def success(s: => String) = info(f0(s))
		def log(level: Level.Value, msg: => String) =
		{
			val fmsg = f0(msg)
			level match
			{
				case Level.Debug => lg.debug(fmsg)
				case Level.Info => lg.info(fmsg)
				case Level.Warn => lg.warn(fmsg)
				case Level.Error => lg.error(fmsg)
			}
		}
	}
	def f0[T](t: =>T): F0[T] = new F0[T] { def apply = t }

	def m2o[S](m: Maybe[S]): Option[S] = if(m.isDefined) Some(m.get) else None
	def o2m[S](o: Option[S]): Maybe[S] = o match { case Some(v) => Maybe.just(v); case None => Maybe.nothing() }

	def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer], pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position =
		new Position {
			val line = o2m(line0)
			val lineContent = content
			val offset = o2m(offset0)
			val pointer = o2m(pointer0)
			val pointerSpace = o2m(pointerSpace0)
			val sourcePath = o2m(sourcePath0)
			val sourceFile = o2m(sourceFile0)
		}

	def problem(cat: String, pos: Position, msg: String, sev: Severity): Problem =
		new Problem
		{
			val category = cat
			val position = pos
			val message = msg
			val severity = sev
		}
}

/** This is intended to be the simplest logging interface for use by code that wants to log.
* It does not include configuring the logger. */
trait Logger extends xLogger
{
	final def verbose(message: => String): Unit = debug(message)
	final def debug(message: => String): Unit = log(Level.Debug, message)
	final def info(message: => String): Unit = log(Level.Info, message)
	final def warn(message: => String): Unit = log(Level.Warn, message)
	final def error(message: => String): Unit = log(Level.Error, message)

	def ansiCodesSupported = false
	
	def trace(t: => Throwable): Unit
	def success(message: => String): Unit
	def log(level: Level.Value, message: => String): Unit
	
	def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
	def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
	def info(msg: F0[String]): Unit = log(Level.Info, msg)
	def error(msg: F0[String]): Unit = log(Level.Error, msg)
	def trace(msg: F0[Throwable]) = trace(msg.apply)
	def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
}