package scala.reflect
import scala.collection.mutable.{ WrappedArray, ArrayBuilder }
import java.lang.{ Class => JClass }
trait ClassManifest[T] extends OptManifest[T] with Equals with Serializable {
  
  def erasure: JClass[_]
  
  private def subtype(sub: JClass[_], sup: JClass[_]): Boolean = {
    def loop(left: Set[JClass[_]], seen: Set[JClass[_]]): Boolean = {
      left.nonEmpty && {
        val next = left.head
        val supers = next.getInterfaces.toSet ++ Option(next.getSuperclass)
        supers(sup) || {
          val xs = left ++ supers filterNot seen
          loop(xs - next, seen + next)
        }
      }
    }
    loop(Set(sub), Set())
  }
  
  private def subargs(args1: List[OptManifest[_]], args2: List[OptManifest[_]]) = (args1 corresponds args2) {
    
    case (x: ClassManifest[_], y: ClassManifest[_]) => x <:< y
    case (x, y)                                     => (x eq NoManifest) && (y eq NoManifest)
  }
  
  def <:<(that: ClassManifest[_]): Boolean = {    
    
    def cannotMatch = {
      import Manifest._
      that.isInstanceOf[AnyValManifest[_]] || (that eq AnyVal) || (that eq Nothing) || (that eq Null)
    }
    
    
    
    
    
    
    
    
    
    
    
    !cannotMatch && {
      
      if (this.erasure == that.erasure)
        subargs(this.typeArguments, that.typeArguments)
      
      
      else
        that.typeArguments.isEmpty && subtype(this.erasure, that.erasure)
    }
  }
  
  
  def >:>(that: ClassManifest[_]): Boolean =
    that <:< this
  
  def canEqual(other: Any) = other match {
    case _: ClassManifest[_]  => true
    case _                    => false
  }
 
  
  override def equals(that: Any): Boolean = that match {
    case m: ClassManifest[_] => (m canEqual this) && (this.erasure == m.erasure)
    case _                   => false
  }
  override def hashCode = this.erasure.##
  protected def arrayClass[T](tp: JClass[_]): JClass[Array[T]] = 
    java.lang.reflect.Array.newInstance(tp, 0).getClass.asInstanceOf[JClass[Array[T]]]
  def arrayManifest: ClassManifest[Array[T]] = 
    ClassManifest.classType[Array[T]](arrayClass[T](erasure))
  def newArray(len: Int): Array[T] =
    java.lang.reflect.Array.newInstance(erasure, len).asInstanceOf[Array[T]]
  def newArray2(len: Int): Array[Array[T]] =
    java.lang.reflect.Array.newInstance(arrayClass[T](erasure), len)
      .asInstanceOf[Array[Array[T]]]
  def newArray3(len: Int): Array[Array[Array[T]]] =
    java.lang.reflect.Array.newInstance(arrayClass[Array[T]](arrayClass[T](erasure)), len)
      .asInstanceOf[Array[Array[Array[T]]]]
  def newArray4(len: Int): Array[Array[Array[Array[T]]]] =
    java.lang.reflect.Array.newInstance(arrayClass[Array[Array[T]]](arrayClass[Array[T]](arrayClass[T](erasure))), len)
      .asInstanceOf[Array[Array[Array[Array[T]]]]]
  def newArray5(len: Int): Array[Array[Array[Array[Array[T]]]]] =
    java.lang.reflect.Array.newInstance(arrayClass[Array[Array[Array[T]]]](arrayClass[Array[Array[T]]](arrayClass[Array[T]](arrayClass[T](erasure)))), len)
      .asInstanceOf[Array[Array[Array[Array[Array[T]]]]]]
  def newWrappedArray(len: Int): WrappedArray[T] =
    
    new WrappedArray.ofRef[T with AnyRef](newArray(len).asInstanceOf[Array[T with AnyRef]]).asInstanceOf[WrappedArray[T]]
  
  def newArrayBuilder(): ArrayBuilder[T] = 
    
