import Draft
import FreeCAD
import Part

if FreeCAD.GuiUp:
    import FreeCADGui
    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 os
from PVPlantResources import DirIcons as DirIcons


def Mest2FemMesh(obj):
    import Fem

    fm = Fem.FemMesh()

    #i = 0
    #nodes = []
    for mp in obj.Mesh.Points:
        fm.addNode(mp.Vector.x, mp.Vector.y, mp.Vector.z)
    #    nodes.append(mp)
    #    i += 1
    for mf in obj.Mesh.Facets:
        fm.addFace([mf.PointIndices[0] + 1, mf.PointIndices[1] + 1, mf.PointIndices[2] + 1])

    obj2 = FreeCAD.ActiveDocument.addObject("Fem::FemMeshObject")
    obj2.FemMesh = fm
    obj2.ViewObject.DisplayMode = "Faces"

    FreeCAD.activeDocument().recompute()
    return obj2


def makeContours(land, minor = 1000, mayor = 5000,
                 minorColor=(0.0, 0.00, 0.80), mayorColor=(0.00, 0.00, 1.00),
                 minorThickness = 2, mayorThickness = 5,
                 filter_size = 5):
    if not land:
        return

    if land.TypeId == 'Mesh::Feature':
        Contours_Mesh(land.Mesh, minor, mayor, minorColor, mayorColor, minorThickness, mayorThickness, filter_size)
    else:
        Contours_Part(land, minor, mayor, minorColor, mayorColor, minorThickness, mayorThickness, filter_size)

    FreeCAD.ActiveDocument.recompute()


def Contours_Mesh(Mesh, minor, mayor,
                  minorColor, mayorColor,
                  minorLineWidth, mayorLineWidth,
                  filter_size): #filter_size de 3 a 21 y siempre impar

    def calculateSection(cuts):
        for inc in cuts:
            CrossSections = Mesh.crossSections([((0, 0, inc), (0, 0, 1))], 0.000001)
            for PointList in CrossSections[0]:
                if len(PointList) > 1:
                    if PointList[0].sub(PointList[-1]).Length <= 10000.0:
                        PointList.append(PointList[0])

                    # 1. Smooth the points
                    if (len(PointList) > filter_size) and (filter_size > 0):
                        for a in range(len(PointList)):
                            x = 0
                            y = 0
                            for p in range(-filter_radius, filter_radius + 1):
                                point_id = a + p
                                if point_id < 0:
                                    point_id = 0

                                if point_id >= len(PointList):
                                    point_id = len(PointList) - 1

                                x += PointList[point_id].x
                                y += PointList[point_id].y

                            x /= filter_size
                            y /= filter_size
                            PointList[a].x = x
                            PointList[a].y = y

                        for a in reversed(range(len(PointList))):
                            x = 0
                            y = 0
                            for p in range(-filter_radius, filter_radius + 1):
                                point_id = a + p
                                if point_id < 0:
                                    point_id = 0

                                if point_id >= len(PointList):
                                    point_id = len(PointList) - 1

                                x += PointList[point_id].x
                                y += PointList[point_id].y

                            x /= filter_size
                            y /= filter_size
                            PointList[a].x = x
                            PointList[a].y = y

                    # 2. Make lines
                    #Contour = Draft.makeWire(PointList, closed=False, face=None)
                    #Contour.Label = str(int(inc / 1000)) + "m"
                    Contour = Part.makePolygon(PointList)
                    Part.show(Contour, str(int(inc / 1000)) + "m")
                    '''Contours.addObject(Contour)
                    if inc % mayor == 0:
                        Contour.ViewObject.LineWidth = mayorLineWidth
                        Contour.ViewObject.LineColor = mayorColor
                    else:
                        Contour.ViewObject.LineWidth = minorLineWidth
                        Contour.ViewObject.LineColor = minorColor'''

                    del Contour, PointList
            del CrossSections

    filter_radius = int(filter_size / 2)
    try:
        Contours = FreeCAD.ActiveDocument.Contours
    except:
        Contours = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Contours')

    ZMin = Mesh.BoundBox.ZMin // 10000
    ZMin *= 10000
    ZMax = Mesh.BoundBox.ZMax

    import numpy as np
    minor_array = np.arange(ZMin, ZMax, minor)
    mayor_array = np.arange(ZMin, ZMax, mayor)
    minor_array = np.array(list(filter(lambda x: x not in mayor_array, minor_array)))
    calculateSection(minor_array)
    calculateSection(mayor_array)


