# /**********************************************************************
# *                                                                     *
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com>  *
# *                                                                     *
# * 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                                                                 *
# *                                                                     *
# ***********************************************************************

import copy
import math

import ArchComponent
import Draft
import FreeCAD
import Part

import PVPlantFencePost
import PVPlantSite

if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore, QtGui
    from PySide.QtCore import QT_TRANSLATE_NOOP
    import PySide.QtGui as QtGui
    from pivy import coin
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 os
from PVPlantResources import DirIcons as DirIcons

EAST = FreeCAD.Vector(1, 0, 0)

def makeprojection(pathwire):
    site = FreeCAD.ActiveDocument.Site
    land = site.Terrain.Shape
    proj = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))
    return proj

def makePVPlantFence(section, post, path):
    obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence')

    _Fence(obj)
    obj.Post = post
    obj.Base = path

    if FreeCAD.GuiUp:
        _ViewProviderFence(obj.ViewObject)

        hide(section)
        hide(post)
        hide(path)

    FreeCAD.ActiveDocument.recompute()
    return obj

def hide(obj):
    if hasattr(obj, 'ViewObject') and obj.ViewObject:
        obj.ViewObject.Visibility = False

def getAngle(Line1, Line2):
    v1 = Line1.Vertexes[1].Point - Line1.Vertexes[0].Point
    v2 = Line2.Vertexes[1].Point - Line2.Vertexes[0].Point
    return v1.getAngle(v2)


def get_parameter_from_v0(edge, offset):
    """Return parameter at distance offset from edge.Vertexes[0].
    sb method in Part.TopoShapeEdge???
    """
    import DraftVecUtils

    lpt = edge.valueAt(edge.getParameterByLength(0))
    vpt = edge.Vertexes[0].Point

    if not DraftVecUtils.equals(vpt, lpt):
        # this edge is flipped
        length = edge.Length - offset
    else:
        # this edge is right way around
        length = offset
    return edge.getParameterByLength(length)


def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal=None):
    """Orient shape to tangent at parm offset along edge."""
    import functools
    import DraftVecUtils
    # http://en.wikipedia.org/wiki/Euler_angles
    # start with null Placement point so translate goes to right place.

    placement = FreeCAD.Placement()
    placement.Rotation = globalRotation
    placement.move(RefPt + xlate)

    if not align:
        return placement

    # unit +Z  Probably defined elsewhere?
    z = FreeCAD.Vector(0, 0, 1)
    # y = FreeCAD.Vector(0, 1, 0)               # unit +Y
    x = FreeCAD.Vector(1, 0, 0)  # unit +X
    nullv = FreeCAD.Vector(0, 0, 0)

    # get local coord system - tangent, normal, binormal, if possible
    t = edge.tangentAt(get_parameter_from_v0(edge, offset))
    t.normalize()
    n = normal
    b = t.cross(n)
    b.normalize()

    lnodes = z.cross(b)
    try:
        # Can't normalize null vector.
        lnodes.normalize()
    except:
        # pathological cases:
        pass

    print(b, " - ", b.dot(z))
    if abs(b.dot(z)) == 1.0:  # 2) binormal is || z
        # align shape to tangent only
        psi = math.degrees(DraftVecUtils.angle(x, t, z))
        theta = 0.0
        phi = 0.0
        FreeCAD.Console.PrintWarning("Draft PathArray.orientShape - Gimbal lock. Infinite lnodes. Change Path or Base.\n")
    else:  # regular case
        psi = math.degrees(DraftVecUtils.angle(x, lnodes, z))
        theta = math.degrees(DraftVecUtils.angle(z, b, lnodes))
        phi = math.degrees(DraftVecUtils.angle(lnodes, t, b))

    rotations = [placement.Rotation]

    if psi != 0.0:
        rotations.insert(0, FreeCAD.Rotation(z, psi))
    if theta != 0.0:
        rotations.insert(0, FreeCAD.Rotation(lnodes, theta))
    if phi != 0.0:
        rotations.insert(0, FreeCAD.Rotation(b, phi))

    if len(rotations) == 1:
        finalRotation = rotations[0]
    else:
        finalRotation = functools.reduce(lambda rot1, rot2: rot1.multiply(rot2), rotations)

    placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), finalRotation.toEuler()[2])

    return placement

