import copy
import random

import FreeCAD
import Mesh
from freecad.trails import ICONPATH, geo_origin
from pivy import coin


def create(name='Surface'):
    obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Surface")
    obj.Label = name
    Surface(obj)
    ViewProviderSurface(obj.ViewObject)
    FreeCAD.ActiveDocument.recompute()

    return obj


#class Surface(SurfaceFunc):
class Surface(ArchComponent.Component):
    """
    This class is about Surface Object data features.
    """

    def __init__(self, obj):
        '''
        Set data properties.
        '''

        ArchComponent.Component.__init__(self, obj)

        self.Type = 'PVPlant::Surface'
        self.setproperties()
        obj.Proxy = self

        # Does a IfcType exist?
        obj.IfcType = "Civil Element"
        obj.setEditorMode("IfcType", 1)

    def setproperties(self):
        # Triangulation properties.
        obj.addProperty(
            'App::PropertyLinkList', "PointGroups", "Base",
            "List of Point Groups").PointGroups = []

        obj.addProperty(
            "App::PropertyIntegerList", "Delaunay", "Base",
            "Index of Delaunay vertices", 4).Delaunay = []

        obj.addProperty(
            "Mesh::PropertyMeshKernel", "Mesh", "Base",
            "Mesh object of triangulation").Mesh = Mesh.Mesh()

        obj.addProperty(
            "App::PropertyLength", "MaxLength", "Base",
            "Maximum length of triangle edge").MaxLength = 50000

        obj.addProperty(
            "App::PropertyAngle", "MaxAngle", "Base",
            "Maximum angle of triangle edge").MaxAngle = 170

        # Contour properties.
        obj.addProperty(
            "App::PropertyFloatConstraint", "ContourInterval", "Contour",
            "Size of the point group").ContourInterval = (1.0, 0.0, 100.0, 1.0)

        obj.addProperty(
            "App::PropertyVectorList", "ContourPoints", "Contour",
            "Points of contours", 4).ContourPoints = []

        obj.addProperty(
            "App::PropertyIntegerList", "ContourVertices", "Contour",
            "Vertices of contours.", 4).ContourVertices = []


    def onChanged(self, obj, prop):
        '''
        Do something when a data property has changed.
        '''

        points = []
        pgs = obj.getPropertyByName("PointGroups")
        for pg in pgs:
            points.extend(pg.Points)
        if points:
            origin = geo_origin.get(points[0])
        else:
            origin = geo_origin.get()

        if prop == "PointGroups":
            if len(points) > 2:
                obj.Delaunay = self.triangulate(points)
            else:
                obj.Mesh = Mesh.Mesh()

        if prop == "Delaunay" or prop == "MaxLength" or prop == "MaxAngle":
            delaunay = obj.getPropertyByName("Delaunay")
            lmax = obj.getPropertyByName("MaxLength")
            amax = obj.getPropertyByName("MaxAngle")

            if delaunay:
                obj.Mesh = self.test_delaunay(
                    origin.Origin, points, delaunay, lmax, amax)

        if prop == "Mesh" or prop == "ContourInterval":
            deltaH = obj.getPropertyByName("ContourInterval")
            mesh = obj.getPropertyByName("Mesh")

            coords, num_vert = self.contour_points(origin.Origin, mesh, deltaH)

            obj.ContourPoints = coords
            obj.ContourVertices = num_vert

    def execute(self, obj):
        '''
        Do something when doing a recomputation.
        '''
        pass