def Contours_Part(Terrain, minor, mayor,
                  minorColor, mayorColor,
                  minorLineWidth, mayorLineWidth,
                  filter_size): #filter_size de 3 a 21 y siempre impar

    def calculateSection(cuts):
        cnt = 0
        for inc in cuts:
            CrossSections = Terrain.Shape.slice(FreeCAD.Vector(0, 0, 1), inc)

            for wire in CrossSections:
                PointList = []
                for vertex in wire.Vertexes:
                    PointList.append(vertex.Point)

                if len(PointList) > 1:
                    # 1. Smooth the points
                    if (len(PointList) > filter_size) and (filter_size > 0):
                        for a in range(len(PointList)):
                            x = 0
                            y = 0
                            for p in range(-filter_radius, filter_radius + 1):
                                point_id = a + p
                                if point_id < 0:
                                    point_id = 0

                                if point_id >= len(PointList):
                                    point_id = len(PointList) - 1

                                x += PointList[point_id].x
                                y += PointList[point_id].y

                            x /= filter_size
                            y /= filter_size
                            PointList[a].x = x
                            PointList[a].y = y

                        for a in reversed(range(len(PointList))):
                            x = 0
                            y = 0
                            for p in range(-filter_radius, filter_radius + 1):
                                point_id = a + p
                                if point_id < 0:
                                    point_id = 0

                                if point_id >= len(PointList):
                                    point_id = len(PointList) - 1

                                x += PointList[point_id].x
                                y += PointList[point_id].y

                            x /= filter_size
                            y /= filter_size
                            PointList[a].x = x
                            PointList[a].y = y
                    # 2. TODO: close wire

                    # 3. Make lines
                    Contour = Draft.makeWire(PointList, closed=False, face=None, support=None)
                    Contour.MakeFace = False
                    Contour.Label = str(int(inc / 1000)) + "m"
                    Contours.addObject(Contour)

                    if inc % mayor == 0:
                        Contour.ViewObject.LineWidth = mayorLineWidth
                        Contour.ViewObject.LineColor = mayorColor
                    else:
                        Contour.ViewObject.LineWidth = minorLineWidth
                        Contour.ViewObject.LineColor = minorColor
            cnt += 1
            if cnt == 10:
                return

    filter_radius = int(filter_size / 2)
    try:
        Contours = FreeCAD.ActiveDocument.Contours
    except:
        Contours = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Contours')

    ZMin = Terrain.Shape.BoundBox.ZMin // 10000
    ZMin *= 10000
    ZMax = Terrain.Shape.BoundBox.ZMax

    import numpy as np
    minor_array = np.arange(ZMin, ZMax, minor)
    mayor_array = np.arange(ZMin, ZMax, mayor)
    minor_array = np.array(list(filter(lambda x: x not in mayor_array, minor_array)))
    calculateSection(minor_array)
    calculateSection(mayor_array)

# Base widget for task panel terrain analisys


