import math

import FreeCAD
import Part
import ArchComponent
from pivy import coin
import numpy as np

if FreeCAD.GuiUp:
    import FreeCADGui, os
    from PySide import QtCore, QtGui
    from PySide.QtCore import QT_TRANSLATE_NOOP
else:
    # \cond
    def translate(ctxt, txt):
        return txt

    def QT_TRANSLATE_NOOP(ctxt, txt):
        return txt
    # \endcond

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

import PVPlantResources
from PVPlantResources import DirIcons as DirIcons

voltype = ["Fill", "Cut"]


def makeEarthWorksVolume(vtype = 0):
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", voltype[vtype])
    EarthWorksVolume(obj)
    ViewProviderEarthWorksVolume(obj.ViewObject)
    return obj


class EarthWorksVolume(ArchComponent.Component):
    def __init__(self, obj):
        # Definición de Variables:
        ArchComponent.Component.__init__(self, obj)
        self.obj = obj
        self.setProperties(obj)

    def setProperties(self, obj):
        # Definicion de Propiedades:
        pl = obj.PropertiesList

        if not ("VolumeType" in pl):
            obj.addProperty("App::PropertyEnumeration",
                            "VolumeType",
                            "Volume",
                            "Connection").VolumeType = voltype

        if not ("SurfaceSlope" in pl):
            obj.addProperty("App::PropertyPercent",
                            "SurfaceSlope",
                            "Volume",
                            "Connection").SurfaceSlope = 2

        if not ("VolumeMesh" in pl):
            obj.addProperty("Mesh::PropertyMeshKernel",
                            "VolumeMesh",
                            "Volume",
                            "Volume")
        obj.setEditorMode("VolumeMesh", 2)

        if not ("Volume" in pl):
            obj.addProperty("App::PropertyVolume",
                            "Volume",
                            "Volume",
                            "Volume")
        obj.setEditorMode("Volume", 1)

        obj.Proxy = self
        obj.IfcType = "Civil Element"
        obj.setEditorMode("IfcType", 1)
        obj.Proxy = self

    def onDocumentRestored(self, obj):
        ArchComponent.Component.onDocumentRestored(self, obj)
        self.setProperties(obj)

    def onChange(self, obj, prop):
        if prop == "VolumeMesh":
            if obj.VolumeMesh:
                obj.VolumeMesh = obj.VolumeMesh.Volume

    def execute(self, obj):
        '''  '''
        pass


