# SPDX-License-Identifier: LGPL-2.1-or-later

# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2023 Yorik van Havre <yorik@uncreated.net>              *
# *                                                                         *
# *   This file is part of FreeCAD.                                         *
# *                                                                         *
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
# *   under the terms of the GNU Lesser General Public License as           *
# *   published by the Free Software Foundation, either version 2.1 of the  *
# *   License, or (at your option) any later version.                       *
# *                                                                         *
# *   FreeCAD 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      *
# *   Lesser General Public License for more details.                       *
# *                                                                         *
# *   You should have received a copy of the GNU Lesser General Public      *
# *   License along with FreeCAD. If not, see                               *
# *   <https://www.gnu.org/licenses/>.                                      *
# *                                                                         *
# ***************************************************************************

"""This NativeIFC module handles the retrieval and display of geometry compositions of objects"""

import FreeCAD

TAB = 2


def get_geometry_tree(element, prefix=""):
    """Returns a list of elements representing an object's representation"""

    result = [prefix + str(element)]
    prefix += TAB * " "
    if getattr(element, "Representation", None):
        reps = element.Representation
        result.append(prefix + str(reps))
        prefix += prefix
        for rep in reps.Representations:
            result.extend(get_geometry_tree(rep, prefix))
    elif getattr(element, "Items", None):
        for it in element.Items:
            result.extend(get_geometry_tree(it, prefix))
    elif element.is_a("IfcPolyline"):
        for p in element.Points:
            result.append(prefix + str(p))
    elif element.is_a("IfcExtrudedAreaSolid"):
        result.append(prefix + str(element.ExtrudedDirection))
        result.extend(get_geometry_tree(element.SweptArea, prefix))
    elif element.is_a("IfcArbitraryClosedProfileDef"):
        result.extend(get_geometry_tree(element.OuterCurve, prefix))
    elif element.is_a("IfcArbitraryProfileDefWithVoids"):
        result.extend(get_geometry_tree(element.OuterCurve, prefix))
        for inn in element.InnerCurves:
            result.extend(get_geometry_tree(inn, prefix))
    elif element.is_a("IfcMappedItem"):
        result.extend(get_geometry_tree(element.MappingSource[1], prefix))
    elif element.is_a("IfcBooleanClippingResult"):
        result.extend(get_geometry_tree(element.FirstOperand, prefix))
        result.extend(get_geometry_tree(element.SecondOperand, prefix))
    elif element.is_a("IfcBooleanResult"):
        result.extend(get_geometry_tree(element.FirstOperand, prefix))
        result.extend(get_geometry_tree(element.SecondOperand, prefix))
    elif element.is_a("IfcHalfSpaceSolid"):
        result.extend(get_geometry_tree(element.BaseSurface, prefix))
    return result


def print_geometry_tree(element):
    """Same as get_geometry_tree but printed"""

    for line in get_geometry_tree(element):
        print(line)


def show_geometry_tree(element):
    """Same as get_geometry_tree but in a Qt dialog"""

    import Arch_rc
    import FreeCADGui  # lazy import
    from . import ifc_tools
    from PySide import QtWidgets

    if isinstance(element, FreeCAD.DocumentObject):
        element = ifc_tools.get_ifc_element(element)
        if not element:
            return
    dlg = FreeCADGui.PySideUic.loadUi(":/ui/dialogTree.ui")
    tops = {}
    for line in get_geometry_tree(element):
        psize = (len(line) - len(line.lstrip())) / TAB
        wline = QtWidgets.QTreeWidgetItem([line.lstrip()])
        if not psize:
            dlg.geomtree.addTopLevelItem(wline)
        else:
            parent = tops[psize - 1]
            parent.addChild(wline)
        tops[psize] = wline
    dlg.geomtree.expandAll()
    dlg.setWindowTitle(dlg.windowTitle() + " " + element.Name)
    dlg.geomtree.currentItemChanged.connect(show_properties)
    dlg.proptree.itemChanged.connect(edit_property)
    result = dlg.exec_()
    if result:
        props = [r.split("::") for r in dlg.proptree.toolTip().split(";;;;") if r]
        if not props:
            return
        obj = ifc_tools.get_object(element)
        ifcfile = ifc_tools.get_ifcfile(obj)
        modified = False
        for prop in props:
            elt = ifcfile[int(prop[0])]
            attrib = prop[1]
            if attrib not in dir(elt):
                print("DEBUG: Unknown attribute:", attrib)
                continue
            value = prop[3]
            if isfloat(getattr(elt, attrib)):
                try:
                    value = float(value)
                except:
                    print("DEBUG: wrong value for", attrib, ":", value)
                    continue
            ifc_tools.set_attribute(ifcfile, elt, attrib, value)
            modified = True
        if modified:
            obj.touch()
            proj = ifc_tools.get_project(obj)
            proj.Modified = True
            obj.Document.recompute()


