package scalaz

import Scalaz._

/**
 * Data structures that can be folded.
 * Minimal complete definition: 'foldMap' or 'foldRight'.
 */
trait Foldable[F[_]] {

  /**Combine the elements of a structure using a monoid. */
  def fold[M: Monoid](t: F[M]): M = foldMap[M, M](t, x => x)

  /**Map each element of the structure to a monoid, and combine the results. */
  def foldMap[A, M: Monoid](t: F[A], f: A => M): M =
    foldRight[A, M](t, mzero[M], (x, y) => f(x) |+| y)

  /**Right-associative fold of a structure. */
  def foldRight[A, B](t: F[A], z: => B, f: (A, => B) => B): B =
    foldMap(t, (a: A) => (EndoTo(f.curried(a)(_: B)))) apply z

  /**Left-associative fold of a structure. */
  def foldLeft[A, B](t: F[A], z: B, f: (B, A) => B): B =
    foldMap(t, (a: A) => (EndoTo(f.flip.curried(a))) σ).value(z)

  /**A variant of 'foldr' that has no base case,
   *  and thus is undefined for empty structures. */
  def foldr1[A, B](t: F[A], f: (A, => A) => A): Option[A] = {
    def mf(x: A, o: => Option[A]) = o.map(f(x, _)) orElse Some(x)
    foldRight(t, None, mf)
  }

  /**A variant of 'foldl' that has no base case,
   *  and thus may only be applied to non-empty structures. */
  def foldl1[A, B](t: F[A], f: (A, A) => A): Option[A] = {
    def mf(o: Option[A], x: A) = o.map(f(x, _)) orElse Some(x)
    foldLeft(t, None, mf)
  }
}

trait FoldableLow {
 implicit def TraversableFoldable[CC[X] <: Traversable[X]]: Foldable[CC] = new Foldable[CC] {
    override def foldRight[A, B](t: CC[A], b: => B, f: (A, => B) => B): B = t.foldRight(b)(f(_, _))
   
    override def foldLeft[A, B](t: CC[A], b: B, f: (B, A) => B): B = t.foldLeft(b)(f(_, _))
  }
}

object Foldable extends FoldableLow {
  import Scalaz._

  implicit def IdentityFoldable: Foldable[Identity] = new Foldable[Identity] {
    override def foldLeft[A, B](t: Identity[A], b: B, f: (B, A) => B) = f(b, t.value)

    override def foldRight[A, B](t: Identity[A], b: => B, f: (A, => B) => B) = f(t.value, b)
  }

  def IterableSubtypeFoldable[I[X] <: Iterable[X]]: Foldable[I] = new Foldable[I] {
    override def foldRight[A, B](t: I[A], b: => B, f: (A, => B) => B): B = t.foldRight(b)(f(_, _))
  }

  implicit def NonEmptyListFoldable: Foldable[NonEmptyList] = new Foldable[NonEmptyList] {
    override def foldLeft[A, B](t: NonEmptyList[A], b: B, f: (B, A) => B) = t.list.foldLeft(b)(f)

    override def foldRight[A, B](t: NonEmptyList[A], b: => B, f: (A, => B) => B) = ListFoldable.foldRight(t.list, b, f)
  }

  implicit val ListFoldable: Foldable[List] = new Foldable[List] {
    override def foldLeft[A, B](t: List[A], z: B, f: (B, A) => B): B =
      t.foldLeft(z)(f)

    override def foldRight[A, B](t: List[A], z: => B, op: (A, => B) => B): B = {
      import scala.collection.mutable.ArrayStack
      val s = new ArrayStack[A]
      t.foreach(a => s += a)
      var r = z
      while (!s.isEmpty) {r = op(s.pop, r)}
      r
    }
  }

  implicit val IndSeqFoldable: Foldable[IndSeq] = new Foldable[IndSeq] {
    override def foldLeft[A, B](t: IndSeq[A], z: B, f: (B, A) => B) =
      t.toList.foldLeft(z)(f)

    override def foldRight[A, B](t:IndSeq[A], z: => B, op: (A, => B) => B) =
      t.toList.foldr(z)(op)
  }

