package gs.std.v0.core

/** Opaque type for some number of bytes (>= 0).
  */
opaque type ByteCount = Long

/** Opaque type for some number of bytes (>= 0).
  */
object ByteCount:

  /** 0 bytes.
    */
  final val Zero: ByteCount = 0

  /** 1,000 bytes.
    */
  final val OneKilobyte: ByteCount = 1000

  /** 1,000,000 bytes.
    */
  final val OneMegabyte: ByteCount = 1000000

  /** 1,000,000,000 bytes.
    */
  final val OneGigabyte: ByteCount = 1000000000

  /** Provide integer representations for common sizes in terms of number of
    * bytes. These values are useful for things like array sizing that depend on
    * the `Int` type.
    */
  object IntSizes:

    final val Zero: Int        = 0
    final val OneKilobyte: Int = 1000
    final val OneMegabyte: Int = 1000000
    final val OneGigabyte: Int = 1000000000

  end IntSizes

  /** Express the given number as a byte count. All values are normalized to the
    * absolute value -- negative values are coerced to positive.
    *
    * @param value
    *   The input integer.
    * @return
    *   The [[ByteCount]] instance.
    */
  def apply(value: Long): ByteCount = Math.abs(value)

  /** 1 kilobyte = 1,000 bytes
    *
    * @param kb
    *   The number of kilobytes.
    * @return
    *   The number of bytes.
    */
  def fromKilobytes(kb: Long): ByteCount =
    Math.abs(kb) * 1000L

  /** 1 megabyte = 1,000,000 bytes
    *
    * @param mb
    *   The number of megabytes.
    * @return
    *   The number of bytes.
    */
  def fromMegabytes(mb: Long): ByteCount =
    Math.abs(mb) * 1000000L

  /** 1 gigabyte = 1,000,000,000 bytes
    *
    * @param mb
    *   The number of gigabytes.
    * @return
    *   The number of bytes.
    */
  def fromGigabytes(gb: Long): ByteCount =
    Math.abs(gb) * 1000000000

  given CanEqual[ByteCount, ByteCount] = CanEqual.derived

  given Ordering[ByteCount] with

    /** @inheritDocs
      */
    def compare(
      x: ByteCount,
      y: ByteCount
    ): Int =
      if x > y then 1 else if x == y then 0 else -1

  extension (byteCount: ByteCount)
    /** @return
      *   The underlying `Long`.
      */
    def unwrap(): Long = byteCount

    /** @return
      *   The underlying `Long`.
      */
    def toLong(): Long = byteCount

    /** 1 kilobyte = 1,000 bytes
      *
      * @return
      *   The number of kilobytes represented by this count.
      */
    def toKilobytes(): Double = byteCount / 1000.0

    /** 1 megabyte = 1,000,000 bytes.
      *
      * @return
      *   The number of megabytes represented by this count.
      */
    def toMegabytes(): Double = byteCount / 1000000.0

    /** 1 gigabyte = 1,000,000,000 bytes.
      *
      * @return
      *   The number of gigabytes represented by this count.
      */
    def toGigabytes(): Double = byteCount / 1000000000.0

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

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

    /** Check if this value is the same as some number.
      *
      * @param value
      *   The number to compare against.
      * @return
      *   True if the values are equal, false otherwise.
      */
    def equal(value: Int): Boolean = byteCount == value.toLong

end ByteCount