class ViewProviderEarthWorksVolume:
    "A View Provider for the Pipe object"

    def __init__(self, vobj):
        ''' Set view properties.  '''
        pl = vobj.PropertiesList

        (r, g, b) = (1.0, 0.0, 0.0) if vobj.Object.VolumeType == "Cut" else (0.0, 0.0, 1.0)

        # Triangulation properties.
        if not "Transparency" in pl:
            vobj.addProperty("App::PropertyIntegerConstraint",
                             "Transparency", "Surface Style",
                             "Set triangle face transparency")
        vobj.Transparency = (50, 0, 100, 1)

        if not "ShapeColor" in pl:
            vobj.addProperty("App::PropertyColor",
                             "ShapeColor",
                             "Surface Style",
                             "Set triangle face color")
        vobj.ShapeColor = (r, g, b, vobj.Transparency / 100)

        if not "ShapeMaterial" in pl:
            vobj.addProperty("App::PropertyMaterial",
                             "ShapeMaterial", "Surface Style",
                             "Triangle face material")
        vobj.ShapeMaterial = FreeCAD.Material()

        if not "LineTransparency" in pl:
            vobj.addProperty("App::PropertyIntegerConstraint",
                             "LineTransparency", "Surface Style",
                             "Set triangle edge transparency")
        vobj.LineTransparency = (50, 0, 100, 1)

        if not "LineColor" in pl:
            vobj.addProperty("App::PropertyColor",
                             "LineColor", "Surface Style",
                             "Set triangle face color")
        vobj.LineColor = (0.5, 0.5, 0.5, vobj.LineTransparency / 100)

        '''vobj.addProperty(
            "App::PropertyMaterial", "LineMaterial", "Surface Style",
            "Triangle face material").LineMaterial = FreeCAD.Material()

        vobj.addProperty(
            "App::PropertyFloatConstraint", "LineWidth", "Surface Style",
            "Set triangle edge line width").LineWidth = (0.0, 1.0, 20.0, 1.0)

        # Boundary properties.
        vobj.addProperty(
            "App::PropertyColor", "BoundaryColor", "Boundary Style",
            "Set boundary contour color").BoundaryColor = (0.0, 0.75, 1.0, 0.0)

        vobj.addProperty(
            "App::PropertyFloatConstraint", "BoundaryWidth", "Boundary Style",
            "Set boundary contour line width").BoundaryWidth = (3.0, 1.0, 20.0, 1.0)

        vobj.addProperty(
            "App::PropertyEnumeration", "BoundaryPattern", "Boundary Style",
            "Set a line pattern for boundary").BoundaryPattern = [*line_patterns]

        vobj.addProperty(
            "App::PropertyIntegerConstraint", "PatternScale", "Boundary Style",
            "Scale the line pattern").PatternScale = (3, 1, 20, 1)

        # Contour properties.
        vobj.addProperty(
            "App::PropertyColor", "MajorColor", "Contour Style",
            "Set major contour color").MajorColor = (1.0, 0.0, 0.0, 0.0)

        vobj.addProperty(
            "App::PropertyFloatConstraint", "MajorWidth", "Contour Style",
            "Set major contour line width").MajorWidth = (4.0, 1.0, 20.0, 1.0)

        vobj.addProperty(
            "App::PropertyColor", "MinorColor", "Contour Style",
            "Set minor contour color").MinorColor = (1.0, 1.0, 0.0, 0.0)

        vobj.addProperty(
            "App::PropertyFloatConstraint", "MinorWidth", "Contour Style",
            "Set major contour line width").MinorWidth = (2.0, 1.0, 20.0, 1.0)
'''
        vobj.Proxy = self
        vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor

    def onChanged(self, vobj, prop):
        '''
        Update Object visuals when a view property changed.
        '''
        if prop == "ShapeColor" or prop == "Transparency":
            if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"):
                color = vobj.getPropertyByName("ShapeColor")
                transparency = vobj.getPropertyByName("Transparency")
                color = (color[0], color[1], color[2], transparency / 100)
                vobj.ShapeMaterial.DiffuseColor = color

        if prop == "ShapeMaterial":
            if hasattr(vobj, "ShapeMaterial"):
                material = vobj.getPropertyByName("ShapeMaterial")
                self.face_material.diffuseColor.setValue(material.DiffuseColor[:3])
                self.face_material.transparency = material.DiffuseColor[3]

        if prop == "LineColor" or prop == "LineTransparency":
            if hasattr(vobj, "LineColor") and hasattr(vobj, "LineTransparency"):
                color = vobj.getPropertyByName("LineColor")
                transparency = vobj.getPropertyByName("LineTransparency")
                color = (color[0], color[1], color[2], transparency / 100)
                vobj.LineMaterial.DiffuseColor = color

        if prop == "LineMaterial":
            material = vobj.getPropertyByName(prop)
            self.edge_material.diffuseColor.setValue(material.DiffuseColor[:3])
            self.edge_material.transparency = material.DiffuseColor[3]

        if prop == "LineWidth":
            width = vobj.getPropertyByName(prop)
            self.edge_style.lineWidth = width

        if prop == "BoundaryColor":
            color = vobj.getPropertyByName(prop)
            self.boundary_color.rgb = color[:3]

        if prop == "BoundaryWidth":
            width = vobj.getPropertyByName(prop)
            self.boundary_style.lineWidth = width

        if prop == "BoundaryPattern":
            if hasattr(vobj, "BoundaryPattern"):
                pattern = vobj.getPropertyByName(prop)
                self.boundary_style.linePattern = line_patterns[pattern]

        if prop == "PatternScale":
            if hasattr(vobj, "PatternScale"):
                scale = vobj.getPropertyByName(prop)
                self.boundary_style.linePatternScaleFactor = scale

        if prop == "MajorColor":
            color = vobj.getPropertyByName(prop)
            self.major_color.rgb = color[:3]

        if prop == "MajorWidth":
            width = vobj.getPropertyByName(prop)
            self.major_style.lineWidth = width

        if prop == "MinorColor":
            color = vobj.getPropertyByName(prop)
            self.minor_color.rgb = color[:3]

        if prop == "MinorWidth":
            width = vobj.getPropertyByName(prop)
            self.minor_style.lineWidth = width


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

        # Surface features.
        self.triangles = coin.SoIndexedFaceSet()
        self.face_material = coin.SoMaterial()
        self.edge_material = coin.SoMaterial()
        self.edge_color = coin.SoBaseColor()
        self.edge_style = coin.SoDrawStyle()
        self.edge_style.style = coin.SoDrawStyle.LINES

        shape_hints = coin.SoShapeHints()
        shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE
        mat_binding = coin.SoMaterialBinding()
        mat_binding.value = coin.SoMaterialBinding.PER_FACE
        offset = coin.SoPolygonOffset()
        offset.styles = coin.SoPolygonOffset.LINES
        offset.factor = -2.0

        # Boundary features.
        self.boundary_color = coin.SoBaseColor()
        self.boundary_coords = coin.SoGeoCoordinate()
        self.boundary_lines = coin.SoLineSet()
        self.boundary_style = coin.SoDrawStyle()
        self.boundary_style.style = coin.SoDrawStyle.LINES

        # Boundary root.
        boundaries = coin.SoType.fromName('SoFCSelection').createInstance()
        boundaries.style = 'EMISSIVE_DIFFUSE'
        boundaries.addChild(self.boundary_color)
        boundaries.addChild(self.boundary_style)
        boundaries.addChild(self.boundary_coords)
        boundaries.addChild(self.boundary_lines)

        # Major Contour features.
        self.major_color = coin.SoBaseColor()
        self.major_coords = coin.SoGeoCoordinate()
        self.major_lines = coin.SoLineSet()
        self.major_style = coin.SoDrawStyle()
        self.major_style.style = coin.SoDrawStyle.LINES

        # Major Contour root.
        major_contours = coin.SoSeparator()
        major_contours.addChild(self.major_color)
        major_contours.addChild(self.major_style)
        major_contours.addChild(self.major_coords)
        major_contours.addChild(self.major_lines)

        # Minor Contour features.
        self.minor_color = coin.SoBaseColor()
        self.minor_coords = coin.SoGeoCoordinate()
        self.minor_lines = coin.SoLineSet()
        self.minor_style = coin.SoDrawStyle()
        self.minor_style.style = coin.SoDrawStyle.LINES

        # Minor Contour root.
        minor_contours = coin.SoSeparator()
        minor_contours.addChild(self.minor_color)
        minor_contours.addChild(self.minor_style)
        minor_contours.addChild(self.minor_coords)
        minor_contours.addChild(self.minor_lines)

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

        # Face root.
        face = coin.SoSeparator()
        face.addChild(self.face_material)
        face.addChild(highlight)

        # Edge root.
        edge = coin.SoSeparator()
        edge.addChild(self.edge_material)
        edge.addChild(self.edge_style)
        edge.addChild(highlight)

        # Surface root.
        surface_root = coin.SoSeparator()
        surface_root.addChild(face)
        surface_root.addChild(offset)
        surface_root.addChild(edge)
        surface_root.addChild(major_contours)
        surface_root.addChild(minor_contours)
        vobj.addDisplayMode(surface_root, "Surface")

        # Boundary root.
        boundary_root = coin.SoSeparator()
        boundary_root.addChild(boundaries)
        vobj.addDisplayMode(boundary_root, "Boundary")

        # Elevation/Shaded root.
        shaded_root = coin.SoSeparator()
        shaded_root.addChild(face)
        vobj.addDisplayMode(shaded_root, "Elevation")
        vobj.addDisplayMode(shaded_root, "Slope")
        vobj.addDisplayMode(shaded_root, "Shaded")

        # Flat Lines root.
        flatlines_root = coin.SoSeparator()
        flatlines_root.addChild(face)
        flatlines_root.addChild(offset)
        flatlines_root.addChild(edge)
        vobj.addDisplayMode(flatlines_root, "Flat Lines")

        # Wireframe root.
        wireframe_root = coin.SoSeparator()
        wireframe_root.addChild(edge)
        wireframe_root.addChild(major_contours)
        wireframe_root.addChild(minor_contours)
        vobj.addDisplayMode(wireframe_root, "Wireframe")

        # Take features from properties.
        self.onChanged(vobj, "ShapeColor")
        self.onChanged(vobj, "LineColor")
        self.onChanged(vobj, "LineWidth")
        '''self.onChanged(vobj, "BoundaryColor")
        self.onChanged(vobj, "BoundaryWidth")
        self.onChanged(vobj, "BoundaryPattern")
        self.onChanged(vobj, "PatternScale")
        self.onChanged(vobj, "MajorColor")
        self.onChanged(vobj, "MajorWidth")
        self.onChanged(vobj, "MinorColor")
        self.onChanged(vobj, "MinorWidth")'''


    def updateData(self, obj, prop):
        '''
        Update Object visuals when a data property changed.
        '''

        # Set System.
        geo_system = ["UTM", FreeCAD.ActiveDocument.Site.UtmZone, "FLAT"]
        self.geo_coords.geoSystem.setValues(geo_system)
        self.boundary_coords.geoSystem.setValues(geo_system)
        self.major_coords.geoSystem.setValues(geo_system)
        self.minor_coords.geoSystem.setValues(geo_system)

        if prop == "VolumeMesh":
            mesh = obj.VolumeMesh
            copy_mesh = mesh.copy()
            #copy_mesh.Placement.move(origin.Origin)

            triangles = []
            for i in copy_mesh.Topology[1]:
                triangles.extend(list(i))
                triangles.append(-1)

            self.geo_coords.point.values = copy_mesh.Topology[0]
            self.triangles.coordIndex.values = triangles
            del copy_mesh

        '''if prop == "ContourShapes":
            contour_shape = obj.getPropertyByName(prop)

            if contour_shape.SubShapes:
                major_shape = contour_shape.SubShapes[0]
                points, vertices = self.wire_view(major_shape, origin.Origin)

                self.major_coords.point.values = points
                self.major_lines.numVertices.values = vertices

                minor_shape = contour_shape.SubShapes[1]
                points, vertices = self.wire_view(minor_shape, origin.Origin)

                self.minor_coords.point.values = points
                self.minor_lines.numVertices.values = vertices

        if prop == "BoundaryShapes":
            boundary_shape = obj.getPropertyByName(prop)
            points, vertices = self.wire_view(boundary_shape, origin.Origin, True)

            self.boundary_coords.point.values = points
            self.boundary_lines.numVertices.values = vertices

        if prop == "AnalysisType" or prop == "Ranges":
            analysis_type = obj.getPropertyByName("AnalysisType")
            ranges = obj.getPropertyByName("Ranges")

            if analysis_type == "Default":
                if hasattr(obj.ViewObject, "ShapeMaterial"):
                    material = obj.ViewObject.ShapeMaterial
                    self.face_material.diffuseColor = material.DiffuseColor[:3]

            if analysis_type == "Elevation":
                colorlist = self.elevation_analysis(obj.Mesh, ranges)
                self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist)

            elif analysis_type == "Slope":
                colorlist = self.slope_analysis(obj.Mesh, ranges)
                self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist)
    '''
    def getIcon(self):
        """ Return the path to the appropriate icon. """
        return str(os.path.join(DirIcons, "solar-fixed.svg"))

    def getDisplayModes(self, vobj):
        '''
        Return a list of display modes.
        '''
        modes = ["Surface", "Boundary", "Flat Lines", "Shaded", "Wireframe"]

        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 __getstate__(self):
        """
        Save variables to file.
        """
        return None

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


