package scalaz

import collection.generic.GenericTraversableTemplate
import collection.TraversableLike

trait Pure[P[_]] {
  def pure[A](a: => A): P[A]
}

object Pure {
  import Scalaz._

  implicit def IdentityPure: Pure[Identity] = new Pure[Identity] {
    def pure[A](a: => A) = a
  }

  implicit def ConstPure[B: Monoid] = new Pure[({type λ[α]=Const[B, α]})] {
    def pure[A](a: => A) = Const[B, A]()
  }

  implicit def NonEmptyListPure: Pure[NonEmptyList] = new Pure[NonEmptyList] {
    def pure[A](a: => A) = a.wrapNel
  }

  implicit def IndSeqPure: Pure[IndSeq] = new Pure[IndSeq] {
    def pure[A](a: => A) = IndSeq.apply(a)
  }

  implicit def TraversablePure[CC[X] <: TraversableLike[X, CC[X]] : CanBuildAnySelf]: Pure[CC] = new Pure[CC] {
    def pure[A](a: => A) = {
      val builder = implicitly[CanBuildAnySelf[CC]].apply[Nothing, A]
      builder += a
      builder.result
    }
  }

  implicit def StatePure[S]: Pure[({type λ[α]=State[S, α]})] = new Pure[({type λ[α]=State[S, α]})] {
    def pure[A](a: => A) = a.state[S]
  }

  implicit def StateTPure[S, M[_]: Pure]: Pure[({type λ[α]=StateT[M, S, α]})] =
    new Pure[({type λ[α]=StateT[M, S, α]})] {
      def pure[A](a: => A) = stateT(s => ((s, a)).pure[M])
    }

  implicit def Tuple1Pure = new Pure[Tuple1] {
    def pure[A](a: => A) = Tuple1(a)
  }

  implicit def Tuple2Pure[R: Zero]: Pure[({type λ[α]=(R, α)})] = new Pure[({type λ[α]=(R, α)})] {
    def pure[A](a: => A) = (, a)
  }

  implicit def Tuple3Pure[R: Zero, S: Zero]: Pure[({type λ[α]=(R, S, α)})] = new Pure[({type λ[α]=(R, S, α)})] {
    def pure[A](a: => A) = ([R], [S], a)
  }

  implicit def Tuple4Pure[R: Zero, S: Zero, T: Zero]: Pure[({type λ[α]=(R, S, T, α)})] = new Pure[({type λ[α]=(R, S, T, α)})] {
    def pure[A](a: => A) = ([R], [S], [T], a)
  }

  implicit def Tuple5Pure[R: Zero, S: Zero, T: Zero, U: Zero]: Pure[({type λ[α]=(R, S, T, U, α)})] = new Pure[({type λ[α]=(R, S, T, U, α)})] {
    def pure[A](a: => A) = ([R], [S], [T], [U], a)
  }

  implicit def Tuple6Pure[R: Zero, S: Zero, T: Zero, U: Zero, V: Zero]: Pure[({type λ[α]=(R, S, T, U, V, α)})] = new Pure[({type λ[α]=(R, S, T, U, V, α)})] {
    def pure[A](a: => A) = ([R], [S], [T], [U], [V], a)
  }

  implicit def Tuple7Pure[R: Zero, S: Zero, T: Zero, U: Zero, V: Zero, W: Zero]: Pure[({type λ[α]=(R, S, T, U, V, W, α)})] = new Pure[({type λ[α]=(R, S, T, U, V, W, α)})] {
    def pure[A](a: => A) = ([R], [S], [T], [U], [V], [W], a)
  }

  implicit def Function0Pure: Pure[Function0] = new Pure[Function0] {
    def pure[A](a: => A) = new Function0[A] {
      def apply = a
    }
  }

  implicit def Function1Pure[R]: Pure[({type λ[α]=(R) => α})] = new Pure[({type λ[α]=(R) => α})] {
    def pure[A](a: => A) = (_: R) => a
  }

  implicit def Function2Pure[R, S]: Pure[({type λ[α]=(R, S) => α})] = new Pure[({type λ[α]=(R, S) => α})] {
    def pure[A](a: => A) = (_: R, _: S) => a
  }

