# 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 ToolBit object."""

from typing import Optional
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
from ...shape.ui.shapewidget import ShapeWidget
from ...docobject.ui import DocumentObjectEditorWidget
from ..models.base import ToolBit

translate = FreeCAD.Qt.translate


class ToolBitPropertiesWidget(QtGui.QWidget):
    """
    A composite widget for editing the properties and shape of a ToolBit.
    """

    # Signal emitted when the toolbit data has been modified
    toolBitChanged = QtCore.Signal()
    toolNoChanged = QtCore.Signal(int)

    def __init__(
        self,
        toolbit: Optional[ToolBit] = None,
        tool_no: Optional[int] = None,
        parent=None,
        icon: bool = True,
    ):
        super().__init__(parent)
        self._toolbit = None
        self._show_shape = icon
        self._tool_no = tool_no

        # Set schema to user preference if no document is open
        # TODO: Add a preference for toolbit unit schema.
        # We probably want to look at making it possible to have a toolbit be metric
        # or imperial regardless of document settings / or user preferences, but for now this is sufficient.
        if FreeCAD.ActiveDocument is None:
            pref_schema = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt(
                "UserSchema", 0
            )
            FreeCAD.Units.setSchema(pref_schema)

        # UI Elements
        self._label_edit = QtGui.QLineEdit()
        self._id_label = QtGui.QLabel()  # Read-only ID
        self._id_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)

        theicon = toolbit.get_icon() if toolbit else None
        abbr = theicon.abbreviations if theicon else {}
        self._property_editor = DocumentObjectEditorWidget(property_suffixes=abbr)
        self._property_editor.setSizePolicy(
            QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding
        )
        self._shape_widget = None  # Will be created in load_toolbit

        # Layout
        toolbit_group_box = QtGui.QGroupBox(translate("CAM", "Toolbit"))
        form_layout = QtGui.QFormLayout(toolbit_group_box)
        form_layout.addRow(translate("CAM", "Label:"), self._label_edit)
        form_layout.addRow(translate("CAM", "ID:"), self._id_label)

        # Optional tool number edit field.
        self._tool_no_edit = QtGui.QSpinBox()
        self._tool_no_edit.setMinimum(1)
        self._tool_no_edit.setMaximum(99999999)
        if tool_no is not None:
            form_layout.addRow(translate("CAM", "Tool Number:"), self._tool_no_edit)

        main_layout = QtGui.QVBoxLayout(self)
        main_layout.addWidget(toolbit_group_box)

        properties_group_box = QtGui.QGroupBox(FreeCAD.Qt.translate("CAM", "Properties"))
        properties_group_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        properties_layout = QtGui.QVBoxLayout(properties_group_box)
        properties_layout.setSpacing(5)
        properties_layout.addWidget(self._property_editor)

        # Ensure the layout expands horizontally
        properties_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)

        # Set stretch factor to make property editor expand
        properties_layout.setStretchFactor(self._property_editor, 1)

        main_layout.addWidget(properties_group_box)
        main_layout.addStretch(1)

        # Layout for centering the shape widget (created later)
        self._shape_display_layout = QtGui.QHBoxLayout()
        self._shape_display_layout.addStretch(1)

        # Placeholder for the widget
        self._shape_display_layout.addStretch(1)
        main_layout.addLayout(self._shape_display_layout)

        # Connections
        self._label_edit.editingFinished.connect(self._on_label_changed)
        self._tool_no_edit.valueChanged.connect(self._on_tool_no_changed)
        self._property_editor.propertyChanged.connect(self.toolBitChanged)

        if toolbit:
            self.load_toolbit(toolbit)

    def _on_label_changed(self):
        """Update the toolbit's label when the line edit changes."""
        if self._toolbit and self._toolbit.obj:
            new_label = self._label_edit.text()
            if self._toolbit.obj.Label != new_label:
                self._toolbit.obj.Label = new_label
                self.toolBitChanged.emit()

    def _on_tool_no_changed(self, value):
        """Update the tool number when the line edit changes."""
        if self._tool_no != value:
            self._tool_no = value
            self.toolNoChanged.emit(value)

    def load_toolbit(self, toolbit: ToolBit):
        """Load a ToolBit object into the editor."""
        self._toolbit = toolbit
        if not self._toolbit or not self._toolbit.obj:
            # Clear or disable fields if toolbit is invalid
            self._label_edit.clear()
            self._label_edit.setEnabled(False)
            self._id_label.clear()
            self._tool_no_edit.clear()
            self._property_editor.setObject(None)
            # Clear existing shape widget if any
            if self._shape_widget:
                self._shape_display_layout.removeWidget(self._shape_widget)
                self._shape_widget.deleteLater()
                self._shape_widget = None
            self._tool_no_edit.setValue(1)
            self.setEnabled(False)
            return

        self.setEnabled(True)
        self._label_edit.setEnabled(True)
        self._label_edit.setText(self._toolbit.obj.Label)
        self._id_label.setText(self._toolbit.get_id())
        self._tool_no_edit.setValue(int(self._tool_no or 1))

        # Get properties and suffixes
        props_to_show = self._toolbit._get_props(("Shape", "Attributes"))
        icon = self._toolbit._tool_bit_shape.get_icon()
        suffixes = icon.abbreviations if icon else {}
        self._property_editor.setObject(self._toolbit.obj)
        self._property_editor.setPropertiesToShow(props_to_show, suffixes)

        # Clear old shape widget and create/add new one if shape exists
        if self._shape_widget:
            self._shape_display_layout.removeWidget(self._shape_widget)
            self._shape_widget.deleteLater()
            self._shape_widget = None

        if self._show_shape and self._toolbit._tool_bit_shape:
            self._shape_widget = ShapeWidget(shape=self._toolbit._tool_bit_shape, parent=self)
            self._shape_widget.setMinimumSize(200, 150)
            # Insert into the middle slot of the HBox layout
            self._shape_display_layout.insertWidget(1, self._shape_widget)

    def save_toolbit(self):
        """
        Applies changes from the editor widgets back to the ToolBit object.
        Note: Most changes are applied via signals, but this can be called
        for explicit save actions.
        """
        # Ensure label is updated if focus is lost without pressing Enter
        self._on_label_changed()

        # No need to explicitly save the toolbit object itself here,
        # as properties were modified directly on toolbit.obj


