package monocle.std

import monocle.function._
import monocle.{Iso, Optional}

import scalaz.NonEmptyList._
import scalaz.{NonEmptyList, OneAnd}

object nonemptylist extends NonEmptyListInstances

trait NonEmptyListInstances {

  def nelAndOneIso[A]: Iso[NonEmptyList[A], OneAnd[List,A]] =
    Iso((nel: NonEmptyList[A])    => OneAnd[List,A](nel.head, nel.tail))(
        (oneAnd: OneAnd[List, A]) => NonEmptyList.nel(oneAnd.head, oneAnd.tail))

  implicit def nelEach[A]: Each[NonEmptyList[A], A] =
    Each.traverseEach[NonEmptyList, A]

  implicit def nelIndex[A]: Index[NonEmptyList[A], Int, A] =
    new Index[NonEmptyList[A], Int, A] {
      def index(i: Int): Optional[NonEmptyList[A], A] = i match {
        case 0 => nelCons1.head.asOptional
        case _ => nelCons1.tail composeOptional list.listIndex.index(i-1)
      }
    }

  implicit def nelFilterIndex[A]: FilterIndex[NonEmptyList[A], Int, A] =
    FilterIndex.traverseFilterIndex[NonEmptyList, A](n =>
      n.zip(NonEmptyList(0, Stream.from(1).take(n.size): _*))
    )

  implicit def nelReverse[A]: Reverse[NonEmptyList[A], NonEmptyList[A]] =
    reverseFromReverseFunction[NonEmptyList[A]](_.reverse)


  implicit def nelCons1[A]: Cons1[NonEmptyList[A], A, List[A]] =
    new Cons1[NonEmptyList[A],A,List[A]]{
      def cons1: Iso[NonEmptyList[A], (A, List[A])] =
        Iso((nel: NonEmptyList[A]) => (nel.head,nel.tail)){case (h,t) => NonEmptyList.nel(h,t)}
    }

  implicit def nelSnoc1[A]:Snoc1[NonEmptyList[A], List[A], A] =
    new Snoc1[NonEmptyList[A],List[A], A]{
      def snoc1: Iso[NonEmptyList[A], (List[A], A)] =
        Iso((nel:NonEmptyList[A]) => nel.init -> nel.last){ case (i,l) => NonEmptyList.nel(l,i.reverse).reverse}
  }


}