  implicit def Function3Pure[R, S, T]: Pure[({type λ[α]=(R, S, T) => α})] = new Pure[({type λ[α]=(R, S, T) => α})] {
    def pure[A](a: => A) = (_: R, _: S, _: T) => a
  }

  implicit def Function4Pure[R, S, T, U]: Pure[({type λ[α]=(R, S, T, U) => α})] = new Pure[({type λ[α]=(R, S, T, U) => α})] {
    def pure[A](a: => A) = (_: R, _: S, _: T, _: U) => a
  }

  implicit def Function5Pure[R, S, T, U, V]: Pure[({type λ[α]=(R, S, T, U, V) => α})] = new Pure[({type λ[α]=(R, S, T, U, V) => α})] {
    def pure[A](a: => A) = (_: R, _: S, _: T, _: U, _: V) => a
  }

  implicit def Function6Pure[R, S, T, U, V, W]: Pure[({type λ[α]=(R, S, T, U, V, W) => α})] = new Pure[({type λ[α]=(R, S, T, U, V, W) => α})] {
    def pure[A](a: => A) = (_: R, _: S, _: T, _: U, _: V, _: W) => a
  }

  implicit def OptionPure: Pure[Option] = new Pure[Option] {
    def pure[A](a: => A) = Some(a)
  }

  implicit def FirstOptionPure: Pure[FirstOption] = new Pure[FirstOption] {
    def pure[A](a: => A) = Some(a).fst
  }
  
  implicit def LastOptionPure: Pure[LastOption] = new Pure[LastOption] {
    def pure[A](a: => A) = Some(a).lst
  }
  
  implicit def LazyOptionPure: Pure[LazyOption] = new Pure[LazyOption] {
    def pure[A](a: => A) = LazyOption.some(a)
  }

  implicit def FirstLazyOptionPure: Pure[FirstLazyOption] = new Pure[FirstLazyOption] {
    def pure[A](a: => A) = LazyOption.some(a).fst
  }
  
  implicit def LastLazyOptionPure: Pure[LastLazyOption] = new Pure[LastLazyOption] {
    def pure[A](a: => A) = LazyOption.some(a).lst
  }

  implicit def EitherLeftPure[X]: Pure[({type λ[α]=Either.LeftProjection[α, X]})] = new Pure[({type λ[α]=Either.LeftProjection[α, X]})] {
    def pure[A](a: => A) = Left(a).left
  }

  implicit def EitherRightPure[X]: Pure[({type λ[α]=Either.RightProjection[X, α]})] = new Pure[({type λ[α]=Either.RightProjection[X, α]})] {
    def pure[A](a: => A) = Right(a).right
  }

  implicit def EitherPure[X]: Pure[({type λ[α]=Either[X, α]})] = new Pure[({type λ[α]=Either[X, α]})] {
    def pure[A](a: => A) = Right(a)
  }

  implicit def ResponderPure: Pure[Responder] = new Pure[Responder] {
    def pure[A](a: => A) = new Responder[A] {
      def respond(k: A => Unit) = k(a)
    }
  }

  import java.util.concurrent.Callable

  implicit def CallablePure: Pure[Callable] = new Pure[Callable] {
    def pure[A](a: => A) = new Callable[A] {
      def call = a
    }
  }

  import java.util.Map.Entry
  import java.util.AbstractMap.SimpleImmutableEntry

  implicit def MapEntryPure[X: Zero]: Pure[({type λ[α]=Entry[X, α]})] = new Pure[({type λ[α]=Entry[X, α]})] {
    def pure[A](a: => A) = new SimpleImmutableEntry(, a)
  }

  implicit def ValidationPure[X]: Pure[({type λ[α]=Validation[X, α]})] = new Pure[({type λ[α]=Validation[X, α]})] {
    def pure[A](a: => A) = a.success
  }

  implicit def ValidationFailurePure[X]: Pure[({type λ[α]=FailProjection[α, X]})] = new Pure[({type λ[α]=FailProjection[α, X]})] {
    def pure[A](a: => A) = Failure(a).fail
  }
  
  implicit def IterVPure[E] = new Pure[({type λ[α]=IterV[E, α]})] {
    import IterV._
    def pure[A](a: => A) = Done(a, Empty[E])
  }

