# /**********************************************************************
# *                                                                     *
# * 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 os
from copy import deepcopy

import FreeCAD
import Part
from pivy import coin

if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore
    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

from PVPlantResources import DirIcons as DirIcons

'''
import threading
import datetime

class ThreadClass(threading.Thread):
    def __init__(self, land, Trace):
        self.land = land
        self.trace = Trace

    def run(self):
        tmp = self.land.makeParallelProjection(self.trace, FreeCAD.Vector(0, 0, 1))
        pts = [ver.Point for ver in tmp.Vertexes]
        return pts

        
for i in range(2):
    t = ThreadClass()
    t.start()
'''


def makeTrace(points = None, label = "Trace"):
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Trace")
    obj.Label = label
    Trace(obj)
    ViewProviderTrace(obj.ViewObject)
    if points:
        obj.Points = points
    return obj


class Trace:
    def __init__(self, obj):
        self.setCommonProperties(obj)

    def setCommonProperties(self, obj):
        pl = obj.PropertiesList
        if not ("Points" in pl):
            obj.addProperty("App::PropertyVectorList",
                            "Points",
                            "PlacementLine",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            )

        if not ("ProjectionPoints" in pl):
            obj.addProperty("App::PropertyVectorList",
                            "ProjectionPoints",
                            "PlacementLine",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            )

        if not ("ShowTrend" in pl):
            obj.addProperty("App::PropertyBool",
                            "ShowTrend",
                            "PlacementLine",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).ShowTrend = True

        if not ("Edit" in pl):
            obj.addProperty("App::PropertyBool",
                            "Edit",
                            "PlacementLine",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).Edit = False

        self.Type = "TraceLine"
        obj.Proxy = self

    def onChanged(self, obj, prop):
        ''' Do something when a property has changed '''
        if prop == "Edit":
            if obj.Edit:
                subobj = FreeCADGui.Selection.getSelectionEx()[0].SubObjects[0]
                if subobj.__class__ == Part.Circle:
                    print ("Circle")

    def execute(self, obj):
        shape = None
        base = None
        if len(obj.Points) > 1:
            base = Part.makePolygon(obj.Points)
        import DraftGeomUtils

        w = DraftGeomUtils.filletWire(base, 8000)
        if w:
            shape = w
        if shape:
            obj.Shape = shape

        '''
        if len(obj.ProjectionPoints) > 1 and obj.ShowTrend:
            # TODO: check:
            from scipy import stats
            xx = list()
            yy = list()
            zz = list()

            for pts in obj.ProjectionPoints:
                xx.append(pts.x)
                yy.append(pts.y)
                zz.append(pts.z)

            slope, intercept, r, p, std_err = stats.linregress(yy, zz)

            def myfunc(x):
                return slope * x + intercept

            x = list()
            x.append(yy[0])
            x.append(yy[-1])
            newzz = list(map(myfunc, x))

            for p in range(len(obj.Points) - 1):
                #found = [i for i in map(lambda n: float(n.replace(',', '.')), values) if 1.50 < i < 2.50]
                r = []

                #if obj.Points[p]
                mi = obj.Points[p + 1]
                ma = obj.Points[p]
                for p3d in obj.ProjectionPoints:
                    if mi.x <= p3d.x <= ma.x and mi.y <= p3d.y <= ma.y:
                        r.append(p3d)

                tmp = Part.makePolygon([FreeCAD.Vector(xx[0], yy[0], newzz[0]),
                                        FreeCAD.Vector(xx[-1], yy[-1], newzz[-1])])
                #obj.Shape = obj.Shape.fuse([tmp,])
        '''

class ViewFunctions:
    def get_stations(self, obj):
        """ Retrieve the coordinates of the start and end points of the station
            tick mark orthogonal to the alignment """

        start = obj.Proxy.model.data['meta']['StartStation']
        length = obj.Proxy.model.data['meta']['Length']
        end = start + length/1000

        stations = {}

        for sta in range(int(start), int(end)):
            if sta % 10 == 0:
                tuple_coord, tuple_vec =\
                    obj.Proxy.model.get_orthogonal( sta, "Left", True)

                coord = FreeCAD.Vector(tuple_coord)
                vec = FreeCAD.Vector(tuple_vec)

                left_vec = deepcopy(vec)
                right_vec = deepcopy(vec)

                left_side = coord.add(left_vec.multiply(1500))
                right_side = coord.add(right_vec.negative().multiply(1500))

                stations[sta] = [left_side, right_side]

        return stations

