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

# ***************************************************************************
# *   Copyright (c) 2009 Yorik van Havre <yorik@uncreated.net>              *
# *   Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
# *   Copyright (c) 2025 FreeCAD Project Association                        *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   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.                                 *
# *                                                                         *
# *   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 Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with FreeCAD; if not, write to the Free Software        *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************
"""Provides the task panel code for the Draft ShapeString tool."""
## @package task_shapestring
# \ingroup drafttaskpanels
# \brief Provides the task panel code for the Draft ShapeString tool.

## \addtogroup drafttaskpanels
# @{
import PySide.QtCore as QtCore
import PySide.QtGui as QtGui

import FreeCAD as App
import FreeCADGui as Gui
import Draft_rc
import WorkingPlane
from draftguitools import gui_tool_utils
from draftutils import params
from draftutils.translate import translate
from DraftVecUtils import toString

# So the resource file doesn't trigger errors from code checkers (flake8)
True if Draft_rc.__name__ else False


class ShapeStringTaskPanel:
    """Base class for Draft_ShapeString task panel."""

    def __init__(self, point=None, size=None, string="", font=""):

        self.form = Gui.PySideUic.loadUi(":/ui/TaskShapeString.ui")
        self.form.setWindowTitle(translate("draft", "ShapeString"))
        self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_ShapeString.svg"))

        self.global_mode = params.get_param("GlobalMode")
        self.form.cbGlobalMode.setChecked(self.global_mode)
        self.change_coord_labels()

        self.wp = WorkingPlane.get_working_plane()
        if point is not None:
            self.point = point
        elif self.global_mode:
            self.point = App.Vector()
        else:
            self.point = self.wp.position
        self.display_point(self.point)
        self.pointPicked = False

        self.height = size if size is not None else params.get_param("ShapeStringHeight")
        self.form.sbHeight.setProperty("rawValue", self.height)
        self.text = string if string else params.get_param("ShapeStringText")
        self.form.leText.setText(self.text)
        self.platform_win_dialog("Overwrite")
        self.font_file = font if font else params.get_param("ShapeStringFontFile")
        self.form.fcFontFile.setFileName(self.font_file)

        # Prevent cyclic processing of point values:
        self.display_point_active = False
        # Default for the "DontUseNativeFontDialog" preference:
        self.font_dialog_pref = False
        # Dummy attribute used by gui_tool_utils.getPoint in action method
        self.node = None

        self.form.sbX.valueChanged.connect(self.set_point_x)
        self.form.sbY.valueChanged.connect(self.set_point_y)
        self.form.sbZ.valueChanged.connect(self.set_point_z)
        if hasattr(self.form.cbGlobalMode, "checkStateChanged"):  # Qt version >= 6.7.0
            self.form.cbGlobalMode.checkStateChanged.connect(self.set_global_mode)
        else:  # Qt version < 6.7.0
            self.form.cbGlobalMode.stateChanged.connect(self.set_global_mode)
        self.form.pbReset.clicked.connect(self.reset_point)
        self.form.sbHeight.valueChanged.connect(self.set_height)
        self.form.leText.textEdited.connect(self.set_text)
        self.form.fcFontFile.fileNameSelected.connect(self.set_file)

    def action(self, arg):
        """scene event handler"""
        if arg["Type"] == "SoKeyboardEvent":
            if arg["Key"] == "ESCAPE":
                self.reject()
        elif arg["Type"] == "SoLocation2Event":  # mouse movement detection
            if not self.pointPicked:
                self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg, noTracker=True)
                self.display_point(self.point)
                self.update_hints()
        elif arg["Type"] == "SoMouseButtonEvent":
            if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
                self.display_point(self.point)
                self.pointPicked = True
                self.update_hints()

    def change_coord_labels(self):
        if self.global_mode:
            self.form.labelX.setText(translate("draft", "Global {}").format("X"))
            self.form.labelY.setText(translate("draft", "Global {}").format("Y"))
            self.form.labelZ.setText(translate("draft", "Global {}").format("Z"))
        else:
            self.form.labelX.setText(translate("draft", "Local {}").format("X"))
            self.form.labelY.setText(translate("draft", "Local {}").format("Y"))
            self.form.labelZ.setText(translate("draft", "Local {}").format("Z"))

    def display_point(self, point):
        """Display the selected point."""
        self.display_point_active = True
        if not self.global_mode:
            point = self.wp.get_local_coords(point)
        self.form.sbX.setProperty("rawValue", point.x)
        self.form.sbY.setProperty("rawValue", point.y)
        self.form.sbZ.setProperty("rawValue", point.z)
        self.display_point_active = False

    def escape_string(self, string):
        return string.replace("\\", "\\\\").replace('"', '\\"')

    def platform_win_dialog(self, flag):
        """Handle the type of dialog depending on the platform."""
        ParamGroup = App.ParamGet("User parameter:BaseApp/Preferences/Dialog")
        if flag == "Overwrite":
            if "DontUseNativeFontDialog" not in ParamGroup.GetBools():
                # initialize nonexisting one
                ParamGroup.SetBool("DontUseNativeFontDialog", True)
            param = ParamGroup.GetBool("DontUseNativeFontDialog")
            self.font_dialog_pref = ParamGroup.GetBool("DontUseNativeDialog")
            ParamGroup.SetBool("DontUseNativeDialog", param)
        elif flag == "Restore":
            ParamGroup.SetBool("DontUseNativeDialog", self.font_dialog_pref)

    def reset_point(self):
        """Reset the selected point and display new point in the task panel."""
        if self.global_mode:
            self.point = App.Vector()
        else:
            self.point = self.wp.position
        self.pointPicked = False
        self.display_point(self.point)
        self.update_hints()

    def set_file(self, val):
        """Assign the selected font file."""
        self.font_file = val
        params.set_param("ShapeStringFontFile", self.font_file)

    def set_global_mode(self, val):
        self.global_mode = bool(getattr(val, "value", val))
        params.set_param("GlobalMode", self.global_mode)
        self.change_coord_labels()
        self.display_point(self.point)

    def set_height(self, val):
        self.height = App.Units.Quantity(val).Value
        params.set_param("ShapeStringHeight", self.height)

    def set_point_coord(self, coord, val):
        """Change self.point based on  X, Y or Z value entered in the task panel.

        coord should be "x", "y" or "z".
        """
        if self.display_point_active:
            return
        if self.global_mode:
            point = self.point
        else:
            point = self.wp.get_local_coords(self.point)
        setattr(point, coord, App.Units.Quantity(val).Value)
        if self.global_mode:
            self.point = point
        else:
            self.point = self.wp.get_global_coords(point)

    def set_point_x(self, val):
        self.set_point_coord("x", val)

    def set_point_y(self, val):
        self.set_point_coord("y", val)

    def set_point_z(self, val):
        self.set_point_coord("z", val)

    def set_text(self, val):
        self.text = val
        params.set_param("ShapeStringText", self.text)

    def update_hints(self):
        if self.pointPicked:
            Gui.HintManager.hide()
        else:
            Gui.HintManager.show(
                Gui.InputHint(translate("draft", "%1 pick point"), Gui.UserInput.MouseLeft)
            )


