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

# ***************************************************************************
# *   Copyright (c) 2025 Samuel Abels <knipknap@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                                                                   *
# *                                                                         *
# ***************************************************************************

"""Widget for editing a list of properties of a DocumentObject."""

import re
from PySide import QtGui, QtCore
from .property import BasePropertyEditorWidget


def _get_label_text(prop_name):
    """Generate a human-readable label from a property name."""
    # Add space before capital letters (CamelCase splitting)
    s1 = re.sub(r"([A-Z][a-z]+)", r" \1", prop_name)
    # Add space before sequences of capitals (e.g., ID) followed by lowercase
    s2 = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1 \2", s1)
    # Add space before sequences of capitals followed by end of string
    s3 = re.sub(r"([A-Z]+)$", r" \1", s2)
    # Remove leading/trailing spaces and capitalize
    return s3.strip().capitalize()


class DocumentObjectEditorWidget(QtGui.QWidget):
    """
    A widget that displays a user friendly form for editing properties of a
    FreeCAD DocumentObject.
    """

    # Signal emitted when any underlying property value might have changed
    propertyChanged = QtCore.Signal()

    def __init__(self, obj=None, properties_to_show=None, property_suffixes=None, parent=None):
        """
        Initialize the editor widget.

        Args:
            obj (App.DocumentObject, optional): The object to edit. Defaults to None.
            properties_to_show (list[str], optional): List of property names to display.
                                                     Defaults to None (shows nothing).
            property_suffixes (dict[str, str], optional): Dictionary mapping property names
                                                          to suffixes for their labels.
                                                          Defaults to None.
            parent (QWidget, optional): The parent widget. Defaults to None.
        """
        super().__init__(parent)
        self._obj = obj
        self._properties_to_show = properties_to_show if properties_to_show else []
        self._property_suffixes = property_suffixes if property_suffixes else {}
        self._property_editors = {}  # Store {prop_name: editor_widget}

        self._layout = QtGui.QFormLayout(self)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._layout.setFieldGrowthPolicy(QtGui.QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)

        self._populate_form()

    def _clear_form(self):
        """Remove all rows from the form layout."""
        while self._layout.rowCount() > 0:
            self._layout.removeRow(0)
        self._property_editors.clear()

    def _populate_form(self):
        """Create and add property editors to the form."""
        self._clear_form()
        if not self._obj:
            return

        for prop_name in self._properties_to_show:
            # Only create an editor if the property exists on the object
            if not hasattr(self._obj, prop_name):
                continue

            editor_widget = BasePropertyEditorWidget.for_property(self._obj, prop_name, self)
            label_text = _get_label_text(prop_name)
            suffix = self._property_suffixes.get(prop_name)
            if suffix:
                label_text = f"{label_text} ({suffix}):"
            else:
                label_text = f"{label_text}:"

            label = QtGui.QLabel(label_text)
            self._layout.addRow(label, editor_widget)
            self._property_editors[prop_name] = editor_widget

            # Connect the editor's signal to our own signal
            editor_widget.propertyChanged.connect(self.propertyChanged)

    def setObject(self, obj):
        """Set or change the DocumentObject being edited."""
        if obj != self._obj:
            self._obj = obj
            # Re-populate might be too slow if only object changes,
            # better to just re-attach existing editors.
            # self._populate_form()
            for prop_name, editor in self._property_editors.items():
                editor.attachTo(self._obj, prop_name)

    def setPropertiesToShow(self, properties_to_show, property_suffixes=None):
        """Set or change the list of properties to display."""
        self._properties_to_show = properties_to_show if properties_to_show else []
        self._property_suffixes = property_suffixes if property_suffixes else {}
        self._populate_form()  # Rebuild the form completely

    def updateUI(self):
        """Update all child editor widgets from the object's properties."""
        for editor in self._property_editors.values():
            editor.updateWidget()

    def updateObject(self):
        """Update the object's properties from all child editor widgets."""
        # This might not be strictly necessary if signals are connected,
        # but can be useful for explicit save actions.
        for editor in self._property_editors.values():
            editor.updateProperty()