  implicit def StreamFoldable[A]: Foldable[Stream] = new Foldable[Stream] {
    override def foldRight[A, B](t: Stream[A], b: => B, f: (A, => B) => B): B =
      if (t.isEmpty)
        b
      else
        f(t.head, foldRight(t.tail, b, f))

    override def foldLeft[A, B](t: Stream[A], b: B, f: (B, A) => B): B = t.foldLeft(b)(f(_, _))
  }

  implicit def StateFoldable: Foldable[({type λ[α]=State[Unit, α]})] = new Foldable[({type λ[α]=State[Unit, α]})] {
    override def foldLeft[A, B](t: State[Unit, A], b: B, f: (B, A) => B) = f(b, t(())._2)

    override def foldRight[A, B](t: State[Unit, A], b: => B, f: (A, => B) => B) = f(t(())._2, b)
  }

  implicit def Tuple1Foldable: Foldable[Tuple1] = new Foldable[Tuple1] {
    override def foldLeft[A, B](t: Tuple1[A], b: B, f: (B, A) => B) = f(b, t._1)

    override def foldRight[A, B](t: Tuple1[A], b: => B, f: (A, => B) => B) = f(t._1, b)
  }

  implicit def Function0Foldable: Foldable[Function0] = new Foldable[Function0] {
    override def foldLeft[A, B](t: () => A, b: B, f: (B, A) => B) = f(b, t.apply)

    override def foldRight[A, B](t: () => A, b: => B, f: (A, => B) => B) = f(t.apply, b)
  }

  implicit def OptionFoldable: Foldable[Option] = new Foldable[Option] {
    override def foldLeft[A, B](t: Option[A], b: B, f: (B, A) => B) = t match {
      case Some(a) => f(b, a)
      case None => b
    }

    override def foldRight[A, B](t: Option[A], b: => B, f: (A, => B) => B) = t match {
      case Some(a) => f(a, b)
      case None => b
    }
  }

  implicit def LazyOptionFoldable: Foldable[LazyOption] = new Foldable[LazyOption] {
    override def foldLeft[A, B](t: LazyOption[A], b: B, f: (B, A) => B) = t.fold(a => f(b, a), b)

    override def foldRight[A, B](t: LazyOption[A], b: => B, f: (A, => B) => B) = t.fold(a => f(a, b), b)
  }

  implicit def TreeFoldable: Foldable[Tree] = new Foldable[Tree] {
    override def foldMap[A, M: Monoid](t: Tree[A], f: A => M) = t.foldMap(f)
  }