class ShapeStringTaskPanelCmd(ShapeStringTaskPanel):
    """Task panel for Draft_ShapeString."""

    def __init__(self, sourceCmd):
        super().__init__()
        self.sourceCmd = sourceCmd

    def accept(self):
        """Execute when clicking the OK button."""
        self.create_object()
        self.reject()
        return True

    def create_object(self):
        """Create object in the current document."""
        Gui.addModule("Draft")
        Gui.addModule("WorkingPlane")
        cmd = "Draft.make_shapestring("
        cmd += 'String="' + self.escape_string(self.text) + '", '
        cmd += 'FontFile="' + self.escape_string(self.font_file) + '", '
        cmd += "Size=" + str(self.height) + ", "
        cmd += "Tracking=0.0"
        cmd += ")"
        self.sourceCmd.commit(
            translate("draft", "Create ShapeString"),
            [
                "ss = " + cmd,
                "pl = FreeCAD.Placement()",
                "pl.Base = " + toString(self.point),
                "pl.Rotation = WorkingPlane.get_working_plane().get_placement().Rotation",
                "ss.Placement = pl",
                "Draft.autogroup(ss)",
                "FreeCAD.ActiveDocument.recompute()",
            ],
        )

    def reject(self):
        """Run when clicking the Cancel button."""
        self.sourceCmd.finish()
        self.platform_win_dialog("Restore")
        return True


class ShapeStringTaskPanelEdit(ShapeStringTaskPanel):
    """Task panel for Draft ShapeString object in edit mode."""

    def __init__(self, vobj):

        self.obj = vobj.Object
        super().__init__(
            self.obj.Placement.Base, self.obj.Size.Value, self.obj.String, self.obj.FontFile
        )
        self.pointPicked = True
        self.call = Gui.activeView().addEventCallback("SoEvent", self.action)

    def accept(self):

        Gui.doCommand('ss = FreeCAD.ActiveDocument.getObject("' + self.obj.Name + '")')
        Gui.doCommand('ss.String="' + self.escape_string(self.text) + '"')
        Gui.doCommand('ss.FontFile="' + self.escape_string(self.font_file) + '"')
        Gui.doCommand("ss.Size=" + str(self.height))
        Gui.doCommand("ss.Placement.Base=" + toString(self.point))
        Gui.doCommand("FreeCAD.ActiveDocument.recompute()")

        self.reject()
        return True

    def finish(self):

        Gui.activeView().removeEventCallback("SoEvent", self.call)
        Gui.Snapper.off()
        Gui.Control.closeDialog()
        return None

    def reject(self):

        self.obj.ViewObject.Document.resetEdit()
        self.platform_win_dialog("Restore")
        return True


## @}