def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
    """ Calculates the placements of a shape along a given path so that each copy will be distributed evenly
        - shapeRotation: rotation offset
        - pathwire: path where place the shapes
        - count: number of sections
        - xlate: transformation offset
    """
    import Part
    import DraftGeomUtils

    closedpath = DraftGeomUtils.isReallyClosed(pathwire)
    normal = DraftGeomUtils.getNormal(pathwire)
    if normal:
        if normal.z < 0:  # asegurarse de que siempre se dibuje por encima del suelo
            normal.z *= -1
    else:
        normal = FreeCAD.Vector(0, 0, 1)
    path = Part.__sortEdges__(pathwire.Edges)
    ends = []
    cdist = 0

    # -----------------------------------------------------------------------------------------------------------------
    totalEdges = len(path)
    if not closedpath:
        totalEdges -= 1
    # -----------------------------------------------------------------------------------------------------------------

    for e in path:  # find cumulative edge end distance
        cdist += e.Length
        ends.append(cdist)

    placements = []
    # place the start shape
    pt = path[0].Vertexes[0].Point
    placements.append(calculatePlacement(shapeRotation, path[0], 0, pt, xlate, align, normal))

    # closed path doesn't need shape on last vertex
    if not closedpath:
        # place the end shape
        pt = path[-1].Vertexes[-1].Point
        placements.append(calculatePlacement(shapeRotation, path[-1], path[-1].Length, pt, xlate, align, normal))

    if count < 3:
        return placements

    # place the middle shapes
    if closedpath:
        stop = count
    else:
        stop = count - 1
    step = float(cdist) / stop
    travel = step
    for i in range(1, stop):
        # which edge in path should contain this shape?
        # avoids problems with float math travel > ends[-1]
        iend = len(ends) - 1
        for j in range(0, len(ends)):
            if travel <= ends[j]:
                iend = j
                break
        # place shape at proper spot on proper edge
        remains = ends[iend] - travel
        offset = path[iend].Length - remains
        pt = path[iend].valueAt(get_parameter_from_v0(path[iend], offset))
        placements.append(calculatePlacement(shapeRotation, path[iend], offset, pt, xlate, align, normal))
        travel += step

    return placements

