# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import os
import logging

from edalize.edatool import Edatool

logger = logging.getLogger(__name__)


class Isim(Edatool):

    argtypes = ["plusarg", "vlogdefine", "vlogparam"]

    MAKEFILE_TEMPLATE = """#Auto generated by Edalize
include config.mk

all: $(TARGET)

$(TARGET):
	fuse $(TOPLEVEL) -prj $(TARGET).prj -o $(TARGET) $(VLOG_DEFINES) $(VLOG_INCLUDES) $(VLOG_PARAMS) $(FUSE_OPTIONS)

run: $(TARGET)
	./$(TARGET) -tclbatch run_$(TARGET).tcl $(ISIM_OPTIONS) $(EXTRA_OPTIONS)

run-gui: $(TARGET)
	./$(TARGET) -gui $(ISIM_OPTIONS) $(EXTRA_OPTIONS)
"""

    CONFIG_MK_TEMPLATE = """#Auto generated by Edalize
TARGET        = {target}
TOPLEVEL      = {toplevel}

VLOG_DEFINES  = {vlog_defines}
VLOG_INCLUDES = {vlog_includes}
VLOG_PARAMS   = {vlog_params}

FUSE_OPTIONS  =	{fuse_options}
ISIM_OPTIONS  =	{isim_options}

EXTRA_OPTIONS ?= {extra_options}
"""

    RUN_TCL_TEMPLATE = """#Auto generated by Edalize
wave log -r /
run all
quit
"""

    @classmethod
    def get_doc(cls, api_ver):
        if api_ver == 0:
            return {
                "description": "Xilinx ISim simulator from ISE design suite",
                "lists": [
                    {
                        "name": "fuse_options",
                        "type": "String",
                        "desc": "Additional options for compilation with FUSE",
                    },
                    {
                        "name": "isim_options",
                        "type": "String",
                        "desc": "Additional run options for ISim",
                    },
                ],
            }

    def configure_main(self):
        # Check if any VPI modules are present and display warning
        if len(self.vpi_modules) > 0:
            modules = [m["name"] for m in self.vpi_modules]
            logger.error("VPI modules not supported by Isim: %s" % ", ".join(modules))

        with open(os.path.join(self.work_root, self.name + ".prj"), "w") as f:
            (src_files, self.incdirs) = self._get_fileset_files()

            for src_file in src_files:
                prefix = ""
                logical_name = ""
                if src_file.file_type in [
                    "verilogSource",
                    "verilogSource-95",
                    "verilogSource-2001",
                ]:
                    prefix = "verilog"
                elif src_file.file_type.startswith("vhdlSource"):
                    prefix = "vhdl"
                    if src_file.logical_name:
                        logical_name = src_file.logical_name + " "
                elif src_file.file_type in [
                    "systemVerilogSource",
                    "systemVerilogSource-3.0",
                    "systemVerilogSource-3.1",
                    "systemVerilogSource-3.1a",
                    "verilogSource-2005",
                ]:
                    prefix = "sv"
                elif src_file.file_type in ["user"]:
                    pass
                else:
                    _s = "{} has unknown file type '{}'"
                    logger.warning(_s.format(src_file.name, src_file.file_type))
                if prefix:
                    f.write(
                        "{} work {}{}\n".format(prefix, logical_name, src_file.name)
                    )

        with open(os.path.join(self.work_root, "run_" + self.name + ".tcl"), "w") as f:
            f.write(self.RUN_TCL_TEMPLATE)

        with open(os.path.join(self.work_root, "Makefile"), "w") as f:
            f.write(self.MAKEFILE_TEMPLATE)

        with open(os.path.join(self.work_root, "config.mk"), "w") as f:
            vlog_defines = " ".join(
                [
                    "--define {}={}".format(k, self._param_value_str(v))
                    for k, v, in self.vlogdefine.items()
                ]
            )
            vlog_includes = " ".join(["-i " + k for k in self.incdirs])
            vlog_params = " ".join(
                [
                    "--generic_top {}={}".format(k, self._param_value_str(v))
                    for k, v, in self.vlogparam.items()
                ]
            )
            fuse_options = " ".join(self.tool_options.get("fuse_options", []))
            isim_options = " ".join(self.tool_options.get("isim_options", []))

            _s = "-testplusarg {}={}"
            extra_options = " ".join(
                [
                    _s.format(k, self._param_value_str(v))
                    for k, v in self.plusarg.items()
                ]
            )
            f.write(
                self.CONFIG_MK_TEMPLATE.format(
                    target=self.name,
                    toplevel=self.toplevel,
                    vlog_defines=vlog_defines,
                    vlog_includes=vlog_includes,
                    vlog_params=vlog_params,
                    fuse_options=fuse_options,
                    isim_options=isim_options,
                    extra_options=extra_options,
                )
            )

    def run_main(self):
        args = ["run"]
        # Plusargs
        if self.plusarg:
            _s = "-testplusarg {}={}"
            args.append(
                "EXTRA_OPTIONS="
                + " ".join(
                    [
                        _s.format(k, self._param_value_str(v))
                        for k, v in self.plusarg.items()
                    ]
                )
            )
        self._run_tool("make", args)
