# ***************************************************************************
# *   Copyright (c) 2016 Bernd Hahnebach <bernd@bimstatik.org>              *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

__title__ = "FreeCAD FEM solver writer base object"
__author__ = "Bernd Hahnebach"
__url__ = "https://www.freecad.org"

## \addtogroup FEM
#  @{

import os
from os.path import join

import FreeCAD

from femmesh import meshsetsgetter


class FemInputWriter:
    def __init__(
        self, analysis_obj, solver_obj, mesh_obj, member, dir_name=None, mat_geo_sets=None
    ):
        # class attributes from parameter values
        self.analysis = analysis_obj
        self.solver_obj = solver_obj
        self.mesh_object = mesh_obj
        self.member = member
        # more attributes
        self.analysis_type = self.solver_obj.AnalysisType
        self.document = self.analysis.Document
        # working dir
        # if dir_name was not given or if it exists but is not empty: create a temporary dir
        # Purpose: makes sure the analysis can be run even on wired situation
        make_tmp_dir = False
        if dir_name is None:
            FreeCAD.Console.PrintWarning(
                "Error: The working_dir in base input file writer class was not set. "
                "A temporary directory is used.\n"
            )
            make_tmp_dir = True
        elif not os.path.isdir(dir_name):
            FreeCAD.Console.PrintWarning(
                "Error: The working_dir: '{}' given to "
                "base input file writer class does not exist. "
                "A temporary directory is used.\n".format(dir_name)
            )
            make_tmp_dir = True
        if make_tmp_dir is True:
            from tempfile import mkdtemp

            dir_name = mkdtemp(prefix="fcfem_")
            FreeCAD.Console.PrintWarning(
                f"The working directory '{dir_name}' was created and will be used."
            )
        self.dir_name = dir_name

        # new class attributes
        self.fc_ver = FreeCAD.Version()
        self.ccx_nall = "Nall"
        self.ccx_eall = "Eall"
        self.ccx_evolumes = "Evolumes"
        self.ccx_efaces = "Efaces"
        self.ccx_eedges = "Eedges"
        self.mat_geo_sets = mat_geo_sets
        if self.mesh_object:
            self.femmesh = self.mesh_object.FemMesh
        else:
            FreeCAD.Console.PrintWarning(
                "No finite element mesh object was given to the writer class. "
                "In rare cases this might not be an error. "
            )

        # *************************************************
        # deprecated, leave for compatibility reasons
        # if these are calculated here they are calculated twice :-(
        self.femnodes_mesh = {}
        self.femelement_table = {}
        self.constraint_conflict_nodes = []
        self.femnodes_ele_table = {}
        self.femelements_edges_only = []
        self.femelements_faces_only = []
        self.femelement_volumes_table = {}
        self.femelement_faces_table = {}
        self.femelement_edges_table = {}
        self.femelement_count_test = True

        # deprecated, leave for compatibility reasons
        # do not add new objects
        # only the ones which exists on 0.19 release are kept
        # materials
        self.material_objects = member.mats_linear
        self.material_nonlinear_objects = member.mats_nonlinear
        # geometries
        self.beamsection_objects = member.geos_beamsection
        self.beamrotation_objects = member.geos_beamrotation
        self.fluidsection_objects = member.geos_fluidsection
        self.shellthickness_objects = member.geos_shellthickness
        # constraints
        self.contact_objects = member.cons_contact
        self.displacement_objects = member.cons_displacement
        self.fixed_objects = member.cons_fixed
        self.force_objects = member.cons_force
        self.heatflux_objects = member.cons_heatflux
        self.initialtemperature_objects = member.cons_initialtemperature
        self.planerotation_objects = member.cons_planerotation
        self.pressure_objects = member.cons_pressure
        self.selfweight_objects = member.cons_selfweight
        self.temperature_objects = member.cons_temperature
        self.tie_objects = member.cons_tie
        self.transform_objects = member.cons_transform
        self.electrostatic_objects = member.cons_electrostatic
        self.electricchargedensity_objects = member.cons_electricchargedensity

        # meshdatagetter, for compatibility, same with all getter methods
        self.meshdatagetter = meshsetsgetter.MeshSetsGetter(
            self.analysis,
            self.solver_obj,
            self.mesh_object,
            self.member,
        )

    # ********************************************************************************************
    # ********************************************************************************************
    # generic writer for constraints mesh sets and constraints property data
    # write constraint node sets, constraint face sets, constraint element sets
    def write_constraints_meshsets(self, f, femobjs, con_module):
        if not femobjs:
            return

        analysis_types = con_module.get_analysis_types()
        if analysis_types != "all" and self.analysis_type not in analysis_types:
            return

        def constraint_sets_loop_writing(the_file, femobjs, write_before, write_after):
            if write_before != "":
                the_file.write(write_before)
            for femobj in femobjs:
                # femobj --> dict, FreeCAD document object is femobj["Object"]
                the_obj = femobj["Object"]
                the_file.write(f"** {the_obj.Label}\n")
                con_module.write_meshdata_constraint(the_file, femobj, the_obj, self)
            if write_after != "":
                the_file.write(write_after)

        write_before = con_module.get_before_write_meshdata_constraint()
        write_after = con_module.get_after_write_meshdata_constraint()

        # write sets to file
        write_name = con_module.get_sets_name()
        f.write("\n{}\n".format(59 * "*"))
        f.write("** {}\n".format(write_name.replace("_", " ")))

        if self.split_inpfile is True:
            file_name_split = f"{self.mesh_name}_{write_name}.inp"
            f.write(f"*INCLUDE,INPUT={file_name_split}\n")
            inpfile_split = open(join(self.dir_name, file_name_split), "w")
            constraint_sets_loop_writing(inpfile_split, femobjs, write_before, write_after)
            inpfile_split.close()
        else:
            constraint_sets_loop_writing(f, femobjs, write_before, write_after)

    # write constraint property data
    def write_constraints_propdata(self, f, femobjs, con_module):

        if not femobjs:
            return

        analysis_types = con_module.get_analysis_types()
        if analysis_types != "all" and self.analysis_type not in analysis_types:
            return

        write_before = con_module.get_before_write_constraint()
        write_after = con_module.get_after_write_constraint()

        # write constraint to file
        f.write("\n{}\n".format(59 * "*"))
        f.write(f"** {con_module.get_constraint_title()}\n")
        if write_before != "":
            f.write(write_before)
        for femobj in femobjs:
            # femobj --> dict, FreeCAD document object is femobj["Object"]
            the_obj = femobj["Object"]
            f.write(f"** {the_obj.Label}\n")
            con_module.write_constraint(f, femobj, the_obj, self)
        if write_after != "":
            f.write(write_after)

    # ********************************************************************************************
    # deprecated, do not add new constraints
    # only the ones which exists on 0.19 release are kept
    def get_constraints_fixed_nodes(self):
        self.meshdatagetter.get_constraints_fixed_nodes()

    def get_constraints_displacement_nodes(self):
        self.meshdatagetter.get_constraints_displacement_nodes()

    def get_constraints_planerotation_nodes(self):
        self.meshdatagetter.get_constraints_planerotation_nodes()

    def get_constraints_transform_nodes(self):
        self.meshdatagetter.get_constraints_transform_nodes()

    def get_constraints_temperature_nodes(self):
        self.meshdatagetter.get_constraints_temperature_nodes()

    def get_constraints_fluidsection_nodes(self):
        self.meshdatagetter.get_constraints_fluidsection_nodes()

    def get_constraints_force_nodeloads(self):
        self.meshdatagetter.get_constraints_force_nodeloads()

    def get_constraints_pressure_faces(self):
        self.meshdatagetter.get_constraints_pressure_faces()

    def get_constraints_contact_faces(self):
        self.meshdatagetter.get_constraints_contact_faces()

    def get_constraints_tie_faces(self):
        self.meshdatagetter.get_constraints_tie_faces()

    def get_constraints_heatflux_faces(self):
        self.meshdatagetter.get_constraints_heatflux_faces()


##  @}
