package projet

import scala.io.Source
import scala.collection.mutable.ListBuffer
import org.scalatest.freespec.AnyFreeSpec
import chisel3.simulator.scalatest.ChiselSim

object ZzTopCommon extends AnyFreeSpec with ChiselSim {
    def expectedValuesFromAsm(path: String): Seq[BigInt] = {
        Source
            .fromFile(path)
            .getLines()
            .filter(_.trim().startsWith("# expected:"))
            .map(
                _.trim().stripPrefix("# expected:")
                    .split(",")
                    .map(_.trim)
                    .filter(_.nonEmpty)
                    .map(str => {
                        if (str.charAt(0) == '-') {
                            BigInt("FFFFFFFFFFFFFFFF", 16) -
                                BigInt(str.substring(1), 16) + 1
                        } else {
                            BigInt(str, 16)
                        }
                    })
                    .toSeq
            )
            .fold(Seq.empty)((acc, line) => {
                acc.concat(line)
            })
    }

    def failTrace(msg: String = "Erreur", trace: ListBuffer[String]): Unit = {
        val prefix = s"🛑 $msg\n"
        val traceStr =
            if (trace.nonEmpty)
                "Trace:\n" + trace.map("  - " + _).mkString("\n") + "\n"
            else ""
        fail(prefix + traceStr)
    }

    def expectNextValue(
        dut: ZzTop,
        expected: BigInt,
        timeout: Int,
        trace: ListBuffer[String]
    ): Unit = {
        var cycles = 0
        val clock_per_cpucycle = 2;
        dut.clock.step(
            clock_per_cpucycle
        ) // Attente d'un cycle proc (clock_per_cpucycle cycles systeme)
        // Attendre une écriture dans x31
        while (!dut.io.valid_x31.get.peek().litToBoolean && cycles < timeout) {
            dut.clock.step(clock_per_cpucycle) // Attente d'un cycle proc
            cycles += 1
            trace += f"Attente $cycles cycles"
        }

        if (cycles < timeout) {
            // Vérifier la nouvelle valeur
            val got = dut.io.x31.get.peek().litValue
            trace += f"Valeur attendue = 0x$expected%016X, reçue = 0x$got%016X"
            if (got != expected) failTrace("Mauvaise sortie", trace)
        } else {
            trace += f"Valeur attendue = 0x$expected%016X, reçue = ⏰"
            failTrace("Simulation bloquée", trace)
        }
    }
}