class _generalTaskPanel:
    '''The TaskPanel for Slope setup'''

    def __init__(self):
        self.ranges = []

        self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/PVPlantTerrainAnalisys.ui")
        self.tableWidget = self.form.tableWidget
        self.form.editSteps.valueChanged.connect(self.changeDivision)
        self.tableWidget.itemChanged.connect(self.cellChanged)

    def cellChanged(self, item):
        if (item.column() == 1) and (item.row() != (self.tableWidget.rowCount() - 1)):
            item2 = self.tableWidget.item(item.row() + 1, 0)
            item2.setText(item.text())

    def updateTableValues(self):
        maxval = self.form.editTo.value() - self.form.editFrom.value()
        ran = maxval / self.tableWidget.rowCount()

        for i in range(self.tableWidget.rowCount()):
            for j in range(2):
                item = self.tableWidget.item(i, j)
                val = ran * (i + j) + self.form.editFrom.value()
                item.setText('{:.1f}'.format(val))
                self.ranges[i][j] = val

        #self.tableWidget.item(0, 0).setText('{:.1f}'.format(self.form.editFrom.value()))
        #self.tableWidget.item(self.tableWidget.rowCount() - 1, 1).setText('{:.1f}'.format(self.form.editTo.value()))

    def changeDivision(self):
        self.tableWidget.blockSignals(True)
        rows = self.tableWidget.rowCount()
        to = self.form.editSteps.value()
        if to > rows:
            for i in range(0, to - rows):
                self.ranges.append([0.0, 0.0, (0.0, 0.0, 0.0)])
                self.createRow()
        elif to < rows:
            self.tableWidget.setRowCount(to)
            self.ranges = self.ranges[:to]
        self.updateTableValues()
        self.tableWidget.blockSignals(False)

    def createRow(self):
        row = self.tableWidget.rowCount()
        self.tableWidget.setRowCount(self.tableWidget.rowCount() + 1)

        newItem = QtGui.QTableWidgetItem("item")
        self.tableWidget.setItem(row, 0, newItem)
        newItem.setTextAlignment(QtCore.Qt.AlignCenter)
        newItem.setText("0.0")
        newItem.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsDragEnabled|
                         QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsUserCheckable)

        newItem = QtGui.QTableWidgetItem("item")
        self.tableWidget.setItem(row, 1, newItem)
        newItem.setTextAlignment(QtCore.Qt.AlignCenter)
        newItem.setText("0.0")

        #import random
        # r = random.randint(0, 255)
        # g = random.randint(0, 255)
        # b = random.randint(0, 255)

        val = int(127 * row / (self.form.editSteps.value() / 2))
        g = (127 + val) if val <= 127 else 0
        r = (328 - val if val >= 127 else 0)
        color = QtGui.QColor(r, g, 0)
        colorPix = QtGui.QPixmap(16, 16)
        colorPix.fill(color)
        self.ranges[row][2] = (color.red()/255, color.green()/255, color.blue()/255)
        buttonColor = QtGui.QPushButton('')
        buttonColor.setIcon(QtGui.QIcon(colorPix))
        buttonColor.setMaximumSize(QtCore.QSize(20, 20))
        buttonColor.clicked.connect(lambda: self.selColor(buttonColor))
        self.tableWidget.setCellWidget(row, 2, buttonColor)

    def selColor(self, button):
        color = QtGui.QColorDialog.getColor()
        if color.isValid():
            print("añadir color")
            colorPix = QtGui.QPixmap(16, 16)
            colorPix.fill(color)
            button.setIcon(QtGui.QIcon(colorPix))
            curentIndex = self.tableWidget.currentIndex()
            self.ranges[curentIndex.row()][2] = (color.red()/255, color.green()/255, color.blue()/255)