class ToolBitEditorPanel(QtGui.QWidget):
    """
    A widget for editing a ToolBit object, wrapping ToolBitEditorWidget
    and providing standard dialog buttons.
    """

    # Signals
    accepted = QtCore.Signal(ToolBit)
    rejected = QtCore.Signal()
    toolBitChanged = QtCore.Signal()  # Re-emit signal from inner widget

    def __init__(self, toolbit: ToolBit | None = None, parent=None):
        super().__init__(parent)

        # Create the main editor widget
        self._editor_widget = ToolBitPropertiesWidget(toolbit, self)

        # Create the button box
        buttons = QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel
        self._button_box = QtGui.QDialogButtonBox(buttons)

        # Connect button box signals to custom signals
        self._button_box.accepted.connect(self._accepted)
        self._button_box.rejected.connect(self.rejected.emit)

        # Layout
        main_layout = QtGui.QVBoxLayout(self)
        main_layout.addWidget(self._editor_widget)
        main_layout.addWidget(self._button_box)

        # Connect the toolBitChanged signal from the inner widget
        self._editor_widget.toolBitChanged.connect(self.toolBitChanged)

    def _accepted(self):
        self.accepted.emit(self._editor_widget._toolbit)

    def load_toolbit(self, toolbit: ToolBit):
        """Load a ToolBit object into the editor."""
        self._editor_widget.load_toolbit(toolbit)

    def save_toolbit(self):
        """Applies changes from the editor widgets back to the ToolBit object."""
        self._editor_widget.save_toolbit()


