package scala.collection
import mutable.{ Buffer, ListBuffer, ArrayBuffer }
import annotation.unchecked.{ uncheckedVariance => uV }
trait TraversableOnce[+A] extends GenTraversableOnce[A] {
  self =>
  
  def foreach[U](f: A => U): Unit
  def isEmpty: Boolean
  def hasDefiniteSize: Boolean
    
  
  
  
  
  
  
  
  
  
  def seq: TraversableOnce[A]
  
  
  def forall(p: A => Boolean): Boolean
  def exists(p: A => Boolean): Boolean
  def find(p: A => Boolean): Option[A]
  def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Unit
  
  
  protected[this] def reversed = {
    var elems: List[A] = Nil
    self.seq foreach (elems ::= _)
    elems
  }
    
  def size: Int = {
    var result = 0	
    for (x <- self) result += 1
    result
  }
  
  def nonEmpty: Boolean = !isEmpty
  
  def count(p: A => Boolean): Int = {
    var cnt = 0
    for (x <- this)
      if (p(x)) cnt += 1
    cnt
  }
  
  
  def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = {
    for (x <- self.toIterator) { 
      if (pf isDefinedAt x)
        return Some(pf(x))
    }
    None
  }
  
  def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
  
  def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
  
  def foldLeft[B](z: B)(op: (B, A) => B): B = {
    var result = z
    this.seq foreach (x => result = op(result, x))
    result
  }
  
  def foldRight[B](z: B)(op: (A, B) => B): B =
    reversed.foldLeft(z)((x, y) => op(y, x))
  
  def reduceLeft[B >: A](op: (B, A) => B): B = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.reduceLeft")
    
    var first = true
    var acc: B = 0.asInstanceOf[B]
    for (x <- self) {
      if (first) {
        acc = x
        first = false
      }
      else acc = op(acc, x)
    }
    acc
  }
  
  def reduceRight[B >: A](op: (A, B) => B): B = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.reduceRight")
    
    reversed.reduceLeft[B]((x, y) => op(y, x))
  }
  
  def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] =
    if (isEmpty) None else Some(reduceLeft(op))
  def reduceRightOption[B >: A](op: (A, B) => B): Option[B] =
    if (isEmpty) None else Some(reduceRight(op))
  
  def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
  
  def reduceOption[A1 >: A](op: (A1, A1) => A1): Option[A1] = reduceLeftOption(op)
  
  def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
  
  def aggregate[B](z: B)(seqop: (B, A) => B, combop: (B, B) => B): B = foldLeft(z)(seqop)
  
  def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus)
  
  def product[B >: A](implicit num: Numeric[B]): B = foldLeft(num.one)(num.times)
  def min[B >: A](implicit cmp: Ordering[B]): A = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.min")
    
    reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
  }
  def max[B >: A](implicit cmp: Ordering[B]): A = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.max")
    
    reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
  }
  def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.maxBy")
    
    reduceLeft((x, y) => if (cmp.gteq(f(x), f(y))) x else y)
  }
  def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = {
    if (isEmpty)
      throw new UnsupportedOperationException("empty.minBy")
    
    reduceLeft((x, y) => if (cmp.lteq(f(x), f(y))) x else y)
  }
  
  def copyToBuffer[B >: A](dest: Buffer[B]): Unit = dest ++= seq
  
  def copyToArray[B >: A](xs: Array[B], start: Int): Unit = 
    copyToArray(xs, start, xs.length - start)
  def copyToArray[B >: A](xs: Array[B]): Unit =
    copyToArray(xs, 0, xs.length)
  
  def toArray[B >: A : ClassManifest]: Array[B] = {
    if (isTraversableAgain) {
      val result = new Array[B](size)
      copyToArray(result, 0)
      result
    }
    else toBuffer.toArray
  }
  
  def toTraversable: Traversable[A]
  
  def toList: List[A] = new ListBuffer[A] ++= seq toList
  def toIterable: Iterable[A] = toStream
 
  def toSeq: Seq[A] = toStream
 
  def toIndexedSeq[B >: A]: immutable.IndexedSeq[B] = immutable.IndexedSeq() ++ seq
  
  def toBuffer[B >: A]: mutable.Buffer[B] = new ArrayBuffer[B] ++= seq
  
  def toSet[B >: A]: immutable.Set[B] = immutable.Set() ++ seq
  def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
    val b = immutable.Map.newBuilder[T, U]
    for (x <- self)
      b += x
      
    b.result
  }
  
  def mkString(start: String, sep: String, end: String): String =
    addString(new StringBuilder(), start, sep, end).toString
  def mkString(sep: String): String = mkString("", sep, "")
  def mkString: String = mkString("")
  
  def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = {
    var first = true
    b append start    
    for (x <- self) {
      if (first) {
        b append x
        first = false
      }
      else {
        b append sep
        b append x
      }
    }
    b append end
    b
  }
  
  def addString(b: StringBuilder, sep: String): StringBuilder = addString(b, "", sep, "")
  
  def addString(b: StringBuilder): StringBuilder = addString(b, "")
}
object TraversableOnce {
  implicit def traversableOnceCanBuildFrom[T] = new OnceCanBuildFrom[T]  
  implicit def wrapTraversableOnce[A](trav: TraversableOnce[A]) = new MonadOps(trav)
  implicit def flattenTraversableOnce[A, CC[_]](travs: TraversableOnce[CC[A]])(implicit ev: CC[A] => TraversableOnce[A]) =
    new FlattenOps[A](travs map ev)
  
  
  class OnceCanBuildFrom[A] extends generic.CanBuildFrom[TraversableOnce[A], A, TraversableOnce[A]] {
    def newIterator = new ArrayBuffer[A] mapResult (_.iterator)
    
    
    def apply(from: TraversableOnce[A]) = newIterator
    
    
    def apply() = newIterator
  }
  
  class FlattenOps[A](travs: TraversableOnce[TraversableOnce[A]]) {
    def flatten: Iterator[A] = new Iterator[A] {
      val its = travs.toIterator
      private var it: Iterator[A] = Iterator.empty
      def hasNext: Boolean = it.hasNext || its.hasNext && { it = its.next.toIterator; hasNext }
      def next(): A = if (hasNext) it.next() else Iterator.empty.next()
    }
  }
  class MonadOps[+A](trav: TraversableOnce[A]) {    
    def map[B](f: A => B): TraversableOnce[B] = trav.toIterator map f
    def flatMap[B](f: A => GenTraversableOnce[B]): TraversableOnce[B] = trav.toIterator flatMap f
    def withFilter(p: A => Boolean) = trav.toIterator filter p
    def filter(p: A => Boolean): TraversableOnce[A] = withFilter(p)
  }
}