# Contours Analisys: ---------------------------------------------------------------------------------
class ContourTaskPanel():
    '''The editmode TaskPanel for contours generator'''

    def __init__(self):
        self.MinorColor = (0.5, 0.5, 0.5)
        self.MayorColor = (0.5, 0.5, 0.5)
        land = None
        self.intervals = ["0.1 m", "0.5 m", "1 m", "5 m", "10 m", "50 m", "100 m", "500 m"]
        self.intervalvalues = [0.1, 0.5, 1, 5, 10, 50, 100, 500]

        # form:
        self.form = QtGui.QWidget()
        self.form.resize(800,640)
        self.form.setWindowTitle("Curvas de nivel")
        self.form.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg")))
        self.grid = QtGui.QGridLayout(self.form)

        # parameters
        self.labelTerrain = QtGui.QLabel()
        self.labelTerrain.setText("Terreno:")
        self.lineEdit1 = QtGui.QLineEdit(self.form)
        self.lineEdit1.setObjectName(_fromUtf8("lineEdit1"))
        self.lineEdit1.readOnly = True
        self.grid.addWidget(self.labelTerrain, self.grid.rowCount(), 0, 1, 1)
        self.grid.addWidget(self.lineEdit1, self.grid.rowCount() - 1, 1, 1, 1)

        self.buttonAdd = QtGui.QPushButton('Seleccionar Terreno')
        self.buttonAdd.clicked.connect(self.add)
        self.grid.addWidget(self.buttonAdd, self.grid.rowCount(), 1, 1, 1)

        ###----------------------

        self.widgetDivisions = QtGui.QGroupBox(self.form)
        self.grid.addWidget(self.widgetDivisions, self.grid.rowCount(), 0, 1, -1)
        self.gridDivisions = QtGui.QGridLayout(self.widgetDivisions)

        self.labelTitle1 = QtGui.QLabel()
        self.labelTitle1.setText("Intervalo")
        self.labelTitle2 = QtGui.QLabel()
        self.labelTitle2.setText("Grosor")
        self.labelTitle3 = QtGui.QLabel()
        self.labelTitle3.setText("Color")
        self.gridDivisions.addWidget(self.labelTitle1, self.gridDivisions.rowCount(), 1, 1, -1)
        self.gridDivisions.addWidget(self.labelTitle2, self.gridDivisions.rowCount() - 1, 2, 1, -1)
        self.gridDivisions.addWidget(self.labelTitle3, self.gridDivisions.rowCount() - 1, 3, 1, -1)

        self.labelMinorContour = QtGui.QLabel(self.form)
        self.labelMinorContour.setText("Menor:")
        self.inputMinorContourMargin = QtGui.QComboBox(self.form)
        self.inputMinorContourMargin.addItems(self.intervals)
        self.inputMinorContourMargin.setCurrentIndex(2)
        self.inputMinorContourThickness = QtGui.QSpinBox(self.form)
        self.inputMinorContourThickness.setRange(1, 10)
        self.inputMinorContourThickness.setValue(2)

        self.gridDivisions.addWidget(self.labelMinorContour, self.gridDivisions.rowCount(), 0, 1, 1)
        self.gridDivisions.addWidget(self.inputMinorContourMargin, self.gridDivisions.rowCount() - 1, 1, 1, 1)
        self.gridDivisions.addWidget(self.inputMinorContourThickness, self.gridDivisions.rowCount() - 1, 2, 1, 1)

        self.buttonMinorContourColor = QtGui.QPushButton('')
        self.color = QtGui.QColor(128, 128, 128)
        colorPix = QtGui.QPixmap(16, 16)
        colorPix.fill(self.color)
        self.buttonMinorContourColor.setIcon(QtGui.QIcon(colorPix))
        self.buttonMinorContourColor.clicked.connect(lambda: self.selColor(self.buttonMinorContourColor))
        self.gridDivisions.addWidget(self.buttonMinorContourColor, self.gridDivisions.rowCount() - 1, 3, 1, 1)

        ###----------------------
        
        self.labelMayorContour = QtGui.QLabel(self.form)
        self.labelMayorContour.setText("Mayor:")

        self.inputMayorContourMargin = QtGui.QComboBox(self.form)
        self.inputMayorContourMargin.addItems(self.intervals)
        self.inputMayorContourMargin.setCurrentIndex(3)

        self.inputMayorContourThickness = QtGui.QSpinBox(self.form)
        self.inputMayorContourThickness.setRange(1, 10)
        self.inputMayorContourThickness.setValue(5)

        self.gridDivisions.addWidget(self.labelMayorContour, self.gridDivisions.rowCount(), 0, 1, 1)
        self.gridDivisions.addWidget(self.inputMayorContourMargin, self.gridDivisions.rowCount() - 1, 1, 1, 1)
        self.gridDivisions.addWidget(self.inputMayorContourThickness, self.gridDivisions.rowCount() - 1, 2, 1, 1)

        self.buttonMayorContourColor = QtGui.QPushButton('')
        self.color = QtGui.QColor(128, 128, 128)
        colorPix = QtGui.QPixmap(16, 16)
        colorPix.fill(self.color)
        self.buttonMayorContourColor.setIcon(QtGui.QIcon(colorPix))
        self.buttonMayorContourColor.clicked.connect(lambda: self.selColor(self.buttonMayorContourColor))
        self.gridDivisions.addWidget(self.buttonMayorContourColor, self.gridDivisions.rowCount() - 1, 3, 1, 1)


    def add(self):
        sel = FreeCADGui.Selection.getSelection()
        if len(sel) > 0:
            self.land = sel[0]
            self.lineEdit1.setText(self.land.Label)

    def selColor(self, button):
        color = QtGui.QColorDialog.getColor()
        colorPix = QtGui.QPixmap(16, 16)
        colorPix.fill(color)
        button.setIcon(QtGui.QIcon(colorPix))

        r = float(color.red()/255.0)
        g = float(color.green()/255.0)
        b = float(color.blue()/255.0)
        col = (r, g, b)

        if button is self.buttonMinorContourColor:
            self.MinorColor = col
        elif button is self.buttonMayorContourColor:
            self.MayorColor = col

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

        if self.land is None:
            print("No hay objetos para procesar")
            return False
        else:
            minor = FreeCAD.Units.Quantity(self.inputMinorContourMargin.currentText()).Value
            mayor = FreeCAD.Units.Quantity(self.inputMayorContourMargin.currentText()).Value

            i = 2
            if i == 0:
                makeContours(self.land, minor, mayor, self.MinorColor, self.MayorColor,
                          self.inputMinorContourThickness.value(), self.inputMayorContourThickness.value())
            elif i == 1:
                import multiprocessing
                p = multiprocessing.Process(target=makeContours,
                                            args=(self.land, minor, mayor,
                                                  self.MinorColor, self.MayorColor,
                                                  self.inputMinorContourThickness.value(),
                                                  self.inputMayorContourThickness.value(), ))
                p.start()
                p.join()

            else:
                import threading
                hilo = threading.Thread(target = makeContours,
                                        args = (self.land, minor, mayor,
                                                self.MinorColor, self.MayorColor,
                                                self.inputMinorContourThickness.value(),
                                                self.inputMayorContourThickness.value()))
                hilo.daemon = True
                hilo.start()

        total_time = datetime.now() - starttime
        print(" -- Tiempo tardado:", total_time)
        FreeCADGui.Control.closeDialog()
        return True

