package scalaz

trait Length[-L[_]] {
  def len[A](a: L[A]): Int
}

object Length {
  import Scalaz._

  implicit def IdentityLength: Length[Identity] = new Length[Identity] {
    def len[A](a: Identity[A]) = 1
  }

  implicit def NonEmptyListLength[A]: Length[NonEmptyList] = new Length[NonEmptyList] {
    def len[A](a: NonEmptyList[A]) = a.list.length
  }

  implicit def ZipStreamLength: Length[ZipStream] = new Length[ZipStream] {
    def len[A](a: ZipStream[A]) = a.value.length
  }

  implicit def Tuple1Length: Length[Tuple1] = new Length[Tuple1] {
    def len[A](a: Tuple1[A]) = 1
  }

  implicit def Function0Length: Length[Function0] = new Length[Function0] {
    def len[A](a: () => A) = 1
  }

  implicit def OptionLength: Length[Option] = new Length[Option] {
    def len[A](a: Option[A]) = a map (_ => 1) getOrElse 0
  }

  implicit def EitherLeftLength[X]: Length[({type λ[α]=Either.LeftProjection[α, X]})] = new Length[({type λ[α]=Either.LeftProjection[α, X]})] {
    def len[A](a: Either.LeftProjection[A, X]) = a.e match {
      case Right(_) => 0
      case Left(_) => 1
    }
  }

  implicit def EitherRightLength[X]: Length[({type λ[α]=Either.RightProjection[X, α]})] = new Length[({type λ[α]=Either.RightProjection[X, α]})] {
    def len[A](a: Either.RightProjection[X, A]) = a.e match {
      case Right(_) => 1
      case Left(_) => 0
    }
  }

  implicit def ValidationLength[X]: Length[({type λ[α]=Validation[X, α]})] = new Length[({type λ[α]=Validation[X, α]})] {
    def len[A](a: Validation[X, A]) = a match {
      case Success(_) => 1
      case Failure(_) => 0
    }
  }

  implicit def ValidationFailureLength[X]: Length[({type λ[α]=FailProjection[α, X]})] = new Length[({type λ[α]=FailProjection[α, X]})] {
    def len[A](a: FailProjection[A, X]) = a.validation match {
      case Success(_) => 0
      case Failure(_) => 1
    }
  }

  implicit def ArraySeqLength: Length[ArraySeq] = new Length[ArraySeq] {
    def len[A](a: ArraySeq[A]) = a.length
  }

  implicit def ArrayLength: Length[Array] = new Length[Array] {
    def len[A](a: Array[A]) = a.length
  }

  implicit def ImmutableArrayLength: Length[ImmutableArray] = new Length[ImmutableArray] {
    def len[A](a: ImmutableArray[A]) = a.length
  }

  import FingerTree.ftip2ft
  implicit def RopeLength: Length[Rope] = new Length[Rope] {
    def len[A](a: Rope[A]) = a.value.measure
  }

  implicit def IterableLength: Length[Iterable] = new Length[Iterable] {
    def len[A](a: Iterable[A]) = {
      var n = 0
      val i = a.iterator
      while(i.hasNext) {
        n = n + 1
        i.next
      }

      n
    }
  }

  implicit def JavaIterableLength: Length[java.lang.Iterable] = new Length[java.lang.Iterable] {
    def len[A](a: java.lang.Iterable[A]) = {
      var n = 0
      val i = a.iterator
      while(i.hasNext) {
        n = n + 1
        i.next
      }

      n
    }
  }

  import java.util.concurrent.Callable

  implicit def CallableLength: Length[Callable] = new Length[Callable] {
    def len[A](a: Callable[A]) = 1
  }
}