  implicit def ZipperFoldable: Foldable[Zipper] = new Foldable[Zipper] {
    override def foldLeft[A, B](t: Zipper[A], b: B, f: (B, A) => B): B =
      t.lefts.foldRight((t.focus #:: t.rights).foldLeft(b)(f))(f.flip)

    override def foldRight[A, B](t: Zipper[A], b: => B, f: (A, => B) => B): B =
      t.lefts.foldLeft(Stream.cons(t.focus, t.rights).foldRight(b)(f(_, _)))((f.flip)(_, _))
  }

  implicit def ZipStreamFoldable: Foldable[ZipStream] = new Foldable[ZipStream] {
    override def foldLeft[A, B](t: ZipStream[A], b: B, f: (B, A) => B): B = implicitly[Foldable[Stream]].foldLeft(t.value, b, f)

    override def foldRight[A, B](t: ZipStream[A], b: => B, f: (A, => B) => B): B = implicitly[Foldable[Stream]].foldRight(t.value, b, f)
  }

  implicit def EitherLeftFoldable[X]: Foldable[({type λ[α]=Either.LeftProjection[α, X]})] = new Foldable[({type λ[α]=Either.LeftProjection[α, X]})] {
    override def foldLeft[A, B](e: Either.LeftProjection[A, X], b: B, f: (B, A) => B) = OptionFoldable.foldLeft(e.toOption, b, f)

    override def foldRight[A, B](e: Either.LeftProjection[A, X], b: => B, f: (A, => B) => B) = OptionFoldable.foldRight(e.toOption, b, f)
  }

  implicit def EitherRightFoldable[X]: Foldable[({type λ[α]=Either.RightProjection[X, α]})] = new Foldable[({type λ[α]=Either.RightProjection[X, α]})] {
    override def foldLeft[A, B](e: Either.RightProjection[X, A], b: B, f: (B, A) => B) = OptionFoldable.foldLeft(e.toOption, b, f)

    override def foldRight[A, B](e: Either.RightProjection[X, A], b: => B, f: (A, => B) => B) = OptionFoldable.foldRight(e.toOption, b, f)
  }

  implicit def ValidationFoldable[X] = new Foldable[({type λ[α]=Validation[X, α]})] {
    override def foldLeft[A, B](e: Validation[X, A], b: B, f: (B, A) => B) = e match {
      case Success(a) => f(b, a)
      case Failure(_) => b
    }

    override def foldRight[A, B](e: Validation[X, A], b: => B, f: (A, => B) => B) = e match {
      case Success(a) => f(a, b)
      case Failure(_) => b
    }
  }

  implicit def ValidationFailureFoldable[X] = new Foldable[({type λ[α]=FailProjection[α, X]})] {
    override def foldLeft[A, B](e: FailProjection[A, X], b: B, f: (B, A) => B) = e.validation match {
      case Success(_) => b
      case Failure(e) => f(b, e)
    }

    override def foldRight[A, B](e: FailProjection[A, X], b: => B, f: (A, => B) => B) = e.validation match {
      case Success(_) => b
      case Failure(e) => f(e, b)
    }
  }

  implicit def FingerFoldable[V] = new Foldable[({type λ[α]=Finger[V, α]})] {
    override def foldMap[A, M: Monoid](t: Finger[V, A], f: A => M): M = t foldMap f
  }

  implicit def NodeFoldable[V] = new Foldable[({type λ[α]=Node[V, α]})] {
    override def foldMap[A, M: Monoid](t: Node[V, A], f: A => M): M = t foldMap f
  }

  implicit def FingerTreeFoldable[V]:Foldable[({type λ[α]=FingerTree[V, α]})] =
    new Foldable[({type λ[α]=FingerTree[V, α]})] {
    override def foldMap[A, M: Monoid](t: FingerTree[V, A], f: A => M): M = t foldMap f
    override def foldRight[A, B](t: FingerTree[V, A], b: => B, f: (A, => B) => B): B =
      t.fold(v => b,
             (v, a) => f(a, b), 
             (v, pr, m, sf) =>
               FingerFoldable[V].foldRight(pr,
                                           foldRight[Node[V, A], B](m,
                                                     FingerFoldable[V].foldRight(sf, b, f),
                                                     (x, y) => NodeFoldable[V].foldRight(x, y, f)),
                                           f))
    override def foldLeft[A, B](t: FingerTree[V, A], b: B, f: (B, A) => B): B =
      t.fold(v => b,
             (v, a) => f(b, a), 
             (v, pr, m, sf) =>
               FingerFoldable[V].foldLeft(sf,
                                           foldLeft[Node[V, A], B](m,
                                                     FingerFoldable[V].foldLeft(pr, b, f),
                                                     (x, y) => NodeFoldable[V].foldLeft(y, x, f)),
                                           f))
  }

  implicit def JavaIterableFoldable: Foldable[java.lang.Iterable] = new Foldable[java.lang.Iterable] {
    override def foldLeft[A, B](t: java.lang.Iterable[A], b: B, f: (B, A) => B) = {
      var x = b
      val i = t.iterator

      while (i.hasNext) {
        val n = i.next
        x = f(x, n)
      }

      x
    }

    override def foldRight[A, B](t: java.lang.Iterable[A], b: => B, f: (A, => B) => B) = {
      val i = new Iterable[A] {
        override def iterator = new Iterator[A] {
          val k = t.iterator

          def hasNext = k.hasNext

          def next = k.next
        }
      }
      IterableSubtypeFoldable[Iterable].foldRight[A, B](i, b, f)
    }
  }

  import java.util.concurrent.Callable

  implicit def CallableFoldable: Foldable[Callable] = new Foldable[Callable] {
    override def foldLeft[A, B](t: Callable[A], b: B, f: (B, A) => B) = f(b, t.call)

    override def foldRight[A, B](t: Callable[A], b: => B, f: (A, => B) => B) = f(t.call, b)
  }
}