# pylint: disable=R0914
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0

import os
import re


def run(test, *, verilator_flags2=()):
    variant, fmt = test.parse_name(r"t_trace_lib_(default|notop)_([a-z]+)")

    # All test use the same SV file
    test.top_filename = "t/t_trace_lib.v"
    # Any variations after the format name must yield the exact same trace
    test.golden_filename = test.py_filename.rpartition(fmt)[0] + fmt + ".out"

    verilator_common_flags = [
        "--cc",
        f"--trace-{fmt}",
        "--trace-underscore",  # Should not trace __Vhandle
        "--trace-max-width",
        "0",
        "--trace-max-array",
        "0",
        "--trace-structs"
    ]

    main_top_name = "top"
    match variant:
        case "default":
            pass
        case "notop":
            main_top_name = ""
        case _:
            test.error(f"Unhandled test variant '{variant}'")

    verilator_common_flags.extend(verilator_flags2)

    # Compile sub0 --lib-create
    test.vm_prefix = "Vsub0"
    test.compile(make_main=False,
                 threads=1,
                 verilator_make_gmake=False,
                 verilator_flags2=verilator_common_flags + [
                     "+define+SUB0",
                     "--lib-create",
                     "sub0",
                     "--top-module",
                     "sub0",
                 ])
    test.run(logfile=test.obj_dir + "/Vsub0_make.log",
             cmd=[os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vsub0.mk", "libsub0.a"])
    # Compile sub1 --lib-create
    test.vm_prefix = "Vsub1"
    test.compile(make_main=False,
                 threads=1,
                 verilator_make_gmake=False,
                 verilator_flags2=verilator_common_flags + [
                     "+define+SUB1",
                     "--lib-create",
                     "sub1",
                     "--top-module",
                     "sub1",
                 ])
    test.run(logfile=test.obj_dir + "/Vsub1_make.log",
             cmd=[os.environ["MAKE"], "-C", test.obj_dir, "-f", "Vsub1.mk", "libsub1.a"])
    # Compile top with libraries
    test.vm_prefix = "Vlibs"
    test.compile(main_top_name=main_top_name,
                 verilator_flags2=verilator_common_flags + [
                     "+define+TOP", test.obj_dir + "/sub0.sv", "libsub0.a",
                     test.obj_dir + "/sub1.sv", "libsub1.a"
                 ])

    # Compile simply without libraries
    test.vm_prefix = "Vnonl"
    test.main_filename = test.obj_dir + "/Vnonl__main.cpp"
    test.compile(main_top_name=main_top_name,
                 verilator_flags2=verilator_common_flags +
                 ["+define+TOP", "+define+SUB0", "+define+SUB1"])

    trace_libs = test.trace_filename.replace("simx", "libs")
    trace_nonl = test.trace_filename.replace("simx", "nonl")

    # Run the --lib-create model
    test.execute(executable=test.obj_dir + "/Vlibs")
    test.run(cmd=["mv", test.trace_filename, trace_libs])
    # Run the simple model without libraries
    test.execute(executable=test.obj_dir + "/Vnonl")
    test.run(cmd=["mv", test.trace_filename, trace_nonl])

    # Scope structure must match exactly, check only in vcd
    if fmt == "vcd":
        with open(trace_nonl, 'r', encoding='utf8') as fnonl, \
             open(trace_libs, 'r', encoding='utf8') as flibs:
            for la, lb in zip(fnonl, flibs):
                la = re.sub(r'^(\s*\$var\s+\S+\s+\S+\s+)\S+(.*)$', r'\1CODE\2', la)
                lb = re.sub(r'^(\s*\$var\s+\S+\s+\S+\s+)\S+(.*)$', r'\1CODE\2', lb)
                if la != lb:
                    test.error_keep_going("VCD header mismatch: '{}' !~ '{}'".format(
                        la.strip(), lb.strip()))
                if "enddefinitions" in la:
                    break

    test.trace_identical(trace_libs, trace_nonl)
    # The --lib-create must match the reference
    test.trace_identical(trace_libs, test.golden_filename)

    test.passes()