    new ArrayBuilder.ofRef[T with AnyRef]()(this.asInstanceOf[ClassManifest[T with AnyRef]]).asInstanceOf[ArrayBuilder[T]]
  def typeArguments: List[OptManifest[_]] = List()
  protected def argString = 
    if (typeArguments.nonEmpty) typeArguments.mkString("[", ", ", "]")
    else if (erasure.isArray) "["+ClassManifest.fromClass(erasure.getComponentType)+"]"
    else ""
}
object ClassManifest {
  val Byte    = Manifest.Byte
  val Short   = Manifest.Short
  val Char    = Manifest.Char
  val Int     = Manifest.Int
  val Long    = Manifest.Long
  val Float   = Manifest.Float
  val Double  = Manifest.Double
  val Boolean = Manifest.Boolean
  val Unit    = Manifest.Unit
  val Any     = Manifest.Any
  val Object  = Manifest.Object
  val AnyVal  = Manifest.AnyVal
  val Nothing = Manifest.Nothing
  val Null    = Manifest.Null
  def fromClass[T](clazz: JClass[T]): ClassManifest[T] = clazz match {
    case java.lang.Byte.TYPE      => Byte.asInstanceOf[ClassManifest[T]]
    case java.lang.Short.TYPE     => Short.asInstanceOf[ClassManifest[T]]
    case java.lang.Character.TYPE => Char.asInstanceOf[ClassManifest[T]]
    case java.lang.Integer.TYPE   => Int.asInstanceOf[ClassManifest[T]]
    case java.lang.Long.TYPE      => Long.asInstanceOf[ClassManifest[T]]
    case java.lang.Float.TYPE     => Float.asInstanceOf[ClassManifest[T]]
    case java.lang.Double.TYPE    => Double.asInstanceOf[ClassManifest[T]]
    case java.lang.Boolean.TYPE   => Boolean.asInstanceOf[ClassManifest[T]]
    case java.lang.Void.TYPE      => Unit.asInstanceOf[ClassManifest[T]]
    case _                        => classType[T with AnyRef](clazz).asInstanceOf[ClassManifest[T]]
  }
  def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = Manifest.singleType(value)
  
  def classType[T <: AnyRef](clazz: JClass[_]): ClassManifest[T] =
    new ClassTypeManifest[T](None, clazz, Nil)
  
  def classType[T <: AnyRef](clazz: JClass[_], arg1: OptManifest[_], args: OptManifest[_]*): ClassManifest[T] =
    new ClassTypeManifest[T](None, clazz, arg1 :: args.toList)
  
  def classType[T <: AnyRef](prefix: OptManifest[_], clazz: JClass[_], args: OptManifest[_]*): ClassManifest[T] =
    new ClassTypeManifest[T](Some(prefix), clazz, args.toList)
  def arrayType[T](arg: OptManifest[_]): ClassManifest[Array[T]] = arg match {
    case NoManifest => Object.asInstanceOf[ClassManifest[Array[T]]]
    case m: ClassManifest[_] => m.asInstanceOf[ClassManifest[T]].arrayManifest
  }
  
  
  def abstractType[T](prefix: OptManifest[_], name: String, clazz: JClass[_], args: OptManifest[_]*): ClassManifest[T] =
    new ClassManifest[T] {
      def erasure = clazz
      override val typeArguments = args.toList
      override def toString = prefix.toString+"#"+name+argString
    }
  
  def abstractType[T](prefix: OptManifest[_], name: String, upperbound: ClassManifest[_], args: OptManifest[_]*): ClassManifest[T] =
    new ClassManifest[T] {
      def erasure = upperbound.erasure
      override val typeArguments = args.toList
      override def toString = prefix.toString+"#"+name+argString
    }
}
private class ClassTypeManifest[T <: AnyRef](
  prefix: Option[OptManifest[_]], 
  val erasure: JClass[_], 
  override val typeArguments: List[OptManifest[_]]) extends ClassManifest[T]
{
  override def toString = 
    (if (prefix.isEmpty) "" else prefix.get.toString+"#") + 
    (if (erasure.isArray) "Array" else erasure.getName) +
    argString
}