/* NSC -- new Scala compiler
 * Copyright 2005-2009 LAMP/EPFL
 * @author Martin Odersky
 */
// $Id: AddInterfaces.scala 18387 2009-07-24 15:28:37Z odersky $

package scala.tools.nsc
package transform

import symtab._
import Flags._
import collection.mutable.{HashMap, ListBuffer}

abstract class AddInterfaces extends InfoTransform {
  import global._                  // the global environment
  import definitions._             // standard classes and methods

  /** <p>
   *    The phase sets <code>lateINTERFACE</code> for non-interface traits
   *    that now become interfaces.
   *  </p>
   *  <p>
   *    It sets <code>lateDEFERRED</code> for formerly concrete methods in
   *    such traits.
   *  </p>
   */
  override def phaseNewFlags: Long = lateDEFERRED | lateINTERFACE

  /** Type reference after erasure; to be defined in subclass
   *  <code>Erasure</code>.
   */
  def erasedTypeRef(sym: Symbol): Type

  /** Erasure type-map; to be defined in subclass
   *  <code>Erasure</code>.
   */
  def erasure: TypeMap

  /** A lazily constructed map that associates every non-interface trait with
   *  its implementation class.
   */
  private val implClassMap = new HashMap[Symbol, Symbol]

  /** A lazily constructed map that associates every concrete method in a non-interface
   *  trait that's currently compiled with its corresponding method in the trait's
   *  implementation class.
   */
  private val implMethodMap = new HashMap[Symbol, Symbol]

  override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = {
    implClassMap.clear
    implMethodMap.clear
    super.newPhase(prev)
  }

  /** Is given trait member symbol a member of the trait's interface 
   *  after this transform is performed? */
  private def isInterfaceMember(sym: Symbol): Boolean = {
    sym.isType ||
    { sym.info; // to set lateMETHOD flag if necessary
      sym.isMethod && !sym.isLabel && !(sym hasFlag (PRIVATE | BRIDGE)) && 
      !sym.isConstructor && !sym.isImplOnly
    }
  }

  /** Does symbol need an implementation method? */
  private def needsImplMethod(sym: Symbol): Boolean =
    sym.isMethod && isInterfaceMember(sym) && 
    (!(sym hasFlag (DEFERRED | SUPERACCESSOR)) || (sym hasFlag lateDEFERRED))

  def implClassPhase = currentRun.erasurePhase.next

  /** Return the implementation class of a trait; create a new one of one does not yet exist */  
  def implClass(iface: Symbol): Symbol = implClassMap.get(iface) match {
    case Some(c) => c
    case None =>
      atPhase(implClassPhase) {
        val implName = nme.implClassName(iface.name)
        var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol
        if (impl == NoSymbol) {
          impl = iface.cloneSymbolImpl(iface.owner)
          impl.name = implName
          impl.sourceFile = iface.sourceFile
          if (iface.owner.isClass)
            iface.owner.info.decls enter impl 
        }
        if (currentRun.compiles(iface)) currentRun.symSource(impl) = iface.sourceFile
        impl setPos iface.pos
        impl.flags = iface.flags & ~(INTERFACE | lateINTERFACE) | IMPLCLASS
        impl setInfo new LazyImplClassType(iface)
        implClassMap(iface) = impl
        if (settings.debug.value) log("generating impl class " + impl + " in " + iface.owner)//debug
        impl
      }
  }

  /** <p>
   *    A lazy type to set the info of an implementation class 
   *    The parents of an implementation class for trait <code>iface</code> are:
   *  </p>
   *  <ul>
   *    <li>superclass: <code>Object</code></li>
   *    <li>
   *      mixin classes: mixin classes of <code>iface</code> where every
   *      non-interface trait is mapped to its implementation class, followed
   *      by <code>iface</code> itself.
   *    </li>
   *  </ul>
   *  <p>
   *    The declarations of a mixin class are:
   *  </p>
   *  <ul>
   *    <li>
   *      for every interface member of <code>iface</code> its implemention
   *      method, if one is needed.
   *    </li>
   *    <li>
   *      every former member of <code>iface</code> that is implementation only
   *    </li>
   *  </ul>
   */
  private class LazyImplClassType(iface: Symbol) extends LazyType {

