# -*- coding: utf8 -*-
# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2024 Yorik van Havre <yorik@uncreated.net>              *
# *                                                                         *
# *   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                                                                   *
# *                                                                         *
# ***************************************************************************

"""This contains nativeifc status widgets and functionality"""


import FreeCAD
import FreeCADGui


translate = FreeCAD.Qt.translate
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
text_on = translate("BIM", "Strict IFC mode is ON (all objects are IFC)")
text_off = translate("BIM", "Strict IFC mode is OFF (IFC and non-IFC objects allowed)")


def set_status_widget(statuswidget):
    """Adds the needed controls to the status bar"""

    from PySide import QtGui  # lazy import
    import Arch_rc

    # lock button
    lock_button = QtGui.QAction()
    icon = QtGui.QIcon(":/icons/IFC.svg")
    lock_button.setIcon(icon)
    lock_button.setCheckable(True)
    doc = FreeCAD.ActiveDocument
    statuswidget.addAction(lock_button)
    statuswidget.lock_button = lock_button
    if doc and "IfcFilePath" in doc.PropertiesList:
        checked = True
    else:
        checked = False
    # set the button first, without converting the document
    lock_button.setChecked(checked)
    on_toggle_lock(checked, noconvert=True)
    lock_button.triggered.connect(on_toggle_lock)


def on_toggle_lock(checked=None, noconvert=False, setchecked=False):
    """When the toolbar button is pressed"""

    if checked is None:
        checked = get_lock_status()
    set_menu(checked)
    set_button(checked, setchecked)
    if not noconvert:
        if checked:
            lock_document()
        else:
            unlock_document()


def on_open():
    """What happens when opening an existing document"""

    pass # TODO implement


def on_activate():
    """What happens when activating a document"""

    from PySide import QtGui  # lazy import

    doc = FreeCAD.ActiveDocument
    if doc and "IfcFilePath" in doc.PropertiesList:
        checked = True
    else:
        checked = False
    mw = FreeCADGui.getMainWindow()
    statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
    if hasattr(statuswidget, "lock_button"):
        statuswidget.lock_button.setChecked(checked)
    on_toggle_lock(checked, noconvert=True)


def on_new():
    """What happens when creating a new document"""

    pass # TODO implement


def set_menu(locked=False):
    """Sets the File menu items"""

    from PySide import QtCore, QtGui  # lazy loading

    # switch Std_Save and IFC_Save
    mw = FreeCADGui.getMainWindow()
    wb = FreeCADGui.activeWorkbench()
    save_action = mw.findChild(QtGui.QAction, "Std_Save")
    if locked and "IFC_Save" in FreeCADGui.listCommands():
        if not hasattr(FreeCADGui,"IFC_WBManipulator"):
            FreeCADGui.IFC_WBManipulator = IFC_WBManipulator()
        # we need to void the shortcut otherwise it keeps active
        # even if the command is not shown
        FreeCADGui.IFC_saveshortcut = save_action.shortcut()
        save_action.setShortcut("")
        FreeCADGui.addWorkbenchManipulator(FreeCADGui.IFC_WBManipulator)
        wb.reloadActive()
    else:
        if hasattr(FreeCADGui,"IFC_saveshortcut"):
            save_action.setShortcut(FreeCADGui.IFC_saveshortcut)
            del FreeCADGui.IFC_saveshortcut
        if hasattr(FreeCADGui,"IFC_WBManipulator"):
            FreeCADGui.removeWorkbenchManipulator(FreeCADGui.IFC_WBManipulator)
            del FreeCADGui.IFC_WBManipulator
        wb.reloadActive()


def set_button(checked=False, setchecked=False):
    """Sets the lock button"""

    from PySide import QtGui  # lazy loading

    mw = FreeCADGui.getMainWindow()
    statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
    if hasattr(statuswidget, "lock_button"):
        lock_button = statuswidget.lock_button
        if checked:
            lock_button.setToolTip(text_on)
            icon = QtGui.QIcon(":/icons/IFC.svg")
            lock_button.setIcon(icon)
            if setchecked:
                lock_button.setChecked(True)
        else:
            lock_button.setToolTip(text_off)
            image = QtGui.QImage(":/icons/IFC.svg")
            grayscale = image.convertToFormat(QtGui.QImage.Format_Grayscale8)
            grayscale = grayscale.convertToFormat(image.format())
            grayscale.setAlphaChannel(image)
            icon = QtGui.QIcon(QtGui.QPixmap.fromImage(grayscale))
            lock_button.setIcon(icon)
            if setchecked:
                lock_button.setChecked(False)


def unlock_document():
    """Unlocks the active document"""

    from nativeifc import ifc_tools  # lazy loading

    doc = FreeCAD.ActiveDocument
    if "IfcFilePath" in doc.PropertiesList:
        # this is a locked document
        doc.openTransaction("Unlock document")
        children = [o for o in doc.Objects if not o.InList]
        project = None
        if children:
            project = ifc_tools.create_document_object(
                doc, filename=doc.IfcFilePath, silent=True
            )
            project.Group = children
        props = ["IfcFilePath", "Modified", "Proxy", "Schema"]
        props += [p for p in doc.PropertiesList if doc.getGroupOfProperty(p) == "IFC"]
        for prop in props:
            doc.removeProperty(prop)
        if project:
            project.Modified = True
        doc.commitTransaction()
        doc.recompute()