class ViewProviderSurface:
    """
    This class is about Surface Object view features.
    """

    def __init__(self, vobj):
        '''
        Set view properties.
        '''
        (r, g, b) = (random.random(), random.random(), random.random())

        vobj.addProperty(
            "App::PropertyColor", "TriangleColor", "Surface Style",
            "Color of the point group").TriangleColor = (r, g, b)

        vobj.Proxy = self

    def attach(self, vobj):
        '''
        Create Object visuals in 3D view.
        '''
        # GeoCoords Node.
        self.geo_coords = coin.SoGeoCoordinate()

        # Surface features.
        self.triangles = coin.SoIndexedFaceSet()
        shape_hints = coin.SoShapeHints()
        shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
        self.mat_color = coin.SoMaterial()
        mat_binding = coin.SoMaterialBinding()
        mat_binding.value = coin.SoMaterialBinding.OVERALL
        edge_color = coin.SoBaseColor()
        edge_color.rgb = (0.5, 0.5, 0.5)
        offset = coin.SoPolygonOffset()

        # Line style.
        line_style = coin.SoDrawStyle()
        line_style.style = coin.SoDrawStyle.LINES
        line_style.lineWidth = 2

        # Contour features.
        cont_color = coin.SoBaseColor()
        cont_color.rgb = (1, 1, 0)
        self.cont_coords = coin.SoGeoCoordinate()
        self.cont_lines = coin.SoLineSet()

        # Contour root.
        contours = coin.SoSeparator()
        contours.addChild(cont_color)
        contours.addChild(line_style)
        contours.addChild(self.cont_coords)
        contours.addChild(self.cont_lines)

        # Face root.
        faces = coin.SoSeparator()
        faces.addChild(shape_hints)
        faces.addChild(self.mat_color)
        faces.addChild(mat_binding)
        faces.addChild(self.geo_coords)
        faces.addChild(self.triangles)

        # Highlight for selection.
        highlight = coin.SoType.fromName('SoFCSelection').createInstance()
        highlight.style = 'EMISSIVE_DIFFUSE'
        highlight.addChild(edge_color)
        highlight.addChild(line_style)
        highlight.addChild(self.geo_coords)
        highlight.addChild(self.triangles)

        # Surface root.
        surface_root = coin.SoSeparator()
        surface_root.addChild(contours)
        surface_root.addChild(offset)
        surface_root.addChild(faces)
        surface_root.addChild(offset)
        surface_root.addChild(highlight)
        vobj.addDisplayMode(surface_root, "Surface")

        # Take features from properties.
        self.onChanged(vobj, "TriangleColor")

    def onChanged(self, vobj, prop):
        '''
        Update Object visuals when a view property changed.
        '''
        try:
            if prop == "TriangleColor":
                color = vobj.getPropertyByName("TriangleColor")
                self.mat_color.diffuseColor = (color[0], color[1], color[2])
        except Exception:
            pass

    def updateData(self, obj, prop):
        '''
        Update Object visuals when a data property changed.
        '''
        if prop == "Mesh":
            mesh = obj.getPropertyByName("Mesh")
            topo_points = mesh.Topology[0]
            topo_tri = mesh.Topology[1]

            # Get GeoOrigin.
            points = []
            triangles = []
            origin = geo_origin.get()
            base = copy.deepcopy(origin.Origin)
            base.z = 0

            for i in topo_points:
                point = copy.deepcopy(i)
                points.append(point.add(base))

            for i in topo_tri:
                triangles.extend(list(i))
                triangles.append(-1)

            # Set GeoCoords.
            geo_system = ["UTM", origin.UtmZone, "FLAT"]
            self.geo_coords.geoSystem.setValues(geo_system)
            self.geo_coords.point.values = points

            # Set contour system.
            self.cont_coords.geoSystem.setValues(geo_system)
            self.triangles.coordIndex.values = triangles

        if prop == "Mesh" or prop == "ContourInterval":
            cont_points = obj.getPropertyByName("ContourPoints")
            cont_vert = obj.getPropertyByName("ContourVertices")

            self.cont_coords.point.values = cont_points
            self.cont_lines.numVertices.values = cont_vert

    def getDisplayModes(self, vobj):
        '''
        Return a list of display modes.
        '''
        modes = []
        modes.append("Surface")

        return modes

    def getDefaultDisplayMode(self):
        '''
        Return the name of the default display mode.
        '''
        return "Surface"

    def setDisplayMode(self, mode):
        '''
        Map the display mode defined in attach with
        those defined in getDisplayModes.
        '''
        return mode

    def getIcon(self):
        '''
        Return object treeview icon.
        '''
        return ICONPATH + '/icons/Surface.svg'

    def __getstate__(self):
        """
        Save variables to file.
        """
        return None

    def __setstate__(self, state):
        """
        Get variables from file.
        """
        return None