class _EarthWorksTaskPanel:
    def __init__(self):
        self.To = None

        # self.form:
        self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantEarthworks.ui"))
        self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg")))

    def accept(self):
        from datetime import datetime
        starttime = datetime.now()

        import MeshPart as mp
        land = FreeCAD.ActiveDocument.Terrain.Mesh.copy()
        frames = []
        for obj in FreeCADGui.Selection.getSelection():
            if hasattr(obj, "Proxy"):
                if obj.Proxy.Type == "Tracker":
                    if not (obj in frames):
                        frames.append(obj)
                elif obj.Proxy.Type == "FrameArea":
                    for fr in obj.Frames:
                        if not (fr in frames):
                            frames.append(fr)
        if len(frames) == 0:
            return False

        FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras")
        def calculateEarthWorks(line, extreme=False):
            pts = []
            pts1 = []
            line1 = line.copy()
            angles = line.Placement.Rotation.toEulerAngles("XYZ")
            line1.Placement.Rotation.setEulerAngles("XYZ", 0, 0, angles[2])
            line1.Placement.Base.z = 0
            pro = mp.projectShapeOnMesh(line1, land, FreeCAD.Vector(0, 0, 1))
            flat = []
            for points in pro:
                flat.extend(points)
            pro = Part.makePolygon(flat)
            points = pro.discretize(Distance=500)

            for point in points:
                ver = Part.Vertex(point)
                dist = ver.distToShape(line)
                linepoint = dist[1][0][1]

                if not extreme:
                    if self.form.groupTolerances.isChecked():
                        if linepoint.z > point.z:
                            if linepoint.sub(point).Length > self.form.editToleranceCut.value():
                                pts.append(linepoint)
                        elif linepoint.z < point.z:
                            if linepoint.sub(point).Length > self.form.editToleranceFill.value():
                                pts1.append(linepoint)
                    else:
                        if linepoint.z > point.z:
                            pts.append(linepoint)
                        elif linepoint.z < point.z:
                            pts1.append(linepoint)
                        #pts.append(linepoint)
                else:
                    if linepoint.z > point.z:
                        if linepoint.sub(point).Length > 200:
                            pts.append(linepoint)

            return pts, pts1

        tools = [[],[]]
        ver = 2
        if ver == 0:
            frames = sorted(frames, key=lambda x: (x.Placement.Base.x, x.Placement.Base.y))
            for frame in frames:
                length = frame.Setup.Length.Value / 2
                p1 = FreeCAD.Vector(-length, 0, 0)
                p2 = FreeCAD.Vector(length, 0, 0)
                line = Part.LineSegment(p1, p2).toShape()
                line.Placement = frame.Placement.copy()
                line.Placement.Base.x = frame.Shape.BoundBox.XMin
                step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2
                for n in range(3):
                    ret = calculateEarthWorks(line, n % 2)
                    tools[0].extend(ret[0])
                    tools[1].extend(ret[1])
                    line.Placement.Base.x += step
        elif ver == 1:
            from PVPlantPlacement import getCols
            columns = getCols(frames)

            '''colelements = set()
            rowelements = set()
            for groups in columns:
                for group in groups:
                    for frame in group:
                        colelements.add(frame.Placement.Base.x)
                        rowelements.add(frame.Placement.Base.y)
            colelements = sorted(colelements)
            rowelements = sorted(rowelements, reverse=True)
            print("Cols: ", len(colelements), " - ", colelements)
            print("Rows: ", len(rowelements), " - ", rowelements)

            a = []
            colnum = len(colelements)
            for r in range(len(rowelements)):
                a.append([None] * colnum)
            mat = np.array(a, dtype=object)
            for groups in columns:
                for group in groups:
                    for frame in group:
                        colidx = colelements.index(frame.Placement.Base.x)
                        rowidx = rowelements.index(frame.Placement.Base.y)
                        mat[rowidx][colidx] = frame
            print(mat)
            return'''

            for groups in columns:
                for group in groups:
                    first = group[0]
                    last = group[-1]
                    for frame in group:
                        length = frame.Setup.Length.Value / 2
                        p1 = FreeCAD.Vector(-(length + (self.form.editOffset.value() if frame == first else -1000)),
                                            0, 0)
                        p2 = FreeCAD.Vector(length + (self.form.editOffset.value() if frame == last else -1000),
                                            0, 0)
                        line = Part.LineSegment(p1, p2).toShape()
                        line.Placement = frame.Placement.copy()
                        line.Placement.Base.x = frame.Shape.BoundBox.XMin
                        step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2
                        for n in range(3):
                            ret = calculateEarthWorks(line, n % 2 == 1)
                            tools[0].extend(ret[0])
                            tools[1].extend(ret[1])
                            line.Placement.Base.x += step
        elif ver == 2:
            print("versión 2")
            import PVPlantPlacement
            rows, columns = PVPlantPlacement.getRows(frames)
            if (rows is None) or (columns is None):
                print("Nada que procesar")
                return False
            tools = []
            lofts = []
            for group in rows:
                lines = []
                cont = 0
                while cont < len(group):
                    aw = 0
                    if cont > 0:
                        p0 = FreeCAD.Vector(group[cont - 1].Placement.Base)
                        p1 = FreeCAD.Vector(group[cont].Placement.Base)
                        aw = getAngle(p0, p1)

                    ae = 0
                    if cont < (len(group) - 1):
                        p1 = FreeCAD.Vector(group[cont].Placement.Base)
                        p2 = FreeCAD.Vector(group[cont + 1].Placement.Base)
                        ae = getAngle(p1, p2)

                    lng = int(group[cont].Setup.Length / 2)
                    wdt = int(group[cont].Setup.Width / 2)
                    line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0),
                                            FreeCAD.Vector(lng, 0, 0)).toShape()

                    line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0),
                                            FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape()

                    anf = (aw + ae) / 2
                    if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value:
                        anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value
                    zz = wdt * math.sin(math.radians(anf))

                    li = line.copy()
                    li.Placement = group[cont].Placement
                    li.Placement.Rotation = group[cont].Placement.Rotation
                    li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0))
                    li.Placement.Base.z -= zz
                    lines.append(li)

                    ld = line.copy()
                    ld.Placement = group[cont].Placement
                    ld.Placement.Rotation = group[cont].Placement.Rotation
                    ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0))
                    ld.Placement.Base.z += zz
                    lines.append(ld)
                    tools.append([group[cont], li, ld])
                    cont += 1
                loft = Part.makeLoft(lines, False, True, False)
                lofts.append(loft)

            for group in rows:
                lines = []
                for frame in group:
                    col, idx = searchFrameInColumns(frame, columns)
                    tool = searchTool(frame, tools)
                    if idx == 0:
                        ''' '''

                    if idx == (len(col) - 1):
                        ''' '''

                    if (idx + 1) < len(col):
                        frame1 = col[idx + 1]
                        tool1 = searchTool(frame1, tools)
                        line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape()
                        lines.append(line)
                        line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape()
                        lines.append(line)

                if len(lines) > 0:
                    loft = Part.makeLoft(lines, False, True, False)
                    lofts.append(loft)

            faces = []
            for loft in lofts:
                faces.extend(loft.Faces)
            sh = Part.makeShell(faces)
            import Utils.PVPlantUtils as utils
            import Mesh
            pro = utils.getProjected(sh)
            pro = utils.simplifyWire(pro)
            #pro = pro.makeOffset2D(20000, 2, False, False, True)
            Part.show(sh, "loft")
            Part.show(pro, "pro")
            pts = [ver.Point for ver in pro.Vertexes]
            '''if pts[0] != pts[-1]:
                pts.append(pts[0])'''
            land.trim(pts, 1)
            tmp = []
            for face in sh.Faces:
                wire = face.Wires[0].copy()
                pl = wire.Placement.Base
                wire.Placement.Base = wire.Placement.Base - pl
                wire = wire.scale(2)
                wire.Placement.Base = wire.Placement.Base + pl
                #wire = wire.makeOffset2D(10000, 0, False, False, True)
                wire.Placement.Base.z = wire.Placement.Base.z - 10000
                face1 = Part.makeLoft([face.Wires[0], wire], True, True, False)


                Part.show(face1, "tool")
                #tmp.append(face.extrude(FreeCAD.Vector(0, 0, -10000)))
                #Part.show(tmp[-1], "face-extrude")
            sh = sh.extrude(FreeCAD.Vector(0, 0, -10000))
            sh = Part.Solid(sh)
            Part.show(sh)
            import MeshPart as mp
            msh = mp.meshFromShape(Shape=sh)  # , MaxLength=1)
            # msh = msh.smooth("Laplace", 3)
            Mesh.show(msh, "tool")
            Mesh.show(land, "trim")
            '''inner = msh.inner(land)
            Mesh.show(inner)
            outer = msh.inner(land)
            Mesh.show(outer)'''
            '''intersec = land.section(msh, MinDist=0.01)
            import Draft
            for sec in intersec:
                Draft.makeWire(sec)'''

            FreeCAD.ActiveDocument.commitTransaction()
            self.closeForm()
            return True

        import MeshTools.Triangulation as TriangulateMesh
        import MeshTools.MeshGetBoundary as mgb
        import Mesh

        for ind, points in enumerate(tools):
            mesh = TriangulateMesh.Triangulate(points, MaxlengthLE=3000, MaxAngleLE=math.radians(100))
            if mesh:
                for mesh in mesh.getSeparateComponents():
                    boundary = mgb.get_boundary(mesh)
                    Part.show(boundary)
                    '''if self.form.editOffset.value() != 0:
                        import Utils.PVPlantUtils as utils
                        pro = utils.getProjected(boundary)
                        pro = pro.makeOffset2D(self.form.editOffset.value(), 0, False, False, True)
                        # TODO: paso intermedio de restar las areas prohibidas
                        pro = mp.projectShapeOnMesh(pro, land, FreeCAD.Vector(0, 0, 1))
                        cnt = 0
                        for lp in pro:
                            cnt += len(lp)
                        # points.extend(boundary.Wires[0].discretize(Number=cnt))
                        points = boundary.Wires[0].discretize(Distance=cnt)
                        for lp in pro:
                            points.extend(lp)
                        mesh1 = TriangulateMesh.Triangulate(points, MaxlengthLE=5000)  # , MaxAngleLE=math.pi / 1.334)
                        import Mesh
                        Mesh.show(mesh1)
                        boundary = Part.makeCompound([])
                        for section in pro:
                            if len(section) > 0:
                                try:
                                    boundary.add(Part.makePolygon(section))
                                except:
                                    pass
                    Part.show(boundary)'''
                    #mesh.smooth("Laplace", 3)
                    #Mesh.show(mesh)
                    #Part.show(boundary)
                    vol = makeEarthWorksVolume(ind)
                    vol.VolumeMesh = mesh.copy()
                    if ind == 0:
                        ''' put inside fills group '''
                    else:
                        ''' put inside fills group '''

        FreeCAD.ActiveDocument.commitTransaction()
        self.closeForm()
        return True

    def reject(self):
        self.closeForm()
        return True

    def closeForm(self):
        FreeCADGui.Control.closeDialog()


