/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author Paul Phillips
 */

package scala.tools.nsc
package interpreter

/** This has a lot of duplication with other methods in Symbols and Types,
 *  but repl completion utility is very sensitive to precise output.  Best
 *  thing would be to abstract an interface for how such things are printed,
 *  as is also in progress with error messages.
 */
trait CompletionOutput {
  val global: Global

  import global._
  import definitions.{ isTupleType, isFunctionType, isRepeatedParamType }

  /** Reducing fully qualified noise for some common packages.
   */
  val typeTransforms = List(
    "java.lang." -> "",
    "scala.collection.immutable." -> "immutable.",
    "scala.collection.mutable." -> "mutable.",
    "scala.collection.generic." -> "generic."
  )

  def quietString(tp: String): String =
    typeTransforms.foldLeft(tp) {
      case (str, (prefix, replacement)) =>
        if (str startsWith prefix) replacement + (str stripPrefix prefix)
        else str
    }

  class MethodSymbolOutput(method: Symbol) {
    val pkg       = method.ownerChain find (_.isPackageClass) map (_.fullName) getOrElse ""

    def relativize(str: String): String = quietString(str stripPrefix (pkg + "."))
    def relativize(tp: Type): String    = relativize(tp.dealiasWiden.toString)
    def relativize(sym: Symbol): String = relativize(sym.info)    

    def braceList(tparams: List[String]) = if (tparams.isEmpty) "" else (tparams map relativize).mkString("[", ", ", "]")
    def parenList(params: List[Any])  = params.mkString("(", ", ", ")")

    def methodTypeToString(mt: MethodType) =
      (mt.paramss map paramsString mkString "") + ": " + relativize(mt.finalResultType)

    def typeToString(tp: Type): String = relativize(
      tp match {
        case x if isFunctionType(x)      => functionString(x)
        case x if isTupleType(x)         => tupleString(x)
        case x if isRepeatedParamType(x) => typeToString(x.typeArgs.head) + "*"
        case mt @ MethodType(_, _)       => methodTypeToString(mt)
        case x                           => x.toString
      }
    )

    def tupleString(tp: Type) = parenList(tp.dealiasWiden.typeArgs map relativize)
    def functionString(tp: Type) = tp.dealiasWiden.typeArgs match {
      case List(t, r) => t + " => " + r
      case xs         => parenList(xs.init) + " => " + xs.last
    }

    def tparamsString(tparams: List[Symbol])  = braceList(tparams map (_.defString))
    def paramsString(params: List[Symbol])    = {
      def paramNameString(sym: Symbol)  = if (sym.isSynthetic) "" else sym.nameString + ": "
      def paramString(sym: Symbol)      = paramNameString(sym) + typeToString(sym.info.dealiasWiden)

      val isImplicit = params.nonEmpty && params.head.isImplicit
      val strs = (params map paramString) match {
        case x :: xs if isImplicit  => ("implicit " + x) :: xs
        case xs                     => xs
      }
      parenList(strs)
    }

    def methodString() =
      method.keyString + " " + method.nameString + (method.info.normalize match {
        case NullaryMethodType(resType)         => ": " + typeToString(resType)
        case PolyType(tparams, resType)         => tparamsString(tparams) + typeToString(resType)
        case mt @ MethodType(_, _)              => methodTypeToString(mt)
        case x                                  => x.toString
      })
  }
}