class _Fence(ArchComponent.Component):
    def __init__(self, obj):
        ArchComponent.Component.__init__(self, obj)
        self.setProperties(obj)
        self.Posts = []
        self.Foundations = []

        # Does a IfcType exist?
        # obj.IfcType = "Fence"

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

        pl = obj.PropertiesList

        if not "Gate" in pl:
            obj.addProperty("App::PropertyLinkList",
                            "Gate",
                            "Fence",
                            "A single fence post")

        if not "Post" in pl:
            obj.addProperty("App::PropertyLink",
                            "Post",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post"))

        if not "Foundation" in pl:
            obj.addProperty("App::PropertyLength",
                            "Foundation",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post"))

        if not "Depth" in pl:
            obj.addProperty("App::PropertyLength",
                            "Depth",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Depth = 650

        if not "Gap" in pl:
            obj.addProperty("App::PropertyLength",
                            "Gap",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Gap = 4000

        if not "Angle" in pl:
            obj.addProperty("App::PropertyAngle",
                            "Angle",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Angle = 60

        if not "MeshOffsetZ" in pl:
            obj.addProperty("App::PropertyLength",
                            "MeshOffsetZ",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshOffsetZ = 0

        if not "MeshHeight" in pl:
            obj.addProperty("App::PropertyLength",
                            "MeshHeight",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshHeight = 2000

        if not "Reinforce" in pl:
            obj.addProperty("App::PropertyLength",
                            "Reinforce",
                            "Fence",
                            QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Reinforce = 50000


        ########## Datos informativos:
        if not "NumberOfSections" in pl:
            obj.addProperty("App::PropertyQuantity",
                            "NumberOfSections",
                            "Output",
                            QT_TRANSLATE_NOOP("App::Property", "The number of sections the fence is built of"))
            obj.setEditorMode("NumberOfSections", 1)

        if not "NumberOfPosts" in pl:
            obj.addProperty("App::PropertyQuantity",
                            "NumberOfPosts",
                            "Output",
                            QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
            obj.setEditorMode("NumberOfPosts", 1)

        if not "Length" in pl:
            obj.addProperty("App::PropertyLength",
                            "Length",
                            "Output",
                            QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
            obj.setEditorMode("Length", 1)

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

        if not "PlacementList" in pl:
            obj.addProperty("App::PropertyPlacementList",
                            "PlacementList",
                            "Output",
                            QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
            obj.setEditorMode("Length", 1)


        self.Type = "PVPlatFence"

    def __getstate__(self):
        if hasattr(self, 'sectionFaceNumbers'):
            return (self.sectionFaceNumbers)
        return None

    def __setstate__(self, state):
        if state is not None and isinstance(state, tuple):
            self.sectionFaceNumbers = state[0]
        return None

    def execute(self, obj):

        pathwire = self.calculatePathWire(obj)
        if pathwire is None:
            # FreeCAD.Console.PrintLog("ArchFence.execute: path " + obj.Base.Name + " has no edges\n")
            return

        if not obj.Post:
            FreeCAD.Console.PrintLog("ArchFence.execute: Post not set\n")
            return

        self.Posts = []
        self.Foundations = []
        site = PVPlantSite.get()
        if True:  # prueba
            import MeshPart as mp
            land = FreeCAD.ActiveDocument.Terrain.Mesh
            segments = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))
            points=[]
            for segment in segments:
                points.extend(segment)
            pathwire = Part.makePolygon(points)
        else:
            if PVPlantSite.get().Terrain.TypeId == 'Mesh::Feature':
                import MeshPart as mp
                land = PVPlantSite.get().Terrain.Mesh
                pathwire = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))

            else:
                land = site.Terrain.Shape
                pathwire = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))

        if pathwire is None:
            return

        ''' no sirve:
        if len(pathwire.Wires) > 1:
            import draftgeoutils
            pathwire = draftgeoutils.wires.superWire(pathwire.Edges, True)
            Part.show(pathwire)
        '''

        ''' unir todas en una '''
        '''
        if len(pathwire.Wires) > 1:
            import Utils.PVPlantUtils as utils
            wires = pathwire.Wires
            new_wire = []
            to_compare = utils.getPoints(wires.pop(0))
            new_wire.extend(to_compare)
            while len(wires)>0:
                wire = wires[0]
                points = utils.getPoints(wire)
                to_remove = None
                if points[0] in to_compare:
                    to_remove = points[0]
                if points[-1] in to_compare:
                    to_remove = points[-1]
                if to_remove:
                    to_compare = points.copy()
                    points.remove(to_remove)
                    new_wire.extend(points)
                    wires.pop()
                    continue
                wires.append(wires.pop())
            pathwire = Part.makePolygon(new_wire)
            #Part.show(pathwire)
        #return
        '''

        sectionLength = obj.Gap.Value
        postLength = 0 #obj.Post.Diameter.Value   #considerarlo 0 porque no influye
        postPlacements = []
        pathsegments = self.calculateSegments(obj, pathwire)

        count = 0
        drawFirstPost = True
        pathLength = 0
        for segment in pathsegments:
            segwire = Part.Wire(Part.__sortEdges__(segment))
            pathLength += segwire.Length

            obj.NumberOfSections = self.calculateNumberOfSections(segwire.Length, sectionLength, postLength)
            obj.NumberOfPosts = obj.NumberOfSections + 1

            count += obj.NumberOfSections

            downRotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), -90)
            placements = self.calculatePostPlacements(obj, segwire, downRotation)

            if drawFirstPost:
                drawFirstPost = False
            else:
                placements.pop(0)

            postPlacements.extend(placements)

        postShapes, postFoundation = self.calculatePosts(obj, postPlacements)
        sections, num = self.calculateSections(obj, postPlacements)

        postShapes = Part.makeCompound(postShapes)
        postFoundation = Part.makeCompound(postFoundation)
        sections = Part.makeCompound(sections)
        compound = Part.makeCompound([postShapes, postFoundation, sections])
        obj.Shape = compound

        # Give information
        obj.NumberOfSections = count
        obj.NumberOfPosts = obj.NumberOfSections + 1
        obj.Length = pathLength
        obj.Concrete = count * postFoundation.SubShapes[0].Volume

    def calculateSegments(self, obj, pathwire):
        ''' Calcular los segmentos de la ruta '''
        import math
        segments = []
        segment = [pathwire.Edges[0]]
        segments.append(segment)
        for ind in range(len(pathwire.Edges) - 1):
            ed1 = pathwire.Edges[ind]
            ed2 = pathwire.Edges[ind + 1]
            vec1 = ed1.Vertexes[1].Point - ed1.Vertexes[0].Point
            vec2 = ed2.Vertexes[1].Point - ed2.Vertexes[0].Point
            angle = math.degrees(vec1.getAngle(vec2))
            if angle > obj.Angle.Value:
                segment = []
                segments.append(segment)
            segment.append(ed2)
        return segments

    def calculateNumberOfSections(self, pathLength, sectionLength, postLength):
        withoutLastPost = pathLength - postLength
        realSectionLength = sectionLength + postLength
        return math.ceil(withoutLastPost / realSectionLength)

    def calculatePostPlacements(self, obj, pathwire, rotation):
        postWidth = obj.Post.Diameter.Value
        transformationVector = FreeCAD.Vector(0, postWidth / 2, 0)
        placements = calculatePlacementsOnPath(rotation, pathwire, int(obj.NumberOfSections) + 1, transformationVector, True)
        # The placement of the last object is always the second entry in the list.
        # So we move it to the end:
        if len(placements) > 1:
            placements.append(placements.pop(1))
        return placements

    def calculatePosts(self, obj, postPlacements):
        posts = []
        foundations = []
        for placement in postPlacements:
            postCopy = obj.Post.Shape.copy()
            postCopy = Part.Solid(postCopy)
            postCopy.Placement = placement
            postCopy.Placement.Base.z += 100
            posts.append(postCopy)

            foundation = Part.makeCylinder(150, 700)
            foundation.Placement = placement
            foundation.Placement.Base.z -= obj.Depth.Value
            foundation = foundation.cut(postCopy)
            foundations.append(foundation)

        return posts, foundations

    def calculateSections(self, obj, postPlacements):
        shapes = []
        faceNumbers = []

        offsetz = obj.MeshOffsetZ.Value
        meshHeight = obj.MeshHeight.Value
        for i in range(len(postPlacements) - 1):
            startPlacement = postPlacements[i]
            endPlacement = postPlacements[i + 1]

            p1 = startPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
            p2 = endPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
            p3 = p2 + FreeCAD.Vector(0, 0, meshHeight)
            p4 = p1 + FreeCAD.Vector(0, 0, meshHeight)
            pointlist = [p1, p2, p3, p4, p1]

            try:
                pol = Part.makePolygon(pointlist)
                face = Part.Face(pol)
                shapes.append(face)
                faceNumbers.append(1)
            except:
                print("No es posible crear la cara: ---------------------------------------------------")
                print("  +++++ Start: ", startPlacement.Base, " - end: ", endPlacement.Base)
                print("  +++++ algo: ", pointlist, "\n")
                print("---------------------------------------------------\n")
        return (shapes, faceNumbers)

    def calculatePathWire(self, obj):
        if obj.Base:
            wire = None
            if hasattr(obj.Base.Shape, 'Wires') and obj.Base.Shape.Wires:
                wire = obj.Base.Shape.Wires[0]
            elif obj.Base.Shape.Edges:
                wire = Part.Wire(obj.Base.Shape.Edges)
            return wire
        return None


class _ViewProviderFence(ArchComponent.ViewProviderComponent):
    "A View Provider for the Fence object"

    def __init__(self, vobj):
        ArchComponent.ViewProviderComponent.__init__(self, vobj)
        vobj.Proxy = self
        #vobj.addExtension("Gui::ViewProviderGroupExtensionPython")

    def getIcon(self):
        return str(os.path.join(DirIcons, "fence.svg"))

    def attach(self, vobj):
        '''
        Create Object visuals in 3D view.
        '''

        self.Object = vobj.Object

        # 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()

        # Face root.
        faces = coin.SoSeparator()
        faces.addChild(shape_hints)
        faces.addChild(self.face_material)
        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'
        faces.addChild(shape_hints)
        highlight.addChild(self.edge_material)
        highlight.addChild(mat_binding)
        highlight.addChild(self.edge_style)
        highlight.addChild(self.geo_coords)
        highlight.addChild(self.triangles)

    def updateData(self, obj, prop):
        '''
        Update Object visuals when a data property changed.
        '''
        origin = PVPlantSite.get()
        base = copy.deepcopy(origin.Origin)
        base.z = 0
        #print("  - Propiedad: ", prop)
        if prop == "Shape":
            shape = obj.getPropertyByName(prop)
            # Get GeoOrigin.
            points = [ver.Point for ver in shape.Vertexes]
            # Set GeoCoords.
            geo_system = ["UTM", origin.UtmZone, "FLAT"]
            self.geo_coords.geoSystem.setValues(geo_system)
            self.geo_coords.point.values = points

    def claimChildren(self):
        children = []
        if self.Object.Post:
            children.append(self.Object.Post)
        if self.Object.Base:
            children.append(self.Object.Base)
        if self.Object.Gate:
            children.append(self.Object.Gate)
        return children

class _FenceTaskPanel:
    '''The TaskPanel to setup the fence'''

    def __init__(self):
        self.section = None
        self.post = None
        self.path = None

        # form: -----------------------------------------------------------------------------------
        self.formFence = QtGui.QWidget()
        self.formFence.resize(800, 640)
        self.formFence.setWindowTitle("Fence setup")
        self.formFence.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
        self.grid = QtGui.QGridLayout(self.formFence)

        # parameters
        self.labelPath = QtGui.QLabel()
        self.labelPath.setText("Recorrido:")
        self.linePath = QtGui.QLineEdit(self.formFence)
        self.linePath.setObjectName(_fromUtf8("lineEdit1"))
        self.linePath.readOnly = True
        self.grid.addWidget(self.labelPath, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.linePath, self.grid.rowCount() - 1, 1, 1, 1)

        self.buttonPathSelect = QtGui.QPushButton('Add')
        self.grid.addWidget(self.buttonPathSelect, self.grid.rowCount() - 1, 2, 1, 1)

        self.line1 = QtGui.QFrame()
        self.line1.setFrameShape(QtGui.QFrame.HLine)
        self.line1.setFrameShadow(QtGui.QFrame.Sunken)
        self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1)

        self.label = QtGui.QLabel()
        self.label.setText("Separación entre apoyos:")
        self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1)

        self.labelInterval = QtGui.QLabel()
        self.labelInterval.setText("Intervalo:")
        self.valueInterval = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueInterval.setText("4,0 m")
        self.grid.addWidget(self.labelInterval, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueInterval, self.grid.rowCount() - 1, 1, 1, 2)

        self.line1 = QtGui.QFrame()
        self.line1.setFrameShape(QtGui.QFrame.HLine)
        self.line1.setFrameShadow(QtGui.QFrame.Sunken)
        self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1)

        self.label = QtGui.QLabel()
        self.label.setText("Mayado:")
        self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1)

        self.labelHeight = QtGui.QLabel()
        self.labelHeight.setText("Altura:")
        self.valueHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueHeight.setText("2 m")
        self.grid.addWidget(self.labelHeight, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueHeight, self.grid.rowCount() - 1, 1, 1, 2)

        self.labelOffset = QtGui.QLabel()
        self.labelOffset.setText("Separación del suelo:")
        self.valueOffset = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueOffset.setText("0 m")
        self.grid.addWidget(self.labelOffset, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueOffset, self.grid.rowCount() - 1, 1, 1, 2)

        self.buttonPathSelect.clicked.connect(self.addPath)
        self.valueInterval.valueChanged.connect(self.SetupGrid)
        self.valueHeight.valueChanged.connect(self.SetupGrid)
        # self.valueDepth.valueChanged.connect(self.SetupPost)

        # Form para configurar el poste: -------------------------------------------------------------------
        self.formPost = QtGui.QWidget()
        self.formPost.resize(800, 640)
        self.formPost.setWindowTitle("Post setup")
        self.formPost.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
        self.grid = QtGui.QGridLayout(self.formPost)

        # parameters
        self.labelDiameter = QtGui.QLabel()
        self.labelDiameter.setText("Diámetro:")
        self.valueDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueDiameter.setText("48 mm")
        self.grid.addWidget(self.labelDiameter, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueDiameter, self.grid.rowCount() - 1, 1, 1, 1)

        self.labelLength = QtGui.QLabel()
        self.labelLength.setText("Longitud:")
        self.valueLength = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueLength.setText("3,0 m")
        self.grid.addWidget(self.labelLength, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueLength, self.grid.rowCount() - 1, 1, 1, 1)

        self.labelDepth = QtGui.QLabel()
        self.labelDepth.setText("Profundidad:")
        self.valueDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueDepth.setText("700,0 mm")
        self.grid.addWidget(self.labelDepth, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueDepth, self.grid.rowCount() - 1, 1, 1, 1)

        self.valueDiameter.valueChanged.connect(self.SetupPost)
        self.valueLength.valueChanged.connect(self.SetupPost)
        self.valueDepth.valueChanged.connect(self.SetupPost)

        # Form para configurar la zapata: ----------------------------------------------------------
        self.formFoundation = QtGui.QWidget()
        self.formFoundation.resize(800, 640)
        self.formFoundation.setWindowTitle("Post setup")
        self.formFoundation.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
        self.grid = QtGui.QGridLayout(self.formFoundation)

        # parameters
        self.labelFoundationDiameter = QtGui.QLabel()
        self.labelFoundationDiameter.setText("Cimentación:")
        self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1)

        self.labelFoundationDiameter = QtGui.QLabel()
        self.labelFoundationDiameter.setText("Diámetro:")
        self.valueFoundationDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueFoundationDiameter.setText("200,0 mm")
        self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueFoundationDiameter, self.grid.rowCount() - 1, 1, 1, 1)

        self.labelFoundationDepth = QtGui.QLabel()
        self.labelFoundationDepth.setText("Profundidad:")
        self.valueFoundationDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField")
        self.valueFoundationDepth.setText("700,0 mm")
        self.grid.addWidget(self.labelFoundationDepth, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.valueFoundationDepth, self.grid.rowCount() - 1, 1, 1, 1)

        self.form = [self.formFence, self.formPost, self.formFoundation]

        # valores iniciales y creación del la valla:
        import Draft
        self.post = PVPlantFencePost.makeFencePost()  # Arch.makePipe()
        self.post.Label = "Post"
        Draft.autogroup(self.post)

        '''
        self.section = self.makeGrid()
        self.path = self.section.Base
        '''

        FreeCAD.ActiveDocument.recompute()
        self.fence = makePVPlantFence(self.section, self.post, self.path)

    def addPath(self):
        sel = FreeCADGui.Selection.getSelection()
        if len(sel) > 0:
            self.path = sel[0]
            self.linePath.setText(self.path.Label)
            self.fence.Base = self.path

        FreeCAD.ActiveDocument.recompute()

    def SetupPost(self):
        if self.post.Diameter != FreeCAD.Units.Quantity(self.valueDiameter.text()).Value:
            self.post.Diameter = FreeCAD.Units.Quantity(self.valueDiameter.text()).Value
        if self.post.Length != FreeCAD.Units.Quantity(self.valueLength.text()).Value:
            self.post.Length = FreeCAD.Units.Quantity(self.valueLength.text()).Value

        if self.post.Placement.Base.z != -FreeCAD.Units.Quantity(self.valueDepth.text()).Value:
            self.post.Placement.Base.z = -FreeCAD.Units.Quantity(self.valueDepth.text()).Value
            self.fence.Depth = FreeCAD.Units.Quantity(self.valueDepth.text()).Value

        FreeCAD.ActiveDocument.recompute()

    def SetupGrid(self):
        return
        if self.path.End.x != FreeCAD.Units.Quantity(self.valueInterval.text()).Value:
            self.path.End.x = FreeCAD.Units.Quantity(self.valueInterval.text()).Value
        if self.section.LengthFwd != FreeCAD.Units.Quantity(self.valueHeight.text()).Value:
            self.section.LengthFwd = FreeCAD.Units.Quantity(self.valueHeight.text()).Value

        FreeCAD.ActiveDocument.recompute()

    def makeGrid(self):
        return None

        import Draft

        p1 = FreeCAD.Vector(0, 0, 0)
        p2 = FreeCAD.Vector(4000, 0, 0)
        line = Draft.makeLine(p1, p2)

        section = FreeCAD.ActiveDocument.addObject('Part::Extrusion', 'Extrude')
        section.Base = line
        section.DirMode = "Custom"
        section.Dir = FreeCAD.Vector(0.0, 0.0, 1.0)
        section.DirLink = None
        section.LengthFwd = 2000.0
        section.LengthRev = 0.0
        section.Solid = False
        section.Reversed = False
        section.Symmetric = False
        section.TaperAngle = 0.0
        section.TaperAngleRev = 0.0
        line.Visibility = False

        return section