class ToolBitEditor(QtGui.QWidget):
    """
    A widget for editing a ToolBit object, wrapping ToolBitEditorWidget
    and providing standard dialog buttons.
    """

    # Signals
    toolBitChanged = QtCore.Signal()  # Re-emit signal from inner widget

    def __init__(
        self,
        toolbit: ToolBit,
        tool_no: Optional[int] = None,
        parent=None,
        icon: bool = False,
    ):
        super().__init__(parent)
        self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitEditor.ui")

        self.toolbit = toolbit
        self.tool_no = tool_no
        self.default_title = self.form.windowTitle()

        # Get first tab from the form, add the shape widget to the right.
        tool_tab_layout = self.form.toolTabLayout

        # Create a horizontal layout for the tab content
        tab_content_layout = QtGui.QHBoxLayout()

        # Add tool properties editor to the left with stretch
        props = ToolBitPropertiesWidget(toolbit, tool_no, self, icon=icon)

        # Wrap properties in a scroll area for vertical scrolling
        scroll_area = QtGui.QScrollArea()
        scroll_area.setWidget(props)
        scroll_area.setWidgetResizable(True)
        scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        scroll_area.setMinimumHeight(550)  # Set minimum height for the scroll area
        scroll_area.setMaximumHeight(600)  # Set maximum height to prevent excessive growth

        tab_content_layout.addWidget(scroll_area, 1)

        # Add shape widget to the right without stretch
        widget = ShapeWidget(toolbit._tool_bit_shape)
        tab_content_layout.addWidget(widget, 0)

        # Add the horizontal layout to the tab layout
        tool_tab_layout.addLayout(tab_content_layout)

        props.toolBitChanged.connect(self._update)
        props.toolNoChanged.connect(self._on_tool_no_changed)

        self.form.tabWidget.setCurrentIndex(0)
        self.form.tabWidget.currentChanged.connect(self._on_tab_switched)

        # Hide second tab (tool notes) for now.
        self.form.tabWidget.setTabVisible(1, False)

        # Feeds & Speeds
        self.feeds_tab_idx = None
        """
        TODO: disabled for now.
        if tool.supports_feeds_and_speeds():
            label = translate('CAM', 'Feeds && Speeds')
            self.feeds = FeedsAndSpeedsWidget(db, serializer, tool, parent=self)
            self.feeds_tab_idx = self.form.tabWidget.insertTab(1, self.feeds, label)
        else:
            self.feeds = None
            self.feeds_tab_idx = None

        self.form.lineEditCoating.setText(toolbit.get_coating())
        self.form.lineEditCoating.textChanged.connect(toolbit.set_coating)
        self.form.lineEditHardness.setText(toolbit.get_hardness())
        self.form.lineEditHardness.textChanged.connect(toolbit.set_hardness)
        self.form.lineEditMaterials.setText(toolbit.get_materials())
        self.form.lineEditMaterials.textChanged.connect(toolbit.set_materials)
        self.form.lineEditSupplier.setText(toolbit.get_supplier())
        self.form.lineEditSupplier.textChanged.connect(toolbit.set_supplier)
        self.form.plainTextEditNotes.setPlainText(tool.get_notes())
        self.form.plainTextEditNotes.textChanged.connect(self._on_notes_changed)
        """

    def _update(self):
        title = self.default_title
        tool_name = self.toolbit.label
        if tool_name:
            title = "{} - {}".format(tool_name, title)
        self.form.setWindowTitle(title)

    def _on_tab_switched(self, index):
        if index == self.feeds_tab_idx:
            self.feeds.update()

    def _on_notes_changed(self):
        self.toolbit.set_notes(self.form.plainTextEditNotes.toPlainText())

    def _on_tool_no_changed(self, value):
        self.tool_no = value

    def get_tool_no(self):
        return self.tool_no

    def show(self):
        return self.form.exec_()
