# /**********************************************************************
# *                                                                     *
# * 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 math

import ArchComponent
import FreeCAD
import Part

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

import os
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")

def makeStringSetup():
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringSetup")
    _StringSetup(obj)
    _ViewProviderStringSetup(obj.ViewObject)

    try:
        if FreeCAD.ActiveDocument.StringsSetup:
            FreeCAD.ActiveDocument.StringsSetup.addObject(obj)
    except:
        pass
    return obj

class _StringSetup:
    def __init__(self, obj):
        self.setCommonProperties(obj)
        self.obj = obj
        self.StringCount = 1

    def setCommonProperties(self, obj):
        pl = obj.PropertiesList

        if not ("NumberOfStrings" in pl):
            obj.addProperty("App::PropertyInteger",
                            "NumberOfStrings",
                            "Setup",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).NumberOfStrings = 0
            obj.setEditorMode("NumberOfStrings", 1)

        self.Type = "StringSetup"
        obj.Proxy = self

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

    def addString(self, modulelist):
        stringName = "String" + str(self.StringCount)
        self.obj.addProperty("App::PropertyIntegerList",
                             stringName,
                             "Setup",
                             "String: " + stringName
                             )
        setattr(self.obj, stringName, modulelist)

        '''
        self.obj.addProperty("App::PropertyInteger",
                             stringName + "_Power",
                             "Outputs",
                             QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                             )
        setattr(self.obj, stringName + "_Power", len(modulelist) * 450)
        '''
        self.obj.NumberOfStrings = self.StringCount
        self.StringCount += 1


class _ViewProviderStringSetup:
    def __init__(self, vobj):
        '''
        Set view properties.
        '''
        self.Object = vobj.Object
        vobj.Proxy = self

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

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

        return str(os.path.join(DirIcons, "stringsetup.svg"))
    '''
    def claimChildren(self):
        """
        Provides object grouping
        """
        return self.Object.Group
    '''

    def setEdit(self, vobj, mode=0):
        """
        Enable edit
        """
        return True

    def unsetEdit(self, vobj, mode=0):
        """
        Disable edit
        """
        return False

    def doubleClicked(self, vobj):
        """
        Detect double click
        """
        pass

    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 _SelObserver:
    def __init__(self, form):
        self.form = form

    def addSelection(self, doc, obj, sub, pnt):
        rack = FreeCAD.ActiveDocument.getObjectsByLabel(obj)[0].Shape
        modules = rack.SubShapes[0].SubShapes[0].SubShapes
        if sub[0:4] == 'Face':
            numFace = int(sub.replace('Face', '')) - 1
            selFace = rack.Faces[numFace]
            for module in modules:
                for num, face in enumerate(module.Faces):
                    if selFace.isSame(face):
                        self.form.setModule(modules.index(module))
                        FreeCADGui.Selection
                        return True

        elif sub[0:4] == 'Edge':
            numEdge = int(sub.replace('Edge', '')) - 1
            selEdge = rack.Edge[numEdge]
            for module in modules:
                for num, edge in enumerate(module.Edges):
                    if selEdge.isSame(edge):
                        print("Encontrado: ", modules.index(module))
                        return True
        return True

    def removeSelection(self, doc, obj, sub):  # Delete the selected object
        '''print("FSO-RemSel:" + str(obj) + ":" + str(sub) + "\n")'''
        return True

    def setSelection(self, doc):  # Selection in ComboView
        '''print("FSO-SetSel:" + "\n")'''
        return True

    def clearSelection(self, doc):  # If click on the screen, clear the selection
        '''print("FSO-ClrSel:" + "\n")'''
        return True