def getAngle(vec1, vec2):
    dX = vec2.x - vec1.x
    dZ = vec2.z - vec1.z
    return math.degrees(math.atan2(float(dZ), float(dX)))


def searchFrameInColumns(obj, columns):
    for colidx, col in enumerate(columns):
        for group in col:
            if obj in group:
                return group, group.index(obj) #groupidx


def searchTool(obj, tools):
    for tool in tools:
        if obj in tool:
            return tool


class _CommandCalculateEarthworks:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "pico.svg")),
                'Accel': "C, E",
                'MenuText': QT_TRANSLATE_NOOP("Placement", "Movimiento de tierras"),
                'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el movimiento de tierras")}

    def Activated(self):
        TaskPanel = _EarthWorksTaskPanel()
        FreeCADGui.Control.showDialog(TaskPanel)

    def IsActive(self):
        active = not (FreeCAD.ActiveDocument is None)
        if not (FreeCAD.ActiveDocument.getObject("Terrain") is None):
            active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
        return active

if FreeCAD.GuiUp:
    FreeCADGui.addCommand('PVPlantEarthworks', _CommandCalculateEarthworks())


def accept():
    import MeshPart as mp
    land = FreeCAD.ActiveDocument.Terrain.Mesh
    frames = []
    for obj in FreeCADGui.Selection.getSelection():
        if hasattr(obj, "Proxy"):
            if obj.Proxy.Type == "Tracker":
                if not (obj in frames):
                    frames.append(obj)
            elif obj.Proxy.Type == "FrameArea":
                for fr in obj.Frames:
                    if not (fr in frames):
                        frames.append(fr)
    if len(frames) == 0:
        return False

    FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras")
    import PVPlantPlacement
    rows, columns = PVPlantPlacement.getRows(frames)
    if (rows is None) or (columns is None):
        print("Nada que procesar")
        return False
    tools = []

    for group in rows:
        lines = []
        cont = 0
        while cont < len(group):
            aw = 0
            if cont > 0:
                p0 = FreeCAD.Vector(group[cont - 1].Placement.Base)
                p1 = FreeCAD.Vector(group[cont].Placement.Base)
                aw = getAngle(p0, p1)

            ae = 0
            if cont < (len(group) - 1):
                p1 = FreeCAD.Vector(group[cont].Placement.Base)
                p2 = FreeCAD.Vector(group[cont + 1].Placement.Base)
                ae = getAngle(p1, p2)

            lng = int(group[cont].Setup.Length / 2)
            wdt = int(group[cont].Setup.Width / 2)
            line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0),
                                    FreeCAD.Vector(lng, 0, 0)).toShape()

            line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0),
                                    FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape()

            anf = (aw + ae) / 2
            if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value:
                anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value
            zz = wdt * math.sin(math.radians(anf))

            li = line.copy()
            li.Placement = group[cont].Placement
            li.Placement.Rotation = group[cont].Placement.Rotation
            li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0))
            li.Placement.Base.z -= zz
            lines.append(li)

            ld = line.copy()
            ld.Placement = group[cont].Placement
            ld.Placement.Rotation = group[cont].Placement.Rotation
            ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0))
            ld.Placement.Base.z += zz
            lines.append(ld)
            tools.append([group[cont], li, ld])
            cont += 1

        loft = Part.makeLoft(lines, False, True, False)
        import MeshPart as mp
        msh = mp.meshFromShape(Shape=loft)  #, MaxLength=1)
        #msh = msh.smooth("Laplace", 3)
        import Mesh
        Mesh.show(msh)
        '''intersec = land.section(msh, MinDist=0.01)
        import Draft
        for sec in intersec:
            Draft.makeWire(sec)'''

    for group in rows:
        lines = []
        for frame in group:
            col, idx = searchFrameInColumns(frame, columns)
            tool = searchTool(frame, tools)
            if idx == 0:
                ''' '''
            if idx == (len(col) - 1):
                ''' '''

            if (idx + 1) < len(col):
                frame1 = col[idx + 1]
                tool1 = searchTool(frame1, tools)
                line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape()
                Part.show(line)
                lines.append(line)
                line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape()
                Part.show(line)
                lines.append(line)


        if len(lines) > 0:
            loft = Part.makeLoft(lines, False, True, False)
            import MeshPart as mp
            msh = mp.meshFromShape(Shape=loft)  # , MaxLength=1)
            #msh = msh.smooth("Laplace", 3)
            import Mesh
            Mesh.show(msh)
            intersec = land.section(msh, MinDist=0.01)
            import Draft
            for sec in intersec:
                Draft.makeWire(sec)

    FreeCAD.ActiveDocument.commitTransaction()
    self.closeForm()
    return True