package xsbt
package boot

/** A wrapper around 'raw' static methods to meet the sbt application interface. */
class PlainApplication private (mainMethod: java.lang.reflect.Method) extends xsbti.AppMain {
  override def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = {
    // TODO - Figure out if the main method returns an Int...
    val IntClass = classOf[Int]
    val ExitClass = classOf[xsbti.Exit]
    // It seems we may need to wrap exceptions here...
    try mainMethod.getReturnType match {
        case ExitClass =>
          mainMethod.invoke(null, configuration.arguments).asInstanceOf[xsbti.Exit]
        case IntClass =>
          PlainApplication.Exit(mainMethod.invoke(null, configuration.arguments).asInstanceOf[Int])
        case _ => 
          // Here we still invoke, but return 0 if sucessful (no exceptions).
          mainMethod.invoke(null, configuration.arguments)
          PlainApplication.Exit(0)
    } catch {
      // This is only thrown if the underlying reflective call throws.
      // Let's expose the underlying error.
      case e: java.lang.reflect.InvocationTargetException if e.getCause != null =>
        throw e.getCause
    }
    
  }
}
/** An object that lets us detect compatible "plain" applications and launch them reflectively. */
object PlainApplication {
  def isPlainApplication(clazz: Class[_]): Boolean = findMainMethod(clazz).isDefined
  def apply(clazz: Class[_]): xsbti.AppMain =
   findMainMethod(clazz) match {
     case Some(method) => new PlainApplication(method)
     case None => sys.error("Class: " + clazz + " does not have a main method!")
   }
  private def findMainMethod(clazz: Class[_]): Option[java.lang.reflect.Method] =
    try {
      val method = 
        clazz.getMethod("main", classOf[Array[String]])
      if(java.lang.reflect.Modifier.isStatic(method.getModifiers)) Some(method)
      else None
    } catch {
      case n: NoSuchMethodException => None
    }

  case class Exit(code: Int) extends xsbti.Exit
}