class _StringSetupPanel:
    def __init__(self, obj=None):
        self.obj = obj
        self.new = False

        if obj is None:
            self.new = True
            self.obj = makeStringSetup()

        self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantStringSetup.ui")
        self.form.buttonSelFrame.clicked.connect(self.selFrame)
        self.form.buttonAddString.clicked.connect(self.addString)
        self.form.listStrings.currentRowChanged.connect(self.listStringsCurrentRowChanged)
        self.form.editName.editingFinished.connect(self.setStringName)

        self.selobserver = _SelObserver(self)
        self.stringslist = []
        self.currentstring = 0
        self.totalModules = 0
        self.left = 0

        self.frameColor = None

    def selFrame(self):
        sel = FreeCADGui.Selection.getSelection()
        frame = None
        if len(sel) == 0:
            # TODO: lanzar un error
            return
        elif len(sel) == 1:
            frame = sel[0]
        else:
            for obj in sel:
                if not hasattr(obj, 'Proxy'):
                    continue
                print(obj.Proxy.__class__)
                print(issubclass(obj.Proxy.__class__, PVPlantRack._Frame))
                if issubclass(obj.Proxy.__class__, PVPlantRack._Frame):
                    frame = obj
                    break

        if frame == None:
            # TODO: lanzar un error
            print("No frame selected")
            return

        self.frame = frame
        self.form.editFrame.setText(frame.Label)
        self.totalModules = frame.ModuleCols * frame.ModuleRows
        self.left = self.totalModules
        self.form.editTotal.setText(str(int(self.totalModules)))
        self.form.editUsed.setText("0")
        self.form.editLeft.setText(str(int(self.left)))

        FreeCADGui.Selection.removeObserver(self.selobserver)
        FreeCADGui.Selection.addObserver(self.selobserver)

        self.frameColor = self.obj.ViewObject.ShapeColor
        self.colorlist = []
        for face in self.frame.Shape.Faces:
            self.colorlist.append((1.0, 0.50, 0.40, 0.25))
        self.frame.ViewObject.DiffuseColor = self.colorlist

    def addString(self):
        FreeCADGui.Selection.clearSelection()
        self.stringslist.append([])
        self.currentstring = len(self.stringslist) - 1
        self.form.listStrings.addItem("String" + str(self.form.listStrings.count()))
        self.form.listStrings.setCurrentRow(self.form.listStrings.count() - 1)

    def listStringsCurrentRowChanged(self, row):
        self.currentstring = row
        self.form.listModules.clear()
        for num in self.stringslist[row]:
            self.form.listModules.addItem(str(num))

    def setModule(self, numModule):
        if len(self.stringslist) == 0:
            return

        if self.left == 0:
            return

        if numModule in self.stringslist[self.currentstring]:
            '''remove module ?? '''
            self.stringslist[self.currentstring].remove(int(numModule))
            item = self.form.listModules.findItems(str(numModule), QtCore.Qt.MatchExactly)[0]
            self.form.listModules.takeItem(self.form.listModules.row(item))
            for moduleFace in self.frame.Shape.Solids[numModule].Faces:
                for i, face in enumerate(self.frame.Shape.Faces):
                    if moduleFace.isEqual(face):
                        self.colorlist[i] = (1.0, 0.50, 0.40, 0.25)
                        self.frame.ViewObject.DiffuseColor = self.colorlist
                        break
        else:
            self.stringslist[self.currentstring].append(int(numModule))
            self.form.listModules.addItem(str(numModule))
            for moduleFace in self.frame.Shape.Solids[numModule].Faces:
                for i, face in enumerate(self.frame.Shape.Faces):
                    if moduleFace.isEqual(face):
                        self.colorlist[i] = (0.0, 0.0, 1.0, 0.0)
                        self.frame.ViewObject.DiffuseColor = self.colorlist
                        break

        self.calculateModules()

    def calculateModules(self):
        num = 0
        for string in self.stringslist:
            num += len(string)

        self.left = int(self.totalModules - num)
        self.form.editUsed.setText(str(num))
        self.form.editLeft.setText(str(self.left))

    def setStringName(self):
        self.obj.Label = self.form.editName.text()

    def accept(self):
        for string in self.stringslist:
            self.obj.Proxy.addString(string.copy())
        self.FormClosing()
        return True

    def reject(self):
        FreeCAD.ActiveDocument.removeObject(self.obj.Name)
        self.FormClosing()
        return True

    def FormClosing(self):
        FreeCADGui.Selection.removeObserver(self.selobserver)
        FreeCADGui.Control.closeDialog()
        self.frame.ViewObject.DiffuseColor = self.frameColor