# Height Analisys: ---------------------------------------------------------------------------------
class HeightTaskPanel(_generalTaskPanel):
    '''The TaskPanel for Slope setup'''

    def __init__(self):
        _generalTaskPanel.__init__(self)

        # Initial set-up:
        land = FreeCAD.ActiveDocument.Site.Terrain
        self.form.editFrom.setSuffix(" m")
        self.form.editFrom.setValue(land.Shape.BoundBox.ZMin / 1000)
        self.form.editTo.setSuffix(" m")
        self.form.editTo.setValue(land.Shape.BoundBox.ZMax / 1000)
        self.form.editSteps.setValue(10)
        self.form.editFrom.valueChanged.connect(self.updateTableValues)
        self.form.editTo.valueChanged.connect(self.updateTableValues)

    def accept(self):
        land = FreeCAD.ActiveDocument.Site.Terrain
        if land.isDerivedFrom("Part::Feature"):
            colorlist = []
            for face in land.Shape.Faces:
                zz = face.CenterOfMass.z / 1000
                color = (.0, .0, .0)
                for i in range(1,len(self.ranges)):
                    if self.ranges[i][0] <= zz <= self.ranges[i][1]:
                        color = self.ranges[i][2]
                        break
                colorlist.append(color)
            land.ViewObject.DiffuseColor = colorlist
        FreeCAD.activeDocument().recompute()
        return True

