package gs.std.v0.core

/** Opaque type for collection sizes. Values are guaranteed to be 0 or greater.
  */
opaque type Size = Int

/** Opaque type for collection sizes. Values are guaranteed to be 0 or greater.
  */
object Size:

  sealed trait Invalid
  object Invalid extends Invalid

  /** The size 0.
    */
  final val Zero: Size = 0

  /** The size 1.
    */
  final val One: Size = 1

  /** Express the given integer as a size.
    *
    * Throws an `IllegalArgumentException` if a negative value is given as
    * input.
    *
    * @param value
    *   The input integer.
    * @return
    *   The [[Size]] instance.
    */
  def apply(value: Int): Size =
    if value >= 0 then value
    else throw new IllegalArgumentException("Size values must be 0 or greater.")

  def validate(value: Int): Either[Invalid, Size] =
    if value >= 0 then Right(value) else Left(Invalid)

  /** Express the size of any collection.
    *
    * @param iter
    *   The collection.
    * @return
    *   The size of the collection.
    */
  def of(iter: Iterable[?]): Size = iter.size

  /** Express the size of any array.
    *
    * @param arr
    *   The array.
    * @return
    *   The size of the array.
    */
  def of(arr: Array[?]): Size = arr.length

  given CanEqual[Size, Size] = CanEqual.derived

  given Ordering[Size] with

    /** @inheritDocs
      */
    def compare(
      x: Size,
      y: Size
    ): Int = x - y

  extension (size: Size)
    /** @return
      *   The underlying integer.
      */
    def unwrap(): Int = size

    /** @return
      *   The next value.
      */
    def next(): Size = size + 1

    /** @return
      *   The next value.
      */
    def increment(): Size = size + 1

    /** Add some size to this one.
      *
      * @param that
      *   The number to add.
      * @return
      *   The sum of the numbers.
      */
    def +(that: Size): Size = size + that

    /** Multiply this size by some other size.
      *
      * @param that
      *   The number to multiply by.
      * @return
      *   The product of the numbers.
      */
    def *(that: Size): Size = size * that

end Size
