# /**********************************************************************
# *                                                                     *
# * 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 FreeCAD
import ArchComponent
import os
import zipfile
import re

if FreeCAD.GuiUp:
    import FreeCADGui
    from DraftTools import translate
else:
    # \cond
    def translate(ctxt,txt):
        return txt
    def QT_TRANSLATE_NOOP(ctxt,txt):
        return txt
    # \endcond

import os
from PVPlantResources import DirIcons as DirIcons

__title__ = "PVPlant Areas"
__author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com"

import PVPlantResources
from PVPlantResources import DirIcons as DirIcons
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")


def makeStringInverter():
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "StringInverter")
    InverterBase(obj)
    ViewProviderStringInverter(obj.ViewObject)

    try:
        folder = FreeCAD.ActiveDocument.StringInverters
    except:
        folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'StringInverters')
        folder.Label = "StringInverters"
    folder.addObject(obj)
    return obj


class InverterBase(ArchComponent.Component):
    def __init__(self, obj):
        ''' Initialize the Area object '''
        ArchComponent.Component.__init__(self, obj)

        self.oldMPPTs = 0

        self.Type = None
        self.obj = None
        self.setProperties(obj)

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

        if not "File" in pl:
            obj.addProperty("App::PropertyFile",
                            "File",
                            "Inverter",
                            "The base file this component is built upon")

        if not ("MPPTs" in pl):
            obj.addProperty("App::PropertyQuantity",
                            "MPPTs",
                            "Inverter",
                            "Points that define the area"
                            ).MPPTs = 0

        if not ("Generator" in pl):
            obj.addProperty("App::PropertyEnumeration",
                            "Generator",
                            "Inverter",
                            "Points that define the area"
                            ).Generator = ["Generic", "Library"]
            obj.Generator = "Generic"

        if not ("Type" in pl):
            obj.addProperty("App::PropertyString",
                            "Type",
                            "Base",
                            "Points that define the area"
                            ).Type = "InverterBase"
            obj.setEditorMode("Type", 1)

        self.Type = obj.Type
        obj.Proxy = self

    def onDocumentRestored(self, obj):
        """ Method run when the document is restored """
        self.setProperties(obj)

    def onBeforeChange(self, obj, prop):

        if prop == "MPPTs":
            self.oldMPPTs = int(obj.MPPTs)

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

        if prop == "Generator":
            if obj.Generator == "Generic":
                obj.setEditorMode("MPPTs", 0)
            else:
                obj.setEditorMode("MPPTs", 1)

        if prop == "MPPTs":
            ''' '''
            if self.oldMPPTs > obj.MPPTs:
                ''' borrar sobrantes '''
                obj.removeProperty()

            elif self.oldMPPTs < obj.MPPTs:
                ''' crear los faltantes '''
                for i in range(self.oldMPPTs, int(obj.MPPTs)):
                    ''' '''
                    print(i)
            else:
                pass

        if (prop == "File") and obj.File:
            ''' '''

    def execute(self, obj):
        ''' '''
        # obj.Shape: compound
        # |- body: compound
        # |-- inverter: solid
        # |-- door: solid
        # |-- holder: solid

        # |- connectors: compound
        # |-- DC: compound
        # |--- MPPT 1..x: compound
        # |---- positive: compound
        # |----- connector 1..y: ??
        # |---- negative 1..y: compound
        # |----- connector 1..y: ??
        # |-- AC: compound
        # |--- R,S,T,: ??
        # |-- Communication

        pl = obj.Placement
        filename = self.getFile(obj)
        if filename:
            parts = self.getPartsList(obj)
            if parts:
                zdoc = zipfile.ZipFile(filename)
                if zdoc:
                    f = zdoc.open(parts[list(parts.keys())[-1]][1])
                    shapedata = f.read()
                    f.close()
                    shapedata = shapedata.decode("utf8")
                    shape = self.cleanShape(shapedata, obj, parts[list(parts.keys())[-1]][2])
                    obj.Shape = shape
                    if not pl.isIdentity():
                        obj.Placement = pl
                    obj.MPPTs = len(shape.SubShapes[1].SubShapes[0].SubShapes)

    def cleanShape(self, shapedata, obj, materials):
        "cleans the imported shape"

        import Part
        shape = Part.Shape()
        shape.importBrepFromString(shapedata)
        '''if obj.FuseArch and materials:
            # separate lone edges
            shapes = []
            for edge in shape.Edges:
                found = False
                for solid in shape.Solids:
                    for soledge in solid.Edges:
                        if edge.hashCode() == soledge.hashCode():
                            found = True
                            break
                    if found:
                        break
                if found:
                    break
            else:
                shapes.append(edge)
            print("solids:",len(shape.Solids),"mattable:",materials)
            for key,solindexes in materials.items():
                if key == "Undefined":
                    # do not join objects with no defined material
                    for solindex in [int(i) for i in solindexes.split(",")]:
                        shapes.append(shape.Solids[solindex])
                else:
                    fusion = None
                    for solindex in [int(i) for i in solindexes.split(",")]:
                        if not fusion:
                            fusion = shape.Solids[solindex]
                        else:
                            fusion = fusion.fuse(shape.Solids[solindex])
                    if fusion:
                        shapes.append(fusion)
            shape = Part.makeCompound(shapes)
            try:
                shape = shape.removeSplitter()
            except Exception:
                print(obj.Label,": error removing splitter")'''
        return shape

    def getFile(self, obj, filename=None):
        "gets a valid file, if possible"

        if not filename:
            filename = obj.File
        if not filename:
            return None
        if not filename.lower().endswith(".fcstd"):
            return None
        if not os.path.exists(filename):
            # search for the file in the current directory if not found
            basename = os.path.basename(filename)
            currentdir = os.path.dirname(obj.Document.FileName)
            altfile = os.path.join(currentdir,basename)
            if altfile == obj.Document.FileName:
                return None
            elif os.path.exists(altfile):
                return altfile
            else:
                # search for subpaths in current folder
                altfile = None
                subdirs = self.splitall(os.path.dirname(filename))
                for i in range(len(subdirs)):
                    subpath = [currentdir]+subdirs[-i:]+[basename]
                    altfile = os.path.join(*subpath)
                    if os.path.exists(altfile):
                        return altfile
                return None
        return filename

    def getPartsList(self, obj, filename=None):

        "returns a list of Part-based objects in a FCStd file"

        parts = {}
        materials = {}
        filename = self.getFile(obj,filename)
        if not filename:
            return parts
        zdoc = zipfile.ZipFile(filename)
        with zdoc.open("Document.xml") as docf:
            name = None
            label = None
            part = None
            materials = {}
            writemode = False
            for line in docf:
                line = line.decode("utf8")
                if "<Object name=" in line:
                    n = re.findall('name=\"(.*?)\"',line)
                    if n:
                        name = n[0]
                elif "<Property name=\"Label\"" in line:
                    writemode = True
                elif writemode and "<String value=" in line:
                    n = re.findall('value=\"(.*?)\"',line)
                    if n:
                        label = n[0]
                        writemode = False
                elif "<Property name=\"Shape\" type=\"Part::PropertyPartShape\"" in line:
                    writemode = True
                elif writemode and "<Part file=" in line:
                    n = re.findall('file=\"(.*?)\"',line)
                    if n:
                        part = n[0]
                        writemode = False
                elif "<Property name=\"MaterialsTable\" type=\"App::PropertyMap\"" in line:
                    writemode = True
                elif writemode and "<Item key=" in line:
                    n = re.findall('key=\"(.*?)\"',line)
                    v = re.findall('value=\"(.*?)\"',line)
                    if n and v:
                        materials[n[0]] = v[0]
                elif writemode and "</Map>" in line:
                    writemode = False
                elif "</Object>" in line:
                    if name and label and part:
                        parts[name] = [label,part,materials]
                    name = None
                    label = None
                    part = None
                    materials = {}
                    writemode = False
        return parts

    def getColors(self,obj):

        "returns the DiffuseColor of the referenced object"

        filename = self.getFile(obj)
        if not filename:
            return None
        part = obj.Part
        if not obj.Part:
            return None
        zdoc = zipfile.ZipFile(filename)
        if not "GuiDocument.xml" in zdoc.namelist():
            return None
        colorfile = None
        with zdoc.open("GuiDocument.xml") as docf:
            writemode1 = False
            writemode2 = False
            for line in docf:
                line = line.decode("utf8")
                if ("<ViewProvider name=" in line) and (part in line):
                    writemode1 = True
                elif writemode1 and ("<Property name=\"DiffuseColor\"" in line):
                    writemode1 = False
                    writemode2 = True
                elif writemode2 and ("<ColorList file=" in line):
                    n = re.findall('file=\"(.*?)\"',line)
                    if n:
                        colorfile = n[0]
                        break
        if not colorfile:
            return None
        if not colorfile in zdoc.namelist():
            return None
        colors = []
        cf = zdoc.open(colorfile)
        buf = cf.read()
        cf.close()
        for i in range(1,int(len(buf)/4)):
            colors.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0))
        if colors:
            return colors
        return None

    def splitall(self,path):

        "splits a path between its components"

        allparts = []
        while 1:
            parts = os.path.split(path)
            if parts[0] == path:  # sentinel for absolute paths
                allparts.insert(0, parts[0])
                break
            elif parts[1] == path: # sentinel for relative paths
                allparts.insert(0, parts[1])
                break
            else:
                path = parts[0]
                allparts.insert(0, parts[1])
        return allparts

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

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


class CommandStringInverter:

    def GetResources(self):
        return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg")),
                'Accel': "E, I",
                'MenuText': "String Inverter",
                'ToolTip': "String Placement",}

    def Activated(self):
        sinverter = makeStringInverter()

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

if FreeCAD.GuiUp:
    FreeCADGui.addCommand('StringInverter', CommandStringInverter())

