# ***************************************************************************
# *   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 mesh gmsh document object"
__author__ = "Bernd Hahnebach"
__url__ = "https://www.freecadweb.org"

## @package mesh_gmsh
#  \ingroup FEM
#  \brief mesh gmsh object

from . import base_fempythonobject


class MeshGmsh(base_fempythonobject.BaseFemPythonObject):
    """
    A Fem::FemMeshObject python type, add Gmsh specific properties
    """

    Type = "Fem::FemMeshGmsh"

    # they will be used from the task panel too, thus they need to be outside of the __init__
    known_element_dimensions = ["From Shape", "1D", "2D", "3D"]
    known_element_orders = ["1st", "2nd"]
    known_mesh_algorithm_2D = [
        "Automatic",
        "MeshAdapt",
        "Delaunay",
        "Frontal",
        "BAMG",
        "DelQuad"
    ]
    known_mesh_algorithm_3D = [
        "Automatic",
        "Delaunay",
        "New Delaunay",
        "Frontal",
        "Frontal Delaunay",
        "Frontal Hex",
        "MMG3D",
        "R-tree"
    ]

    def __init__(self, obj):
        super(MeshGmsh, self).__init__(obj)
        self.add_properties(obj)

    def onDocumentRestored(self, obj):
        self.add_properties(obj)

    def add_properties(self, obj):
        if not hasattr(obj, "MeshBoundaryLayerList"):
            obj.addProperty(
                "App::PropertyLinkList",
                "MeshBoundaryLayerList",
                "Base",
                "Mesh boundaries need inflation layers"
            )
            obj.MeshBoundaryLayerList = []

        if not hasattr(obj, "MeshRegionList"):
            obj.addProperty(
                "App::PropertyLinkList",
                "MeshRegionList",
                "Base",
                "Mesh regions of the mesh"
            )
            obj.MeshRegionList = []

        if not hasattr(obj, "MeshGroupList"):
            obj.addProperty(
                "App::PropertyLinkList",
                "MeshGroupList",
                "Base",
                "Mesh groups of the mesh"
            )
            obj.MeshGroupList = []

        if not hasattr(obj, "Part"):
            obj.addProperty(
                "App::PropertyLink",
                "Part",
                "FEM Mesh",
                "Geometry object, the mesh is made from. The geometry object has to have a Shape."
            )
            obj.Part = None

        if not hasattr(obj, "CharacteristicLengthMax"):
            obj.addProperty(
                "App::PropertyLength",
                "CharacteristicLengthMax",
                "FEM Gmsh Mesh Params",
                "Max mesh element size (0.0 = infinity)"
            )
            obj.CharacteristicLengthMax = 0.0  # will be 1e+22

        if not hasattr(obj, "CharacteristicLengthMin"):
            obj.addProperty(
                "App::PropertyLength",
                "CharacteristicLengthMin",
                "FEM Gmsh Mesh Params",
                "Min mesh element size"
            )
            obj.CharacteristicLengthMin = 0.0

        if not hasattr(obj, "ElementDimension"):
            obj.addProperty(
                "App::PropertyEnumeration",
                "ElementDimension",
                "FEM Gmsh Mesh Params",
                "Dimension of mesh elements (Auto = according ShapeType of part to mesh)"
            )
            obj.ElementDimension = MeshGmsh.known_element_dimensions
            obj.ElementDimension = "From Shape"  # according ShapeType of Part to mesh

        if not hasattr(obj, "ElementOrder"):
            obj.addProperty(
                "App::PropertyEnumeration",
                "ElementOrder",
                "FEM Gmsh Mesh Params",
                "Order of mesh elements"
            )
            obj.ElementOrder = MeshGmsh.known_element_orders
            obj.ElementOrder = "2nd"

        if not hasattr(obj, "OptimizeStd"):
            obj.addProperty(
                "App::PropertyBool",
                "OptimizeStd",
                "FEM Gmsh Mesh Params",
                "Optimize tetra elements"
            )
            obj.OptimizeStd = True

        if not hasattr(obj, "OptimizeNetgen"):
            obj.addProperty(
                "App::PropertyBool",
                "OptimizeNetgen",
                "FEM Gmsh Mesh Params",
                "Optimize tetra elements by use of Netgen"
            )
            obj.OptimizeNetgen = False

        if not hasattr(obj, "HighOrderOptimize"):
            obj.addProperty(
                "App::PropertyBool",
                "HighOrderOptimize",
                "FEM Gmsh Mesh Params",
                "Optimize high order meshes"
            )
            obj.HighOrderOptimize = False

        if not hasattr(obj, "RecombineAll"):
            obj.addProperty(
                "App::PropertyBool",
                "RecombineAll",
                "FEM Gmsh Mesh Params",
                "Apply recombination algorithm to all surfaces"
            )
            obj.RecombineAll = False

        if not hasattr(obj, "CoherenceMesh"):
            obj.addProperty(
                "App::PropertyBool",
                "CoherenceMesh",
                "FEM Gmsh Mesh Params",
                "Removes all duplicate mesh vertices"
            )
            obj.CoherenceMesh = True

        if not hasattr(obj, "GeometryTolerance"):
            obj.addProperty(
                "App::PropertyFloat",
                "GeometryTolerance",
                "FEM Gmsh Mesh Params",
                "Geometrical Tolerance (0.0 = GMSH std = 1e-08)"
            )
            obj.GeometryTolerance = 1e-06

        if not hasattr(obj, "SecondOrderLinear"):
            obj.addProperty(
                "App::PropertyBool",
                "SecondOrderLinear",
                "FEM Gmsh Mesh Params",
                "Second order nodes are created by linear interpolation"
            )
            obj.SecondOrderLinear = False
            # gives much better meshes in the regard of nonpositive jacobians
            # but
            # on curved faces the constraint nodes will no longer found
            # thus standard will be False
            # https://forum.freecadweb.org/viewtopic.php?t=41738
            # https://forum.freecadweb.org/viewtopic.php?f=18&t=45260&start=20#p389494

        if not hasattr(obj, "Algorithm2D"):
            obj.addProperty(
                "App::PropertyEnumeration",
                "Algorithm2D",
                "FEM Gmsh Mesh Params",
                "mesh algorithm 2D"
            )
            obj.Algorithm2D = MeshGmsh.known_mesh_algorithm_2D
            obj.Algorithm2D = "Automatic"  # ?

        if not hasattr(obj, "Algorithm3D"):
            obj.addProperty(
                "App::PropertyEnumeration",
                "Algorithm3D",
                "FEM Gmsh Mesh Params",
                "mesh algorithm 3D"
            )
            obj.Algorithm3D = MeshGmsh.known_mesh_algorithm_3D
            obj.Algorithm3D = "Automatic"  # ?

        if not hasattr(obj, "GroupsOfNodes"):
            obj.addProperty(
                "App::PropertyBool",
                "GroupsOfNodes",
                "FEM Gmsh Mesh Params",
                "For each group create not only the elements but the nodes too."
            )
            obj.GroupsOfNodes = False