    /** Compute the decls of implementation class <code>implClass</code>, 
     *  given the decls <code>ifaceDecls</code> of its interface.
     *
     *  @param implClass  ...
     *  @param ifaceDecls ...
     *  @return           ...
     */
    private def implDecls(implClass: Symbol, ifaceDecls: Scope): Scope = {
      val decls = newScope
      if ((ifaceDecls lookup nme.MIXIN_CONSTRUCTOR) == NoSymbol)
        decls enter (implClass.newMethod(implClass.pos, nme.MIXIN_CONSTRUCTOR) 
                     setInfo MethodType(List(), UnitClass.tpe))
      for (sym <- ifaceDecls.iterator) {
        if (isInterfaceMember(sym)) {
          if (needsImplMethod(sym)) {
            val impl = sym.cloneSymbol(implClass).setInfo(sym.info).resetFlag(lateDEFERRED)
            if (currentRun.compiles(implClass)) implMethodMap(sym) = impl
            decls enter impl
            sym setFlag lateDEFERRED
          }
        } else {
          sym.owner = implClass 
          // note: OK to destructively modify the owner here, 
          // because symbol will not be accessible from outside the sourcefile.
          // mixin constructors are corrected separately; see TermSymbol.owner
          decls enter sym
        }
      }
      decls
    }

    override def complete(sym: Symbol) {
      def implType(tp: Type): Type = tp match {
        case ClassInfoType(parents, decls, _) =>
          assert(phase == implClassPhase)
          ClassInfoType(
            ObjectClass.tpe :: (parents.tail map mixinToImplClass) ::: List(iface.tpe),
            implDecls(sym, decls),
            sym)
        case PolyType(tparams, restpe) =>
          implType(restpe)
      }
      sym.setInfo(implType(atPhase(currentRun.erasurePhase)(iface.info)))
    }

    override def load(clazz: Symbol) { complete(clazz) }
  }

  /** If type <code>tp</code> refers to a non-interface trait, return a
   *  reference to its implementation class. Otherwise return <code>tp</code>
   *  itself.
   *
   *  @param tp ...
   *  @return   ...
   */
  private def mixinToImplClass(tp: Type): Type = 
    erasure(
      tp match { //@MATN: no normalize needed (comes after erasure)
        case TypeRef(pre, sym, args) if (sym.needsImplClass) =>
          typeRef(pre, implClass(sym), args)
        case _ =>
          tp
      })

  def transformMixinInfo(tp: Type): Type = tp match {
    case ClassInfoType(parents, decls, clazz) =>
      if (clazz.needsImplClass) {
        clazz setFlag lateINTERFACE
        implClass(clazz) // generate an impl class
      }
      val parents1 =
        if (parents.isEmpty) List()
        else {
          assert(!parents.head.typeSymbol.isTrait, clazz)
          if (clazz.isTrait) erasedTypeRef(ObjectClass) :: parents.tail
          else parents
        }
      val decls1 = decls filter (sym =>
        if (clazz hasFlag INTERFACE) isInterfaceMember(sym)
        else (!sym.isType || sym.isClass))

      //if (!clazz.isPackageClass) System.out.println("Decls of "+clazz+" after explicitOuter = " + decls1);//DEBUG
      //if ((parents1 eq parents) && (decls1 eq decls)) tp
      //else 
      ClassInfoType(parents1, decls1, clazz)
    case _ =>
      tp
  }

// Tree transformation --------------------------------------------------------------

  private class ChangeOwnerAndReturnTraverser(oldowner: Symbol, newowner: Symbol)
          extends ChangeOwnerTraverser(oldowner, newowner) {
    override def traverse(tree: Tree) {
      tree match {
        case Return(expr) =>
          if (tree.symbol == oldowner) tree.symbol = newowner
        case _ =>
      }
      super.traverse(tree)
    }
  }

  private def ifaceMemberDef(tree: Tree): Tree =
    if (!tree.isDef || !isInterfaceMember(tree.symbol)) EmptyTree
    else if (needsImplMethod(tree.symbol)) DefDef(tree.symbol, EmptyTree)
    else tree

  private def ifaceTemplate(templ: Template): Template =
    treeCopy.Template(templ, templ.parents, emptyValDef, templ.body map ifaceMemberDef)

  private def implMethodDef(tree: Tree, ifaceMethod: Symbol): Tree =
    implMethodMap.get(ifaceMethod) match {
      case Some(implMethod) =>
        tree.symbol = implMethod
        new ChangeOwnerAndReturnTraverser(ifaceMethod, implMethod)(tree)
      case None =>
        throw new Error("implMethod missing for " + ifaceMethod)
    }

  private def implMemberDef(tree: Tree): Tree =
    if (!tree.isDef || !isInterfaceMember(tree.symbol)) tree
    else if (needsImplMethod(tree.symbol)) implMethodDef(tree, tree.symbol)
    else EmptyTree

  /** Add mixin constructor definition 
   *    def $init$(): Unit = ()
   *  to `stats' unless there is already one.
   */
  private def addMixinConstructorDef(clazz: Symbol, stats: List[Tree]): List[Tree] = 
    if (treeInfo.firstConstructor(stats) != EmptyTree) stats
    else DefDef(clazz.primaryConstructor, Block(List(), Literal(()))) :: stats
    
