// SPDX-License-Identifier: Apache-2.0

package firrtlTests

import firrtl._
import firrtl.CompilerUtils.mergeTransforms
import firrtl.testutils._

class CompilerUtilsSpec extends FirrtlFlatSpec {

  def genTransform(_inputForm: CircuitForm, _outputForm: CircuitForm) = new Transform {
    def inputForm = _inputForm
    def outputForm = _outputForm
    def execute(state: CircuitState): CircuitState = state
  }

  // Core lowering transforms
  val chirrtlToHigh = genTransform(ChirrtlForm, HighForm)
  val highToMid = genTransform(HighForm, MidForm)
  val midToLow = genTransform(MidForm, LowForm)
  val chirrtlToLowList = List(chirrtlToHigh, highToMid, midToLow)

  // Custom transforms
  val chirrtlToChirrtl = genTransform(ChirrtlForm, ChirrtlForm)
  val highToHigh = genTransform(HighForm, HighForm)
  val midToMid = genTransform(MidForm, MidForm)
  val lowToLow = genTransform(LowForm, LowForm)

  val lowToHigh = genTransform(LowForm, HighForm)

  val lowToLowTwo = genTransform(LowForm, LowForm)

  behavior.of("mergeTransforms")

  it should "do nothing if there are no custom transforms" in {
    mergeTransforms(chirrtlToLowList, List.empty) should be(chirrtlToLowList)
  }

  it should "insert transforms at the correct place" in {
    mergeTransforms(chirrtlToLowList, List(chirrtlToChirrtl)) should be
    (chirrtlToChirrtl +: chirrtlToLowList)
    mergeTransforms(chirrtlToLowList, List(highToHigh)) should be
    (List(chirrtlToHigh, highToHigh, highToMid, midToLow))
    mergeTransforms(chirrtlToLowList, List(midToMid)) should be
    (List(chirrtlToHigh, highToMid, midToMid, midToLow))
    mergeTransforms(chirrtlToLowList, List(lowToLow)) should be
    (chirrtlToLowList :+ lowToLow)
  }

  it should "insert transforms at the last legal location" in {
    lowToLow should not be (lowToLowTwo) // sanity check
    mergeTransforms(chirrtlToLowList :+ lowToLow, List(lowToLowTwo)).last should be(lowToLowTwo)
  }

  it should "insert multiple transforms correctly" in {
    mergeTransforms(chirrtlToLowList, List(highToHigh, lowToLow)) should be
    (List(chirrtlToHigh, highToHigh, highToMid, midToLow, lowToLow))
  }

  it should "handle transforms that raise the form" in {
    mergeTransforms(chirrtlToLowList, List(lowToHigh)) match {
      case chirrtlToHigh :: highToMid :: midToLow :: lowToHigh :: remainder =>
        // Remainder will be the actual Firrtl lowering transforms
        remainder.head.inputForm should be(HighForm)
        remainder.last.outputForm should be(LowForm)
      case _ => fail()
    }
  }

  // Order is not always maintained, see note on function Scaladoc
  it should "maintain order of custom tranforms" in {
    mergeTransforms(chirrtlToLowList, List(lowToLow, lowToLowTwo)) should be
    (chirrtlToLowList ++ List(lowToLow, lowToLowTwo))
  }

}