def isfloat(s):
    """Tells if the given string is a number"""

    try:
        float(s)
    except ValueError:
        return False
    print(s)
    return True


def show_properties(current, previous):
    """Displays object properties"""

    import FreeCADGui
    from . import ifc_tools  # lazy loading
    from PySide import QtCore, QtWidgets

    ifcid = int(current.text(0).split("=", 1)[0].strip(" ").strip("#"))
    sel = FreeCADGui.Selection.getSelection()
    if len(sel) != 1:
        return
    obj = sel[0]
    ifcfile = ifc_tools.get_ifcfile(obj)
    if not ifcfile:
        return
    elt = ifcfile[ifcid]
    box = current.treeWidget().parentWidget().widget(1)
    box.setTitle("#" + str(ifcid) + ": " + elt.is_a())
    props = [p for p in dir(elt) if p[0].isupper()]
    # allow only numeric values?
    # props = [p for p in  props if isfloat(str(getattr(elt,p)))]
    props = [p for p in props if not str(getattr(elt, p)).startswith("#")]
    props = [p for p in props if not str(getattr(elt, p)).startswith("(")]
    props = [p for p in props if p not in ["Position", "LayerAssignments", "StyledByItem"]]
    proptree = box.children()[0].itemAt(0).widget()
    proptree.clear()
    proptree.setHorizontalHeaderLabels(["Property", "Value"])
    proptree.setRowCount(len(props))
    for i, prop in enumerate(props):
        value = str(getattr(elt, prop))
        if value == "None":
            value = ""
        r1 = QtWidgets.QTableWidgetItem(prop)
        r1.setFlags(QtCore.Qt.ItemIsSelectable)
        proptree.setItem(i, 0, r1)
        r2 = QtWidgets.QTableWidgetItem(value)
        r2.setToolTip(value)
        if value.startswith("#") or value.startswith("(") or prop == "GlobalId":
            r2.setFlags(QtCore.Qt.ItemIsSelectable)
        proptree.setItem(i, 1, r2)
    if "Position" in dir(elt):
        i = proptree.rowCount()
        proptree.setRowCount(i + 6)
        position = FreeCAD.Vector(elt.Position.Location.Coordinates)
        axis = FreeCAD.Vector(elt.Position.Axis.DirectionRatios)
        xref = FreeCAD.Vector(elt.Position.RefDirection.DirectionRatios)
        rotation = FreeCAD.Rotation(axis, xref, FreeCAD.Vector(), "ZXY").toEulerAngles("XYZ")
        rotation = FreeCAD.Vector(rotation)
        for c in ["x", "y", "z"]:
            r1 = QtWidgets.QTableWidgetItem("Position " + c.upper())
            r1.setFlags(QtCore.Qt.ItemIsSelectable)
            proptree.setItem(i, 0, r1)
            v = str(getattr(position, c))
            r2 = QtWidgets.QTableWidgetItem(v)
            r2.setToolTip(v)
            proptree.setItem(i, 1, r2)
            i += 1
        for c in ["x", "y", "z"]:
            r1 = QtWidgets.QTableWidgetItem("Rotation " + c.upper())
            r1.setFlags(QtCore.Qt.ItemIsSelectable)
            proptree.setItem(i, 0, r1)
            v = str(getattr(rotation, c))
            r2 = QtWidgets.QTableWidgetItem(v)
            r2.setToolTip(v)
            proptree.setItem(i, 1, r2)
            i += 1


def edit_property(item):
    """Edits the value of a property"""

    if item.toolTip() and (item.text() != item.toolTip()):
        old = item.toolTip()
        new = item.text()
        table = item.tableWidget()
        prop = table.item(item.row(), 0).text()
        dlg = table.parent().parent().parent()
        line = dlg.geomtree.currentItem().text(0)
        ifcid = line.split("=", 1)[0].strip(" ").strip("#")
        strid = ";;;;" + "::".join([str(i) for i in [ifcid, prop, old, new]])
        dlg.proptree.setToolTip(dlg.proptree.toolTip() + strid)