def makeString(base=None):
    if base is None:
        return
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "String")
    _String(obj)
    obj.Frame = base
    _ViewProviderString(obj.ViewObject)

    if FreeCAD.ActiveDocument.Strings:
        FreeCAD.ActiveDocument.Strings.addObject(obj)

    FreeCAD.ActiveDocument.recompute()
    return obj

class _String(ArchComponent.Component):
    def __init__(self, obj):

        ArchComponent.Component.__init__(self, obj)
        self.setProperties(obj)

        obj.Proxy = self
        obj.IfcType = "Cable Segment"
        obj.setEditorMode("IfcType", 1)

    def setProperties(self, obj):

        pl = obj.PropertiesList
        if not ("Frame" in pl):
            obj.addProperty("App::PropertyLink",
                            "Frame",
                            "Setup",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).Frame = None

        if not ("StringSetup" in pl):
            obj.addProperty("App::PropertyLink",
                            "StringSetup",
                            "Setup",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).StringSetup = None

        if not ("StringPoles" in pl):
            obj.addProperty("App::PropertyVectorList",
                            "StringPoles",
                            "StringOutput",
                            QT_TRANSLATE_NOOP("App::Property", "The height of this object")
                            ).StringPoles = []
            obj.setEditorMode("StringPoles", 1)

        self.Type = "String"

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

    def __getstate__(self):
        return self.Type

    def __setstate__(self, state):
        if state:
            self.Type = state

    def onBeforeChange(self, obj, prop):
        ''''''

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

        def getDownFace(module):
            area_max = max([face.Area for face in module.Faces])
            faces = []
            for face in module.Faces:
                if face.Area == area_max:
                    faces.append(face)
            return faces[0] if faces[0].Placement.Base.z > faces[1].Placement.Base.z else faces[1]

        if (prop == "Frame") or (prop == "StringSetup"):
            if not (obj.Frame is None) and not (obj.StringSetup is None):
                JuntionBoxPosition = 200
                portrait = obj.Frame.ModuleOrientation == "Portrait"
                cableLength = 1200
                if hasattr(obj.Frame, "PoleCableLength"):
                    cableLength = obj.Frame.PoleCableLength.Value

                moduleWidth = obj.Frame.ModuleWidth.Value
                moduleHeight = obj.Frame.ModuleHeight.Value
                dist_x = JuntionBoxPosition + obj.Frame.ModuleColGap.Value + (
                    moduleWidth if portrait else moduleHeight) / 2
                dist_y = obj.Frame.ModuleRowGap.Value + (moduleHeight if portrait else moduleWidth)

                PolePosition = 0
                if portrait:
                    PolePosition = moduleWidth / 2 - JuntionBoxPosition

                FrameModules = obj.Frame.Shape.SubShapes[0].SubShapes[0].SubShapes
                cableProfile = Part.Face(Part.Wire(Part.Circle(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(1, 0, 0), 6).toShape()))
                positiveMC4 = Part.Shape()
                positiveMC4.read(os.path.join(Dir3dObjects, "MC4 POSITIVE.IGS"))
                negativeMC4 = Part.Shape()
                negativeMC4.read(os.path.join(Dir3dObjects, "MC4 NEGATIVE.IGS"))

                vec = None
                if obj.Frame.Route:
                    vertexes = obj.Frame.Route.Shape.Vertexes
                    vec = vertexes[1].Point - vertexes[0].Point
                else:
                    poles = obj.Frame.Shape.SubShapes[1].SubShapes
                    vec = poles[1].BoundBox.Center - poles[0].BoundBox.Center
                vecp= FreeCAD.Vector(-vec.y, vec.x, vec.z)
            # V1:
            for stringnum in range(obj.StringSetup.NumberOfStrings):
                ''''''

            return
            # V0:
            for stringnum in range(obj.StringSetup.NumberOfStrings):
                    string = obj.StringSetup.getPropertyByName("String" + str(stringnum + 1))
                    total = len(string) - 1
                    dir = 0
                    ndir = 0
                    # dirvec = FrameModules(string[-]).Placement.Base - FrameModules(string[0]).Placement.Base
                    for i, num in enumerate(string):
                        face = getDownFace(FrameModules[num])

                        if i < total:
                            dir = string[i + 1] - num
                            dir /= abs(dir)
                            ndir = -dir

                        positivePolePosition = FreeCAD.Vector(0, ndir * PolePosition, 0) + face.CenterOfMass
                        negativePolePosition = FreeCAD.Vector(0, dir * PolePosition, 0) + face.CenterOfMass

                        # dibujar +:
                        p1 = positivePolePosition
                        p2 = p1 + ndir * FreeCAD.Vector(0, 50, 0)
                        p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0)
                        p4 = p3 + ndir * FreeCAD.Vector(0, dist_x - 50, 0)
                        w = Part.makePolygon([p1, p2, p3, p4])
                        #cableProfile.Placement.Base = p1
                        #cable = w.makePipeShell([cableProfile], True, False, 2)
                        Part.show(w)
                        mc4copy = positiveMC4.copy()
                        mc4copy.Placement.Base = p4 + FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0)
                        positiveMC4.Placement.Rotation.Angle = math.radians(ndir * 90)
                        Part.show(mc4copy)

                        # dibujar -:
                        p1 = negativePolePosition
                        p2 = p1 + dir * FreeCAD.Vector(0, 50, 0)
                        p3 = p2 + ndir * FreeCAD.Vector(cableLength - dist_x, 0, 0)
                        p4 = p3 + dir * FreeCAD.Vector(0, dist_x - 50, 0)
                        w = Part.makePolygon([p1, p2, p3, p4])
                        Part.show(w)
                        mc4copy = negativeMC4.copy()
                        mc4copy.Placement.Base = p4 - FreeCAD.Vector(0, mc4copy.BoundBox.XLength/2 + 1.3, 0)
                        mc4copy.Placement.Rotation.Angle = math.radians(dir * 90)
                        Part.show(mc4copy)

                        if i == 0 or i == total:
                            list = obj.StringPoles.copy()
                            list.append(p3)
                            obj.StringPoles = list
                    break

    def execute(self, obj):
        '''Do something when recompute'''