def lock_document():
    """Locks the active document"""

    from nativeifc import ifc_tools  # lazy loading
    from importers import exportIFC
    from nativeifc import ifc_geometry
    from PySide import QtCore

    doc = FreeCAD.ActiveDocument
    products = []
    spatial = []
    ifcfile = None
    if "IfcFilePath" not in doc.PropertiesList:
        # this is not a locked document
        projects = [o for o in doc.Objects if getattr(o, "Class", None) == "IfcProject"]
        if len(projects) == 1:
            # 1 there is a project already
            project = projects[0]
            children = project.OutListRecursive
            rest = [o for o in doc.Objects if o not in children and o != project]
            doc.openTransaction("Lock document")
            ifc_tools.convert_document(
                doc, filename=project.IfcFilePath, strategy=3, silent=True
            )
            ifcfile = doc.Proxy.ifcfile
            if rest:
                # 1b some objects are outside
                objs = find_toplevel(rest)
                prefs, context = ifc_tools.get_export_preferences(ifcfile)
                products = exportIFC.export(objs, ifcfile, preferences=prefs)
                for product in products.values():
                    if not getattr(product, "ContainedInStructure", None):
                        if not getattr(product, "FillsVoids", None):
                            if not getattr(product, "VoidsElements", None):
                                if not getattr(product, "Decomposes", None):
                                    new = ifc_tools.create_object(product, doc, ifcfile)
                                    children = ifc_tools.create_children(
                                        new, ifcfile, recursive=True
                                    )
                                    for o in [new] + children:
                                        ifc_geometry.add_geom_properties(o)
                for n in [o.Name for o in rest]:
                    doc.removeObject(n)
            else:
                # 1a all objects are already inside a project
                pass
            doc.removeObject(project.Name)
            doc.Modified = True
            # all objects have been deleted, we need to show at least something
            if not doc.Objects:
                ifc_tools.create_children(doc, ifcfile, recursive=True)
            doc.commitTransaction()
            doc.recompute()
        elif len(projects) > 1:
            # 2 there is more than one project
            FreeCAD.Console.PrintError(
                "Unable to lock this document because it contains several IFC documents\n"
            )
            QtCore.QTimer.singleShot(100, on_toggle_lock)
        elif doc.Objects:
            # 3 there is no project but objects
            doc.openTransaction("Lock document")
            ifc_tools.convert_document(doc, silent=True)
            ifcfile = doc.Proxy.ifcfile
            objs = find_toplevel(doc.Objects)
            prefs, context = ifc_tools.get_export_preferences(ifcfile)
            exportIFC.export(objs, ifcfile, preferences=prefs)
            for n in [o.Name for o in doc.Objects]:
                if doc.getObject(n):
                    doc.removeObject(n)
            ifc_tools.create_children(doc, ifcfile, recursive=True)
            doc.Modified = True
            doc.commitTransaction()
            doc.recompute()
        else:
            # 4 this is an empty document
            doc.openTransaction("Create IFC document")
            ifc_tools.convert_document(doc)
            doc.commitTransaction()
            doc.recompute()
        # reveal file contents if needed
        if "IfcFilePath" in doc.PropertiesList:
            create = True
            for o in doc.Objects:
                # scan for site or building
                if getattr(o, "IfcClass", "") in ("IfcSite", "IfcBuilding"):
                    create = False
                    break
            if create:
                if not ifcfile:
                    ifcfile = doc.Proxy.ifcfile
                ifc_tools.create_children(doc, recursive=False)


def find_toplevel(objs):
    """Finds the top-level objects from the list"""

    import Draft
    # filter out any object that depend on another from the list
    nobjs = []
    for obj in objs:
        for parent in obj.InListRecursive:
            if parent in objs:
                # exception: The object is hosting another
                if hasattr(parent,"Host") and parent.Host == obj:
                    nobjs.append(obj)
                elif hasattr(parent,"Hosts") and obj in parent.Hosts:
                    nobjs.append(obj)
                break
        else:
            nobjs.append(obj)
    # filter out non-convertible objects
    objs = filter_out(nobjs)
    return objs


def filter_out(objs):
    """Filter out objects that should not be converted to IFC"""

    nobjs = []
    for obj in objs:
        if obj.isDerivedFrom("Part::Feature"):
            nobjs.append(obj)
        elif obj.isDerivedFrom("Mesh::Feature"):
            nobjs.append(obj)
        elif Draft.is_group(obj):
            if filter_out(obj.Group):
                # only append groups that contain exportable objects
                nobjs.append(obj)
            else:
                print("DEBUG: Filtering out",obj.Label)
        elif obj.isDerivedFrom("App::Feature"):
            if Draft.get_type(obj) in ("Dimension","LinearDimension","Layer","Text","DraftText"):
                nobjs.append(obj)
            else:
                print("DEBUG: Filtering out",obj.Label)
        else:
            print("DEBUG: Filtering out",obj.Label)
    return nobjs


def get_lock_status():
    """Returns the status of the IFC lock button"""

    if not FreeCAD.GuiUp:
        return PARAMS.GetBool("SingleDoc")
    from PySide import QtGui
    mw = FreeCADGui.getMainWindow()
    statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
    if hasattr(statuswidget, "lock_button"):
        return statuswidget.lock_button.isChecked()
    else:
        return False


# add entry to File menu
# https://github.com/FreeCAD/FreeCAD/pull/10933
class IFC_WBManipulator:
    def modifyMenuBar(self):
        return [{"remove":"Std_Save"},
                {"remove":"Std_SaveAs"},
                {"insert":"IFC_Save", "menuItem":"Std_SaveCopy"},
                {"insert":"IFC_SaveAs", "menuItem":"Std_SaveCopy"},
               ]
    def modifyToolBars(self):
        return [{"remove" : "Std_Save"},
                {"append" : "IFC_Save", "toolBar" : "File"},
               ]