  private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) {
    val templ1 = atPos(templ.pos) {
      Template(templ.parents, emptyValDef, 
               addMixinConstructorDef(clazz, templ.body map implMemberDef))
        .setSymbol(clazz.newLocalDummy(templ.pos))
    }
    new ChangeOwnerTraverser(templ.symbol.owner, clazz)(
      new ChangeOwnerTraverser(templ.symbol, templ1.symbol)(templ1))
  }

  def implClassDefs(trees: List[Tree]): List[Tree] = {
    val buf = new ListBuffer[Tree]
    for (tree <- trees)
      tree match {
        case ClassDef(_, _, _, impl) =>
          if (tree.symbol.needsImplClass)
            buf += {
              val clazz = implClass(tree.symbol).initialize
              ClassDef(clazz, implTemplate(clazz, impl))
            }
        case _ =>
      }
    buf.toList
  }

  /** Add calls to supermixin constructors
   *  <blockquote><pre>super[mix].$init$()</pre></blockquote>
   *  to <code>tree</code>. <code>tree</code> which is assumed to be the body
   *  of a constructor of class <code>clazz</code>.
   */
  private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = {
    def mixinConstructorCall(impl: Symbol): Tree = atPos(tree.pos) {
      Apply(Select(This(clazz), impl.primaryConstructor), List())
    }
    val mixinConstructorCalls: List[Tree] = {
      for (mc <- clazz.mixinClasses.reverse
           if mc.hasFlag(lateINTERFACE) && mc != ScalaObjectClass)
      yield mixinConstructorCall(implClass(mc))
    }
    (tree: @unchecked) match {
      case Block(stats, expr) =>
        // needs `hasSymbol' check because `supercall' could be a block (named / default args)
        val (presuper, supercall :: rest) = stats span (t => t.hasSymbol && t.symbol.hasFlag(PRESUPER))
        //assert(supercall.symbol.isClassConstructor, supercall)
        treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr)
    }
  }

  protected val mixinTransformer = new Transformer {
    override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] =
      (super.transformStats(stats, exprOwner) :::
       super.transformStats(implClassDefs(stats), exprOwner))
    override def transform(tree: Tree): Tree = {
      val sym = tree.symbol
      val tree1 = tree match {
        case ClassDef(mods, name, tparams, impl) if (sym.needsImplClass) =>
          implClass(sym).initialize // to force lateDEFERRED flags
          treeCopy.ClassDef(tree, mods | INTERFACE, name, tparams, ifaceTemplate(impl))
        case DefDef(mods, name, tparams, vparamss, tpt, rhs) 
        if (sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass) =>
          treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, 
                      addMixinConstructorCalls(rhs, sym.owner)) // (3)
        case Template(parents, self, body) =>
          val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
          treeCopy.Template(tree, parents1, emptyValDef, body)
        case This(_) =>
          if (sym.needsImplClass) {
            val impl = implClass(sym)
            var owner = currentOwner
            while (owner != sym && owner != impl) owner = owner.owner;
            if (owner == impl) This(impl) setPos tree.pos
            else tree
          } else tree
/* !!!
        case Super(qual, mix) =>
          val mix1 = mix
            if (mix == nme.EMPTY.toTypeName) mix
            else {
              val ps = atPhase(currentRun.erasurePhase) {
                sym.info.parents dropWhile (p => p.symbol.name != mix)
              }
              assert(!ps.isEmpty, tree);
              if (ps.head.symbol.needsImplClass) implClass(ps.head.symbol).name 
              else mix
            }
          if (sym.needsImplClass) Super(implClass(sym), mix1) setPos tree.pos
          else treeCopy.Super(tree, qual, mix1)
*/
        case _ =>
          tree
      }
      super.transform(tree1)
    }
  }
}
/*
    val ensureNoEscapes = new TypeTraverser {
      def ensureNoEscape(sym: Symbol) {
        if (sym.hasFlag(PRIVATE)) {
          var o = currentOwner;
          while (o != NoSymbol && o != sym.owner && !o.isLocal && !o.hasFlag(PRIVATE))
          o = o.owner
          if (o == sym.owner) sym.makeNotPrivate(base);
        }
      }
      def traverse(t: Type): TypeTraverser = {
        t match {
          case TypeRef(qual, sym, args) =>
            ensureNoEscape(sym)
            mapOver(t)
          case ClassInfoType(parents, decls, clazz) =>
            parents foreach { p => traverse; () }
            traverse(t.typeOfThis)
          case _ => 
            mapOver(t)
        }
        this
      }
    }

*/