class _ViewProviderString(ArchComponent.ViewProviderComponent):
    def __init__(self, vobj):
        ArchComponent.ViewProviderComponent.__init__(self, vobj)

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



class _CommandStringSetup:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "stringsetup.svg")),
                'Accel': "E, C",
                'MenuText': "String Setup",
                'ToolTip': "Configure strings of a Frame"}

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

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

class _CommandStringing:
    def GetResources(self):
        return {'Pixmap': str(os.path.join(DirIcons, "string.svg")),
                'Accel': "E, S",
                'MenuText': QT_TRANSLATE_NOOP("Placement", "String"),
                'ToolTip': QT_TRANSLATE_NOOP("Placement", "Make string on a Frame")}

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

    def Activated(self):
        # issubclass(Car, Vehicles)
        # isinstance(Car, Vehicles)
        sel = FreeCADGui.Selection.getSelection()
        if len(sel) > 0:
            for obj in sel:
                if obj.Name.__contains__("Tracker"):
                    makeString(obj)


if FreeCAD.GuiUp:
    class _CommandStringingGroup:

        def GetCommands(self):
            return tuple(['PVPlantStringSetup',
                          'PVPlantStringing'
                          ])

        def GetResources(self):
            return {'MenuText': 'Stringing',
                    'ToolTip': 'Tools to setup and make strings'
                    }

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

    FreeCADGui.addCommand('PVPlantStringSetup', _CommandStringSetup())
    FreeCADGui.addCommand('PVPlantStringing', _CommandStringing())
    FreeCADGui.addCommand('Stringing', _CommandStringingGroup())