  implicit def ZipperPure = new Pure[Zipper] {
    def pure[A](a: => A) = a.zipper
  }

  implicit def ZipStreamPure: Pure[ZipStream] = new Pure[ZipStream] {
    def pure[A](a: => A) = zip(Stream(a))
  }

  implicit def EndoPure: Pure[Endo] = new Pure[Endo] {
    def pure[A](a: => A) = constantEndo(a)
  }

  implicit def TreePure: Pure[Tree] = new Pure[Tree] {
    def pure[A](a: => A) = leaf(a)
  }

  implicit def TreeLocPure: Pure[TreeLoc] = new Pure[TreeLoc] {
    def pure[A](a: => A) = TreePure.pure(a).loc
  }

  import concurrent._
  implicit def PromisePure(implicit s: Strategy): Pure[Promise] = new Pure[Promise] {
    def pure[A](a: => A) = Promise(a)
  }

  import java.util._
  import java.util.concurrent._

  implicit def JavaArrayListPure: Pure[ArrayList] = new Pure[ArrayList] {
    def pure[A](a: => A) = {
      val k = new ArrayList[A]
      k add a
      k
    }
  }

  implicit def JavaHashSetPure: Pure[HashSet] = new Pure[HashSet] {
    def pure[A](a: => A) = {
      val k = new HashSet[A]
      k add a
      k
    }
  }

  implicit def JavaLinkedHashSetPure: Pure[LinkedHashSet] = new Pure[LinkedHashSet] {
    def pure[A](a: => A) = {
      val k = new LinkedHashSet[A]
      k add a
      k
    }
  }

  implicit def JavaLinkedListPure: Pure[LinkedList] = new Pure[LinkedList] {
    def pure[A](a: => A) = {
      val k = new LinkedList[A]
      k add a
      k
    }
  }

  implicit def JavaPriorityQueuePure: Pure[PriorityQueue] = new Pure[PriorityQueue] {
    def pure[A](a: => A) = {
      val k = new PriorityQueue[A]
      k add a
      k
    }
  }

  implicit def JavaStackPure: Pure[Stack] = new Pure[Stack] {
    def pure[A](a: => A) = {
      val k = new Stack[A]
      k add a
      k
    }
  }

  implicit def JavaTreeSetPure: Pure[TreeSet] = new Pure[TreeSet] {
    def pure[A](a: => A) = {
      val k = new TreeSet[A]
      k add a
      k
    }
  }

  implicit def JavaVectorPure: Pure[Vector] = new Pure[Vector] {
    def pure[A](a: => A) = {
      val k = new Vector[A]
      k add a
      k
    }
  }

  implicit def JavaArrayBlockingQueuePure: Pure[ArrayBlockingQueue] = new Pure[ArrayBlockingQueue] {
    def pure[A](a: => A) = {
      val k = new ArrayBlockingQueue[A](0)
      k add a
      k
    }
  }

  implicit def JavaConcurrentLinkedQueuePure: Pure[ConcurrentLinkedQueue] = new Pure[ConcurrentLinkedQueue] {
    def pure[A](a: => A) = {
      val k = new ConcurrentLinkedQueue[A]
      k add a
      k
    }
  }

  implicit def JavaCopyOnWriteArrayListPure: Pure[CopyOnWriteArrayList] = new Pure[CopyOnWriteArrayList] {
    def pure[A](a: => A) = {
      val k = new CopyOnWriteArrayList[A]
      k add a
      k
    }
  }

  implicit def JavaCopyOnWriteArraySetPure: Pure[CopyOnWriteArraySet] = new Pure[CopyOnWriteArraySet] {
    def pure[A](a: => A) = {
      val k = new CopyOnWriteArraySet[A]
      k add a
      k
    }
  }

  implicit def JavaLinkedBlockingQueuePure: Pure[LinkedBlockingQueue] = new Pure[LinkedBlockingQueue] {
    def pure[A](a: => A) = {
      val k = new LinkedBlockingQueue[A]
      k add a
      k
    }
  }

  implicit def JavaSynchronousQueuePure: Pure[SynchronousQueue] = new Pure[SynchronousQueue] {
    def pure[A](a: => A) = {
      val k = new SynchronousQueue[A]
      k add a
      k
    }
  }
}