# 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 re


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

    # All test use the same SV file
    test.top_filename = "t/t_hier_block.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 = []
    match mode:
        case "cc":
            verilator_common_flags.extend([  #
                "--cc",
            ])
        case "sc":
            verilator_common_flags.extend([  #
                "--sc",
                "--CFLAGS",
                '"-pipe -DCPP_MACRO=cplusplus"',
            ])
        case _:
            test.error(f"Unknown mode '{mode}'")

    verilator_common_flags.extend([
        f"--trace-{fmt}",
        "--trace-underscore",  # Should not trace __Vhandle
        "--trace-max-width",
        "0",
        "--trace-max-array",
        "0",
        "--trace-structs",
        "t/t_hier_block.cpp"
    ])

    main_top_name = "top"
    match variant:
        case "default":
            pass
        case "notop":
            main_top_name = ""
        case "statefulpkg":
            verilator_common_flags.append("+define+STATEFUL_PKG")
        case _:
            test.error(f"Unhandled test variant '{variant}'")

    verilator_common_flags.extend(verilator_flags2)
    verilator_hier_flags = verilator_common_flags + ['--hierarchical']

    # Compile hierarchically
    test.vm_prefix = "Vhier"
    test.main_filename = test.obj_dir + "/Vhier__main.cpp"
    test.compile(verilator_flags2=verilator_hier_flags, main_top_name=main_top_name)

    # Compile non-hierarchically
    test.vm_prefix = "Vnonh"
    test.main_filename = test.obj_dir + "/Vnonh__main.cpp"
    test.compile(verilator_flags2=verilator_common_flags, main_top_name=main_top_name)

    trace_hier = test.trace_filename.replace("simx", "hier")
    trace_nonh = test.trace_filename.replace("simx", "nonh")

    # Run the hierarchical model
    test.execute(executable=test.obj_dir + "/Vhier")
    test.run(cmd=["mv", test.trace_filename, trace_hier])
    # Run the non hierarchical model
    test.execute(executable=test.obj_dir + "/Vnonh")
    test.run(cmd=["mv", test.trace_filename, trace_nonh])

    if variant != "statefulpkg":
        # Scope structure must match exactly, check only in vcd
        if fmt == "vcd":
            with open(trace_nonh, 'r', encoding='utf8') as fnonh, \
                 open(trace_hier, 'r', encoding='utf8') as fhier:
                for la, lb in zip(fnonh, fhier):
                    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_hier, trace_nonh)
    # The hierarchical must match the reference
    test.trace_identical(trace_hier, test.golden_filename)

    test.passes()