# Commands ---------------------------------------------------------------------------------
class _CommandPVPlantFence:
    "the PVPlant Fence command definition"

    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "fence.svg")),
                'Accel': "C, F",
                'MenuText': QT_TRANSLATE_NOOP("PVPlantFence", "Fence"),
                'ToolTip': QT_TRANSLATE_NOOP("PVPlantFence",
                                             "Creates a fence object from a selected section, post and path")}

    def IsActive(self):
        return not FreeCAD.ActiveDocument is None

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


if FreeCAD.GuiUp:
    class CommandFenceGroup:
        def GetCommands(self):
            return tuple(['PVPlantFence',
                          'PVPlantGate',
                          'PVPlantFencePost'
                          ])

        def GetResources(self):
            return {'MenuText': QT_TRANSLATE_NOOP("", 'PVPlantFence'),
                    'ToolTip': QT_TRANSLATE_NOOP("", 'PVPlantFence')
                    }

        def IsActive(self):
            return (not (FreeCAD.ActiveDocument is None) and
                    not (FreeCAD.ActiveDocument.getObject("Site") is None) and
                    not (FreeCAD.ActiveDocument.getObject("Terrain") is None))

    import PVPlantFenceGate
    FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence())
    FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
    FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
    FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())

def movep(obj):
    pl = obj.Shape.BoundBox.Center
    points = []
    for ind in range(len(obj.Shape.Vertexes)):
        points.append(obj.Shape.Vertexes[ind].Point - pl)
    Draft.makeWire(points)