# Slope Analisys: ---------------------------------------------------------------------------------
class SlopeTaskPanel(_generalTaskPanel):
    '''The TaskPanel for Slope setup'''

    def __init__(self):
        _generalTaskPanel.__init__(self)
        self.angles = self.getAngles()
        # Initial set-up:
        self.form.editFrom.setSuffix(" º")
        self.form.editFrom.setValue(0)
        self.form.editTo.setSuffix(" º")
        self.form.editTo.setValue(max(self.angles))
        self.form.editSteps.setValue(10)
        self.form.editFrom.valueChanged.connect(self.updateTableValues)
        self.form.editTo.valueChanged.connect(self.updateTableValues)

    def getAngles(self):
        import math
        land = FreeCAD.ActiveDocument.Site.Terrain
        angles = []
        for face in land.Shape.Faces:
            normal = face.normalAt(0, 0)
            rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
            angle = math.degrees(rad)
            angles.append(angle)
        return angles

    def getPointSlope(self, ranges = None):
        from datetime import datetime
        starttime = datetime.now()
        import math

        land = FreeCAD.ActiveDocument.Site.Terrain
        if land.isDerivedFrom("Part::Feature"):
            colorlist = []
            for face in land.Shape.Faces:
                normal = face.normalAt(0, 0)
                rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
                angle = math.degrees(rad)
                if(angle > 90):
                    angle -= 90
                color = (1.0, 1.0, 1.0)
                for i in range(0, len(ranges)):
                    if ranges[i][0] <= angle <= ranges[i][1]:
                        color = ranges[i][2]
                        break
                colorlist.append(color)
            print(len(land.Shape.Faces) == len(colorlist))
            land.ViewObject.DiffuseColor = colorlist

        # TODO: check this code:
        elif obj.isDerivedFrom("Mesh::Feature"):
            fMesh = Mest2FemMesh(land)
            import math
            setColors = []
            i = 1
            normals = land.Mesh.getPointNormals()
            for normal in normals:
                rad = normal.getAngle(FreeCAD.Vector(0, 0, 1))
                angle = math.degrees(rad)

                # Cambiar esto a la lista de colores configurable
                if angle < 5:
                    setColors[i] = (0.0, 0.5, 0.0)
                elif angle < 7.5:
                    setColors[i] = (0.0, 1.0, 0.5)
                elif (angle < 10):
                    setColors[i] = (0.0, 1.0, 1.0)
                elif (angle < 12.5):
                    setColors[i] = (0.0, 0.0, 0.5)
                elif (angle < 14):
                    setColors[i] = (0.0, 0.0, 1.0)
                elif (angle > 20):
                    setColors[i] = (1.0, 0.0, 0.0)
                else:
                    setColors[i] = (1.0, 1.0, 1.0)
                i += 1

            fMesh.ViewObject.NodeColor = setColors

        FreeCAD.activeDocument().recompute()
        print("Everything OK (", datetime.now() - starttime, ")")

    def accept(self):
        # self.getPointSlope()
        import threading
        hilo = threading.Thread(target=self.getPointSlope(self.ranges))
        hilo.start()
        return True

