// SPDX-License-Identifier: Apache-2.0

package chisel3

import chisel3.internal.firrtl.{IntervalRange, KnownWidth, ULit, UnknownWidth, Width}
import firrtl.Utils
import firrtl.constraint.IsKnown
import firrtl.ir.{Closed, IntWidth, Open}

// This is currently a factory because both Bits and UInt inherit it.
trait UIntFactory {

  /** Create a UInt type with inferred width. */
  def apply(): UInt = apply(Width())

  /** Create a UInt port with specified width. */
  def apply(width: Width): UInt = new UInt(width)

  /** Create a UInt literal with specified width. */
  protected[chisel3] def Lit(value: BigInt, width: Width): UInt = {
    val lit = ULit(value, width)
    val result = new UInt(lit.width)
    // Bind result to being an Literal
    lit.bindLitArg(result)
  }

  /** Create a UInt with the specified range, validate that range is effectively > 0
    */
  def apply(range: IntervalRange): UInt = {
    // Check is only done against lower bound because range will already insist that range high >= low
    range.lowerBound match {
      case Closed(bound) if bound < 0 =>
        throw new ChiselException(s"Attempt to create UInt with closed lower bound of $bound, must be > 0")
      case Open(bound) if bound < -1 =>
        throw new ChiselException(s"Attempt to create UInt with open lower bound of $bound, must be > -1")
      case _ =>
    }

    // because this is a UInt we don't have to take into account the lower bound
    val newWidth = if (range.upperBound.isInstanceOf[IsKnown]) {
      KnownWidth(Utils.getUIntWidth(range.maxAdjusted.get).max(1)) // max(1) handles range"[0,0]"
    } else {
      UnknownWidth()
    }

    apply(newWidth)
  }
}