class ViewProviderTrace(ViewFunctions):
    def __init__(self, vobj):
        ''' Set view properties '''
        self.Object = None
        self.editor = None
        self.select_state = True
        self.active = False
        vobj.Proxy = self

    def getIcon(self):
        ''' Return object treeview icon '''

        return str(os.path.join(DirIcons, "Trace.svg"))

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

        self.Object = vobj.Object

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

        # Line geometry keepers.
        line_color = coin.SoBaseColor()
        line_color.rgb = (1.0, 0.0, 0.0)
        self.lines = coin.SoSeparator()
        self.lines.addChild(line_style)
        self.lines.addChild(line_color)

        # Curve geometry keepers.
        curve_color = coin.SoBaseColor()
        curve_color.rgb = (0.0, 0.5, 0.0)
        self.curves = coin.SoSeparator()
        self.curves.addChild(line_style)
        self.curves.addChild(curve_color)

        # Spiral geometry keepers.
        spiral_color = coin.SoBaseColor()
        spiral_color.rgb = (0.0, 0.33, 1.0)
        self.spirals = coin.SoSeparator()
        self.spirals.addChild(line_style)
        self.spirals.addChild(spiral_color)

        # Labels root.
        ticks = coin.SoSeparator()
        self.tick_coords = coin.SoGeoCoordinate()
        self.tick_lines = coin.SoLineSet()
        ticks.addChild(self.tick_coords)
        ticks.addChild(self.tick_lines)
        self.labels = coin.SoSeparator()

        # Alignment root.
        lines_root = coin.SoSeparator()
        lines_root.addChild(self.lines)
        lines_root.addChild(self.curves)
        lines_root.addChild(self.spirals)
        lines_root.addChild(ticks)
        lines_root.addChild(self.labels)
        vobj.addDisplayMode(lines_root, "Wireframe")

    def updateData(self, obj, prop):
        '''
        Update Object visuals when a data property changed.
        '''
        if prop == "Shape":
            shape = obj.getPropertyByName(prop)
            if not shape.SubShapes: return

            # Set System.
            #origin = geo_origin.get()
            #geo_system = ["UTM", origin.UtmZone, "FLAT"]

            copy_shape = shape.copy()
            copy_shape.Placement.move(shape.Placement.Base)
            #copy_shape.Placement.move(origin.Origin)

            lines = copy_shape.SubShapes[0]
            for wire in lines.Wires:
                points = []
                for vertex in wire.OrderedVertexes:
                    points.append(vertex.Point)

                line = coin.SoType.fromName('SoFCSelection').createInstance()
                line.style = 'EMISSIVE_DIFFUSE'

                line_coords = coin.SoGeoCoordinate()
                #line_coords.geoSystem.setValues(geo_system)
                line_coords.point.values = points
                line_set = coin.SoLineSet()

                line.addChild(line_coords)
                line.addChild(line_set)
                self.lines.addChild(line)

                del points

            curves = copy_shape.SubShapes[1]
            for wire in curves.Wires:
                points = []
                for vertex in wire.OrderedVertexes:
                    points.append(vertex.Point)

                curve = coin.SoType.fromName('SoFCSelection').createInstance()
                curve.style = 'EMISSIVE_DIFFUSE'

                curve_coords = coin.SoGeoCoordinate()
                #curve_coords.geoSystem.setValues(geo_system)
                curve_coords.point.values = points
                curve_set = coin.SoLineSet()

                curve.addChild(curve_coords)
                curve.addChild(curve_set)
                self.curves.addChild(curve)

                del points

            spirals = copy_shape.SubShapes[2]
            for wire in spirals.Wires:
                points = []
                for vertex in wire.OrderedVertexes:
                    points.append(vertex.Point)

                spiral = coin.SoType.fromName('SoFCSelection').createInstance()
                spiral.style = 'EMISSIVE_DIFFUSE'

                spiral_coords = coin.SoGeoCoordinate()
                #spiral_coords.geoSystem.setValues(geo_system)
                spiral_coords.point.values = points
                spiral_set = coin.SoLineSet()

                spiral.addChild(spiral_coords)
                spiral.addChild(spiral_set)
                self.spirals.addChild(spiral)

                del points
            del copy_shape

    def setEdit(self, vobj, mode=0):
        """Method called when the document requests the object to enter edit mode.

        Edit mode is entered when a user double clicks on an object in the tree
        view, or when they use the menu option [Edit -> Toggle Edit Mode].

        Just display the standard Point Edit task panel.

        Parameters
        ----------
        mode: int or str
            The edit mode the document has requested. Set to 0 when requested via
            a double click or [Edit -> Toggle Edit Mode].

        Returns
        -------
        bool
            If edit mode was entered.
        """

        if (mode == 0) and hasattr(self, "Object"):
            import Utils.profile_editor as editor
            if vobj.Selectable:
                self.select_state = True
                vobj.Selectable = False

            pts = list()
            for i in range(len(self.Object.Points)):
                p = self.Object.Points[i]
                #pts.append(editor.MarkerOnShape([p]))
            self.editor = editor.Edit(self.Object) #(pts, self.Object)
            '''for i in range(min(len(self.Object.Shape.Edges), len(self.editor.lines))):
                self.editor.lines[i].updateLine()'''
            self.active = True
            return True
        return False

    def unsetEdit(self, vobj, mode=0):
        """
        Disable edit
        """
        import Utils.profile_editor as editor
        if isinstance(self.editor, editor.Edit):# and check_pivy():
            pts = list()
            for p in self.editor.points:
                if isinstance(p, editor.MarkerOnShape):
                    pt = p.points[0]
                    pts.append(FreeCAD.Vector(pt[0], pt[1], pt[2]))
            self.Object.Points = pts
            vobj.Selectable = self.select_state
            self.editor.quit()
        del self.editor
        self.editor = None
        self.active = False
        self.Object.Document.recompute()
        return True

    def doubleClicked(self, vobj):
        if not hasattr(self, 'active'):
            self.active = False
        if not self.active:
            self.active = True
            # self.setEdit(vobj)
            vobj.Document.setEdit(vobj)
        else:
            vobj.Document.resetEdit()
            self.active = False
        return True

    def setupContextMenu(self, obj, menu):
        """
        Context menu construction
        """
        pass

    def edit(self):
        """
        Edit callback
        """
        pass

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

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

class CommandTrace:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "Trace.svg")),
                'Accel': "T, R",
                'MenuText': "Trace",
                'ToolTip': "Trace"}

    def Activated(self):
        points = None
        '''
        sel = FreeCADGui.Selection.getSelection()
        if sel:
            points = sel[0].Points
        '''
        obj = makeTrace(points)
        #obj.ViewObject.Document.setEdit(obj.ViewObject)

    def IsActive(self):
        if FreeCAD.ActiveDocument:
            return True
        else:
            return False

if FreeCAD.GuiUp:
    FreeCADGui.addCommand('Trace', CommandTrace())