"""
FEM model module for the Metamaterial Analysis Code (MAC) package. It represents the whole model.
"""
from datetime import datetime
from typing import TypedDict

from .MACMaterial import MACMaterial
from .MACProperty import MACProperty
from .MACStructure import MACAuxetic
from .MACGlobals import MAC_VERSION, ELEMENTS_SET, NODES_DICT
from .MACCell import MACCell
from MetamaterialAnalysisCode._modules._centers_RPA import _centers_RPA
from MetamaterialAnalysisCode.classes.MACElement import MACElement


class MACModel:
    """
    FEM model class for the Metamaterial Analysis Code (MAC) package. It represents the whole model.

    Attributes:
        ModelDimensions: dimensions of the model
        CellStructure: structure of the cell (BBC, FCC, Auxetic, etc)
        CellSize: size of the cell
        CellMaterial: MACMaterial of the cell
        CellProperty: MACProperty of the cell
    """

    def __init__(self, modeldimensions: tuple[float], cellstructure: MACAuxetic, cellmaterial: list[MACMaterial],
                 cellproperty: list[MACProperty]):
        """
        Constructor for MACModel class
        """
        self.__modeldimensions = modeldimensions
        self.__cellstructure = cellstructure
        self.__cellmaterial = cellmaterial
        self.__cellproperty = cellproperty
        self.__celldict = {}

        # Function to generate the cells centers for auxetic structures
        cloud_set = _centers_RPA(a=round((3**0.5) * 0.5 * self.__cellstructure.Stepy, 4),
                                 h=round(self.__cellstructure.Hprisma, 4),
                                 stx=round(self.__cellstructure.Stepx, 4),
                                 dimensions=self.__modeldimensions
                                 )

        for i, center in enumerate(cloud_set):
            nodes, elements = self.__cellstructure.build(
                                                         cellcenter=center, mat=self.__cellmaterial[0],
                                                         prop=self.__cellproperty[0], vvector=(0.0, 0.0, 1.0)
                                                        )

            self.__celldict[i] = MACCell(i, self.__cellstructure, center, elements, nodes, self.__cellmaterial,
                                         self.__cellproperty)

        self.NodeDict = {node.ID: node for node in NODES_DICT.values()}
        self.ElementDict: ElementDictType = {element.ID: element for element in ELEMENTS_SET}

    def write_fem(self, path: str, writeheader: bool = True) -> None:
        """
        Writes the .fem file for the FEM model in the specified path. It appends the data to the file if it already
        exists.
        """

        if path[-4:] != ".fem":
            path += ".fem"

        with open(path, "a") as w:

            if writeheader:

                today = datetime.now().strftime("%d/%m/%Y - %H:%M:%S")
                analysis_name = path.split("\\")[-1].split(".")[0]
                macverlen = 78 - (len(MAC_VERSION) + len("$$  MAC version: "))
                analysis_namelen = 78 - (len(analysis_name) + len("$$  Analysis Name: "))

                header = "$" * 80 + "\n" + \
                         "$" * 13 + "  File generated by Metamaterial Analysis Code (MAC)  " + "$" * 13 + "\n" + \
                         "$" * 80 + "\n" + \
                         "$$" + " " * 76 + "$$" + "\n" + \
                         "$$  Escuela Tecnica Superior de Ingenieria Aeronautica y del Espacio" + " " * 10 + "$$" + "\n" + \
                         "$$" + " " * 76 + "$$" + "\n" + \
                         f"$$  MAC version: {MAC_VERSION}" + " " * macverlen + "$$" + "\n" + \
                         "$$" + " " * 76 + "$$" + "\n" + \
                         f"$$  Date: {today}" + " " * 47 + "$$" "\n" + \
                         "$$" + " " * 76 + "$$" + "\n" + \
                         f"$$  Analysis Name: {analysis_name}" + " " * analysis_namelen + "$$" + "\n" + \
                         "$$" + " " * 76 + "$$" + "\n" + \
                         "$" * 80 + "\n"
                w.writelines(header)

            w.writelines("\n$$\n$$  GRID Data\n$$\n")

            for node in sorted(self.NodeDict.keys()):
                w.writelines(str(self.NodeDict[node]))

            w.writelines("\n$$\n$$  Elements\n$$\n")

            for element in self.ElementDict.values():
                    w.writelines(str(element))

            w.writelines("\n$$\n$$ Properties\n$$\n")

            for prop in self.__cellproperty:
                w.writelines(str(prop))

            w.writelines("\n$$\n$$ Materials\n$$\n")

            for material in self.__cellmaterial:
                w.writelines(str(material))

    def __del__(self):
        """
        Destructor for MACModel class
        """
        ELEMENTS_SET.clear()
        NODES_DICT.clear()


def set_model(**kwargs) -> MACModel:
    """
    Function that creates a MACModel object. It uses the kwargs dictionary.
    model = set_model(modeldimensions=(x, y, z), cellstructure=MACAuxetic, cellmaterial=[MACMaterial],
                      cellproperty=[MACProperty])
    """
    model = MACModel(modeldimensions=kwargs["modeldimensions"], cellstructure=kwargs["cellstructure"],
                     cellmaterial=kwargs["cellmaterial"], cellproperty=kwargs["cellproperty"])
    return model


class ElementDictType(TypedDict):
    """
    Dictionary with the elements of the model. The key is the element ID and the value is a MACElement.
    """
    int: MACElement

