package scalaz

trait Order[-A] extends Equal[A] {
  def order(a1: A, a2: A): Ordering

  final def equal(a1: A, a2: A): Boolean = order(a1, a2) == EQ
}

trait Orders {
  import Scalaz._

  def order[A](f: (A, A) => Ordering): Order[A] = new Order[A] {
    def order(a1: A, a2: A) = f(a1, a2)
  }

  def orderBy[A, B: Order](f: A => B): Order[A] = implicitly[Order[B]]  f

}

trait OrderLow {
  import Scalaz._

  implicit def ScalaOrderingOrder[T: scala.Ordering]: Order[T] = order {(t1, t2) =>
    implicitly[scala.Ordering[T]].compare(t1, t2) match {
      case -1 => LT
      case 0 => EQ
      case 1 => GT
    }
  }
}

object Order {
  import Scalaz._
  import java.math.BigInteger

  implicit def DigitOrder: Order[Digit] = orderBy(_.toInt)

  implicit def OrderingOrder: Order[Ordering] = order {
    case (a, EQ) => a
    case (EQ, LT) => GT
    case (EQ, GT) => LT
    case (LT, LT) => EQ
    case (LT, _) => LT
    case (GT, GT) => EQ
    case (GT, _) => GT
  }

  implicit def OrderOrdering[A: Order]: scala.Ordering[A] = new scala.Ordering[A] {
    def compare(a1: A, a2: A) = (a1 ?|? a2) match {
      case EQ => 0
      case LT => -1
      case GT => 1
    }
  }

  implicit def UnitOrder: Order[Unit] = order((_, _) => EQ)

  implicit def StringOrder: Order[String] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def SymbolOrder: Order[Symbol] = orderBy(_.name)

  implicit def IntOrder: Order[Int] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def IntMultiplicationOrder: Order[IntMultiplication] = orderBy(_.value)

  implicit def BooleanOrder: Order[Boolean] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def BooleanConjunctionOrder: Order[BooleanConjunction] = orderBy(_.value)

  implicit def CharOrder: Order[Char] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def CharMultiplicationOrder: Order[CharMultiplication] = orderBy(_.value)

  implicit def ByteOrder: Order[Byte] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def ByteMultiplicationOrder: Order[ByteMultiplication] = orderBy(_.value)

  implicit def LongOrder: Order[Long] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def LongMultiplicationOrder: Order[LongMultiplication] = orderBy(_.value)

  implicit def ShortOrder: Order[Short] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def ShortMultiplicationOrder: Order[ShortMultiplication] = orderBy(_.value)

  implicit def FloatOrder: Order[Float] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def DoubleOrder: Order[Double] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def BigIntegerOrder: Order[BigInteger] = order(_ compareTo _ ordering)

  implicit def BigIntegerMultiplicationOrder: Order[BigIntegerMultiplication] = orderBy(_.value)

  implicit def BigIntOrder: Order[BigInt] = order((a1, a2) => if (a1 > a2) GT else if (a1 < a2) LT else EQ)

  implicit def BigIntMultplicationOrder: Order[BigIntMultiplication] = orderBy(_.value)

  implicit def NonEmptyListOrder[A: Order]: Order[NonEmptyList[A]] = orderBy(_.list)

  implicit def ZipStreamOrder[A: Order]: Order[ZipStream[A]] = orderBy(_.value)

  implicit def Tuple1Order[A: Order]: Order[Tuple1[A]] = order(_._1 ?|? _._1)

  implicit def IndSeqOrder[A: Order]: Order[IndSeq[A]] = orderBy(_.toList)

  import Foldable._

  implicit def Tuple2Order[A: Order, B: Order]: Order[(A, B)] = order {
    case ((a1, b1), (a2, b2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2))
  }

  implicit def Tuple3Order[A: Order, B: Order, C: Order]: Order[(A, B, C)] = order {
    case ((a1, b1, c1), (a2, b2, c2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2, c1 ?|? c2))
  }

  implicit def Tuple4Order[A: Order, B: Order, C: Order, D: Order]: Order[(A, B, C, D)] = order {
    case ((a1, b1, c1, d1), (a2, b2, c2, d2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2, c1 ?|? c2, d1 ?|? d2))
  }

  implicit def Tuple5Order[A: Order, B: Order, C: Order, D: Order, E: Order]: Order[(A, B, C, D, E)] = order {
    case ((a1, b1, c1, d1, e1), (a2, b2, c2, d2, e2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2, c1 ?|? c2, d1 ?|? d2, e1 ?|? e2))
  }

  implicit def Tuple6Order[A: Order, B: Order, C: Order, D: Order, E: Order, F: Order]: Order[(A, B, C, D, E, F)] = order {
    case ((a1, b1, c1, d1, e1, f1), (a2, b2, c2, d2, e2, f2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2, c1 ?|? c2, d1 ?|? d2, e1 ?|? e2, f1 ?|? f2))
  }

  implicit def Tuple7Order[A: Order, B: Order, C: Order, D: Order, E: Order, F: Order, G: Order]: Order[(A, B, C, D, E, F, G)] = order {
    case ((a1, b1, c1, d1, e1, f1, g1), (a2, b2, c2, d2, e2, f2, g2)) => ListFoldable.fold(List(a1 ?|? a2, b1 ?|? b2, c1 ?|? c2, d1 ?|? d2, e1 ?|? e2, f1 ?|? f2, g1 ?|? g2))
  }

  implicit def Function0Order[A: Order]: Order[() => A] = order(_.apply ?|? _.apply)

  implicit def IterableOrder[A: Order]: Order[Iterable[A]] = order((a1, a2) => {
    val i1 = a1.iterator
    val i2 = a2.iterator
    var b = true
    var r: Ordering = EQ

    while (i1.hasNext && i2.hasNext && b) {
      val a1 = i1.next
      val a2 = i2.next

      val o = a1 ?|? a2
      if (o != EQ) {
        r = o
        b = false
      }
    }

    if (i1.hasNext)
      if (i2.hasNext)
        r
      else
        GT
    else
      LT
  })

  implicit def OptionOrder[A: Order]: Order[Option[A]] = order {
    case (Some(x), Some(y)) => x ?|? y
    case (Some(_), None) => GT
    case (None, Some(_)) => LT
    case (None, None) => EQ
  }

  implicit def EitherOrder[A: Order, B: Order]: Order[Either[A, B]] = order {
    case (Left(x), Left(y)) => x ?|? y
    case (Right(x), Right(y)) => x ?|? y
    case (Left(_), Right(_)) => LT
    case (Right(_), Left(_)) => GT
  }

  implicit def EitherLeftOrder[A: Order, B]: Order[Either.LeftProjection[A, B]] = order((a1, a2) =>
    (a1.toOption, a2.toOption) match {
      case (Some(a1), Some(a2)) => a1 ?|? a2
      case (Some(_), None) => GT
      case (None, Some(_)) => LT
      case (None, None) => EQ
    })

  implicit def EitherRightOrder[A, B: Order]: Order[Either.RightProjection[A, B]] = order((b1, b2) =>
    (b1.toOption, b2.toOption) match {
      case (Some(b1), Some(b2)) => b1 ?|? b2
      case (Some(_), None) => GT
      case (None, Some(_)) => LT
      case (None, None) => EQ
    })

  implicit def ValidationOrder[E: Order, A: Order]: Order[Validation[E, A]] = orderBy(_.either)

  implicit def JavaIterableOrder[A: Order]: Order[java.lang.Iterable[A]] = {
    import collection.JavaConversions._
    IterableOrder[A]  (x => x)
  }
}