# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.

"""Module for generation of c- and a2l-file with dummy signal declarations."""

import powertrain_build.build_defs as bd

from powertrain_build.types import byte_size_string, get_bitmask
from powertrain_build.a2l import A2l
from powertrain_build.problem_logger import ProblemLogger


class DummyVar(ProblemLogger):
    """Generate c- and a2l-files which declares all missing variables in the interfaces.

    TODO: Please remove this file! Only used while testing.
    """

    def __init__(self, unit_cfg, ext_dict, res_dict, prj_cfg, user_defined_types):
        """Initialize instance of class."""
        super().__init__()
        self._unit_cfg = unit_cfg
        self._unit_vars = unit_cfg.get_per_cfg_unit_cfg()
        self._ext_dict = ext_dict
        self._res_dict = res_dict
        self._ext_vars = {}
        self._int_vars = {}
        self._prj_cfg = prj_cfg
        self._enumerations = user_defined_types.get_enumerations()
        self._common_header_files = user_defined_types.common_header_files

    def _get_byte_size_string(self, data_type):
        """Get byte size of a data type as string.
        Enumeration byte sizes are derived from the underlying data type.

        Args:
            data_type (str): Data type.
        Returns:
            byte_size_string(powertrain_build.types.byte_size_string): Return result of
                                                                       powertrain_build.types.byte_size_string.
        """
        if data_type in self._enumerations:
            return byte_size_string(self._enumerations[data_type]["underlying_data_type"])
        return byte_size_string(data_type)

    def _restruct_input_data(self):
        """Restructure all the input variables per data-type.

        This will be used for declaring the variables and generating the
        A2L-file
        """
        ext_out = {
            var: data
            for ioclass, vardict in self._ext_dict.items()
            if ioclass.endswith("-Output")
            for var, data in vardict.items()
        }
        ext_ = {}
        for var in self._res_dict["sigs"]["ext"]["missing"]:
            self.debug("ext: %s", var)
            if var in ext_out:
                data = ext_out[var]
                self.debug("ext_data: %s", data)
                ext_[var] = data
        int_ = {}
        for unit in self._res_dict["sigs"]["int"]:
            for var in self._res_dict["sigs"]["int"][unit]["missing"]:
                if var not in ext_ and var in self._unit_vars["inports"]:
                    data = self._unit_vars["inports"][var][unit]
                    int_[var] = data
        for var, data in int_.items():
            data_type_size = self._get_byte_size_string(data["type"])
            self._int_vars.setdefault(data_type_size, {})[var] = data
        for var, data in ext_.items():
            data_type_size = self._get_byte_size_string(data["type"])
            self._ext_vars.setdefault(data_type_size, {})[var] = data

    def _a2l_dict(self):
        """Return a dict defining all parameters for a2l-generation."""
        res = {"vars": {}, "function": "VcDummy"}
        for inp in [self._ext_vars]:
            for sizes in inp.values():
                for var, data in sizes.items():
                    if data["type"] in self._enumerations:
                        data_type = self._enumerations[data["type"]]["underlying_data_type"]
                    else:
                        data_type = data["type"]

                    resv = res["vars"]
                    resv.setdefault(var, {})["a2l_data"] = {
                        "bitmask": get_bitmask(data_type),
                        "description": data.get("description", ""),
                        "lsb": "2^0",
                        "max": data.get("max"),
                        "min": data.get("min"),
                        "offset": "0",
                        "unit": data["unit"],
                        "x_axis": None,
                        "y_axis": None,
                    }
                    resv[var]["array"] = []
                    resv[var]["function"] = ["VcEc"]
                    resv[var]["var"] = {"cvc_type": "CVC_DISP", "type": data_type, "var": var}
        return res

    @classmethod
    def _generate_var_defs(cls, fh_c, vars, enums, comment):
        """Generate the variable definitions."""
        fh_c.write(f"\n{comment}\n\n")
        for varsize in sorted(vars.keys(), reverse=True):
            fh_c.write(f"/* Variables of size {varsize} bytes */\n\n")
            var_defs = vars[varsize]
            for var in sorted(var_defs.keys()):
                data = var_defs[var]
                if data["type"] in enums:
                    if enums[data["type"]]["default_value"] is not None:
                        init_value = enums[data["type"]]["default_value"]
                    else:
                        cls.warning('Initializing enumeration %s to "zero".', data["type"])
                        init_value = [k for k, v in enums[data["type"]]["members"].items() if v == 0][0]
                    fh_c.write(f"{data['type']} {var} = {init_value};\n")
                else:
                    fh_c.write(f"{data['type']} {var} = {0};\n")
        fh_c.write("\n")

    @classmethod
    def _generate_var_initialization(cls, fh_c, vars, comment):
        """Generate the variable initializations."""
        fh_c.write(f"\n{comment}\n\n")
        fh_c.write("\nvoid RESTART_VcDummy(void)\n{\n")
        for varsize in sorted(vars.keys(), reverse=True):
            var_defs = vars[varsize]
            for var in sorted(var_defs.keys()):
                fh_c.write(f"    {var} = {0};\n")
        fh_c.write("}\n")

    def _generate_c_file(self, filename):
        """Generate the c-file defining all missing input variables."""
        general_includes = ""
        general_includes += self._unit_cfg.base_types_headers
        for common_header_file in self._common_header_files:
            general_includes += f'#include "{common_header_file}"\n'
        general_includes += "\n"

        with open(filename, "w", encoding="utf-8") as fh_c:
            fh_c.write(general_includes)
            fh_c.write(f'#include "{bd.CVC_DISP_START}"\n\n')
            self._generate_var_defs(fh_c, self._ext_vars, self._enumerations, "/** Missing external signals **/")
            fh_c.write(f'\n#include "{bd.CVC_DISP_END}"\n')
            self.info("Generated %s", filename)

    def generate_file(self, filename):
        """Generate the files for defining all missing input variables."""
        self._restruct_input_data()
        self._generate_c_file(filename + ".c")
        a2l_dict = self._a2l_dict()
        A2l(a2l_dict, self._prj_cfg).gen_a2l(filename + ".a2l")