# Orientation Analisys: ---------------------------------------------------------------------------------
class OrientationTaskPanel(_generalTaskPanel):
    '''The TaskPanel for Orientation setup'''

    def __init__(self):
        _generalTaskPanel.__init__(self)

        self.getAngles()

        # Initial set-up:
        self.form.editFrom.setSuffix(" º")
        self.form.editFrom.setValue(0.0)
        self.form.editTo.setSuffix(" º")
        self.form.editTo.setMaximum(360.0)
        self.form.editTo.setValue(360.0)
        self.form.editSteps.setValue(15)
        self.form.editFrom.valueChanged.connect(self.updateTableValues)
        self.form.editTo.valueChanged.connect(self.updateTableValues)

    def getAngles(self):
        import math
        land = FreeCAD.ActiveDocument.Site.Terrain
        anglesx = []
        anglesy = []
        for face in land.Shape.Faces:
            normal = face.normalAt(0, 0)
            normal.z = 0
            anglesx.append(math.degrees(normal.getAngle(FreeCAD.Vector(1, 0, 0))))
            anglesy.append(math.degrees(normal.getAngle(FreeCAD.Vector(0, 1, 0))))

        print("Min x: ", min(anglesx), " y: ", min(anglesy))
        print("Max x: ", max(anglesx), " y: ", max(anglesy))
        return anglesx, anglesy

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

        land = FreeCAD.ActiveDocument.Site.Terrain
        if land.isDerivedFrom("Part::Feature"):
            colorlist = []
            j = 0
            minx = 99999
            miny = 99999
            for face in land.Shape.Faces:
                normal = face.normalAt(0, 0)
                normal.z = 0
                anglex = math.degrees(normal.getAngle(FreeCAD.Vector(1, 0, 0)))
                angley = math.degrees(normal.getAngle(FreeCAD.Vector(0, 1, 0)))

                minx = min([minx, anglex])
                miny = min([miny, angley])
                if angley >= 90:
                    anglex = 360.0 - anglex

                #print(anglex, " ", angley)
                for i in range(1, len(self.ranges)):
                    if self.ranges[i][0] <= anglex <= self.ranges[i][1]:
                        colorlist.append(self.ranges[i][2])
                        break
                j += 1
                if j == 100:
                    break
            land.ViewObject.DiffuseColor = colorlist
            print("Angulos: ", math.degrees(minx), " - ", miny)

        # TODO: check this code:
        elif land.isDerivedFrom("Mesh::Feature"):
            fMesh = Mest2FemMesh(land)
            fMesh = Mest2FemMesh(land)

            setColors = {}
            i = 1
            normals = land.Mesh.getPointNormals()
            print("   ---------   time to FEMMESH:  (", datetime.now() - starttime, ")")

            for normal in normals:
                rad = normal.getAngle(FreeCAD.Vector(1, 1, 0))
                angle = math.degrees(rad)

                # Cambiar esto a la lista de colores configurable
                if angle < 45:
                    setColors[i] = (0.0, 1.0, 0.0)
                elif (angle < 90):
                    setColors[i] = (0.0, 1.0, 1.0)
                elif (angle < 135):
                    setColors[i] = (0.0, 0.0, 1.0)
                elif (angle <= 180):
                    setColors[i] = (1.0, 0.0, 1.0)
                #else:
                #    setColors[i] = (1.0, 0.0, 0.0)

                i += 1
                if i== 1000:
                    break

            fMesh.ViewObject.NodeColor = setColors

        FreeCAD.activeDocument().recompute()
        print("Everything OK (", datetime.now() - starttime, ")")
        return True

## Commands ----------------------------------------------------------------------------------------------------------
## 1. Contours:
'''class _CommandContours:
    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "TerrainContours.svg")),
                'Accel': "T, C",
                'MenuText': 'Curvas de nivel',
                'ToolTip': 'Curvas de nivel'
                }

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

        return True

        if FreeCADGui.Selection.getSelection() is not None:
            selection = FreeCADGui.Selection.getSelection()[-1]
            if selection.TypeId == 'Mesh::Feature':
                return True

        return False

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

## 2. Aspect:
class _CommandSlopeAnalisys:
    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "TerrainSlope.svg")),
                'Accel': "T, S",
                'MenuText': 'Analisis de Pendiente',
                'ToolTip': 'Analisis de Pendiente'
                }

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

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

## 3. Height:
class _CommandHeightAnalisys:
    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "TerrainHeight.svg")),
                'Accel': "T, H",
                'MenuText': 'Analisis de Altura',
                'ToolTip': 'Analisis de Altura'
                }

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

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

## 4. Orientation:
class _CommandOrientationAnalisys:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "TerrainOrientation.svg")),
                'Accel': "T, H",
                'MenuText': 'Analisis de Orientación',
			    'ToolTip': 'Analisis de Orientación'
                }

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

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

## 5. Commands
if FreeCAD.GuiUp:
    class CommandTerrainAnalisysGroup:

        def GetCommands(self):
            return tuple(['Contours',
                          'HeightAnalisys',
                          'SlopeAnalisys',
                          'OrientationAnalisys'
                          ])
        def GetResources(self):
            return { 'MenuText': QT_TRANSLATE_NOOP("",'Terrain Analisys'),
                     'ToolTip': QT_TRANSLATE_NOOP("",'Terrain Analisys')
                   }
        def IsActive(self):
            return not FreeCAD.ActiveDocument is None

    FreeCADGui.addCommand('Contours', _CommandContours())
    FreeCADGui.addCommand('SlopeAnalisys', _CommandSlopeAnalisys())
    FreeCADGui.addCommand('HeightAnalisys', _CommandHeightAnalisys())
    FreeCADGui.addCommand('OrientationAnalisys', _CommandOrientationAnalisys())
    FreeCADGui.addCommand('TerrainAnalisys', CommandTerrainAnalisysGroup())'''