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

import xsbti.Logger
import scala.tools.nsc.{ GenericRunnerCommand, Interpreter, InterpreterLoop, ObjectRunner, Settings }
import scala.tools.nsc.interpreter.InteractiveReader
import scala.tools.nsc.reporters.Reporter
import scala.tools.nsc.util.ClassPath

class ConsoleInterface {
  def commandArguments(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Array[String] =
    MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String]

  def run(args: Array[String], bootClasspathString: String, classpathString: String, initialCommands: String, cleanupCommands: String, loader: ClassLoader, bindNames: Array[String], bindValues: Array[Any], log: Logger) {
    lazy val interpreterSettings = MakeSettings.sync(args.toList, log)
    val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log)

    if (!bootClasspathString.isEmpty)
      compilerSettings.bootclasspath.value = bootClasspathString
    compilerSettings.classpath.value = classpathString
    log.info(Message("Starting scala interpreter..."))
    log.info(Message(""))
    val loop = new InterpreterLoop {

      override def createInterpreter() = {

        if (loader ne null) {
          in = InteractiveReader.createDefault()
          interpreter = new Interpreter(settings) {
            override protected def parentClassLoader = if (loader eq null) super.parentClassLoader else loader
            override protected def newCompiler(settings: Settings, reporter: Reporter) = super.newCompiler(compilerSettings, reporter)
          }
          interpreter.setContextClassLoader()
        } else
          super.createInterpreter()

        def bind(values: Seq[(String, Any)]) {
          // for 2.8 compatibility
          final class Compat {
            def bindValue(id: String, value: Any) =
              interpreter.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value)
          }
          implicit def compat(a: AnyRef): Compat = new Compat

          for ((id, value) <- values)
            interpreter.beQuietDuring(interpreter.bindValue(id, value))
        }

        bind(bindNames zip bindValues)

        if (!initialCommands.isEmpty)
          interpreter.interpret(initialCommands)
      }
      override def closeInterpreter() {
        if (!cleanupCommands.isEmpty)
          interpreter.interpret(cleanupCommands)
        super.closeInterpreter()
      }
    }
    loop.main(if (loader eq null) compilerSettings else interpreterSettings)
  }
}
object MakeSettings {
  def apply(args: List[String], log: Logger) =
    {
      val command = new GenericRunnerCommand(args, message => log.error(Message(message)))
      if (command.ok)
        command.settings
      else
        throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg)
    }

  def sync(args: Array[String], bootClasspathString: String, classpathString: String, log: Logger): Settings =
    {
      val compilerSettings = sync(args.toList, log)
      if (!bootClasspathString.isEmpty)
        compilerSettings.bootclasspath.value = bootClasspathString
      compilerSettings.classpath.value = classpathString
      compilerSettings
    }

  def sync(options: List[String], log: Logger) =
    {
      val settings = apply(options, log)

      // -Yrepl-sync is only in 2.9.1+
      final class Compat {
        def Yreplsync = settings.BooleanSetting("-Yrepl-sync", "For compatibility only.")
      }
      implicit def compat(s: Settings): Compat = new Compat

      settings.Yreplsync.value = true
      settings
    }
}
