# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2022 FreeCAD Project Association                        *
# *                                                                         *
# *   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/>.                                      *
# *                                                                         *
# ***************************************************************************

""" Contains a class for adding a single content item, as well as auxiliary classes for
its dependent dialog boxes. """

import os
from typing import Optional, Tuple, List

import FreeCAD
import FreeCADGui

from Addon import INTERNAL_WORKBENCHES

from PySide.QtWidgets import (
    QDialog,
    QLayout,
    QFileDialog,
    QTableWidgetItem,
    QSizePolicy,
)
from PySide.QtGui import QIcon
from PySide.QtCore import Qt

from addonmanager_devmode_validators import (
    VersionValidator,
    NameValidator,
    PythonIdentifierValidator,
)
from addonmanager_devmode_people_table import PeopleTable
from addonmanager_devmode_licenses_table import LicensesTable

# pylint: disable=too-few-public-methods

translate = FreeCAD.Qt.translate


class AddContent:
    """A dialog for adding a single content item to the package metadata."""

    def __init__(self, path_to_addon: str, toplevel_metadata: FreeCAD.Metadata):
        """path_to_addon is the full path to the toplevel directory of this Addon, and
        toplevel_metadata is to overall package.xml Metadata object for this Addon. This
        information is used to assist the use in filling out the dialog by providing
        sensible default values."""
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(os.path.dirname(__file__), "developer_mode_add_content.ui")
        )
        # These are in alphabetical order in English, but their actual label may be translated in
        # the GUI. Store their underlying type as user data.
        self.dialog.addonKindComboBox.setItemData(0, "macro")
        self.dialog.addonKindComboBox.setItemData(1, "preferencepack")
        self.dialog.addonKindComboBox.setItemData(2, "workbench")

        self.people_table = PeopleTable()
        self.licenses_table = LicensesTable()
        large_size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        large_size_policy.setHorizontalStretch(2)
        small_size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        small_size_policy.setHorizontalStretch(1)
        self.people_table.widget.setSizePolicy(large_size_policy)
        self.licenses_table.widget.setSizePolicy(small_size_policy)
        self.dialog.peopleAndLicenseshorizontalLayout.addWidget(
            self.people_table.widget
        )
        self.dialog.peopleAndLicenseshorizontalLayout.addWidget(
            self.licenses_table.widget
        )

        self.toplevel_metadata = toplevel_metadata
        self.metadata = None
        self.path_to_addon = path_to_addon.replace("/", os.path.sep)
        if self.path_to_addon[-1] != os.path.sep:
            self.path_to_addon += (
                os.path.sep
            )  # Make sure the path ends with a separator

        self.dialog.iconLabel.hide()  # Until we have an icon to display

        self.dialog.iconBrowseButton.clicked.connect(self._browse_for_icon_clicked)
        self.dialog.subdirectoryBrowseButton.clicked.connect(
            self._browse_for_subdirectory_clicked
        )
        self.dialog.tagsButton.clicked.connect(self._tags_clicked)
        self.dialog.dependenciesButton.clicked.connect(self._dependencies_clicked)
        self.dialog.freecadVersionsButton.clicked.connect(
            self._freecad_versions_clicked
        )

        self.dialog.versionLineEdit.setValidator(VersionValidator())
        self.dialog.prefPackNameLineEdit.setValidator(NameValidator())
        self.dialog.displayNameLineEdit.setValidator(NameValidator())
        self.dialog.workbenchClassnameLineEdit.setValidator(PythonIdentifierValidator())

    def exec(
        self,
        content_kind: str = "workbench",
        metadata: FreeCAD.Metadata = None,
        singleton: bool = True,
    ) -> Optional[Tuple[str, FreeCAD.Metadata]]:
        """Execute the dialog as a modal, returning a new Metadata object if the dialog
        is accepted, or None if it is rejected. This metadata object represents a single
        new content item. It's returned as a tuple with the object type as the first component,
        and the metadata object itself as the second."""
        if metadata:
            self.metadata = FreeCAD.Metadata(metadata)  # Deep copy
        else:
            self.metadata = FreeCAD.Metadata()
        self.dialog.singletonCheckBox.setChecked(singleton)
        if singleton:
            # This doesn't happen automatically the first time
            self.dialog.otherMetadataGroupBox.hide()
        index = self.dialog.addonKindComboBox.findData(content_kind)
        if index == -1:
            index = 2  # Workbench
            FreeCAD.Console.PrintWarning(
                translate("AddonsInstaller", "Unrecognized content kind '{}'").format(
                    content_kind
                )
                + "\n"
            )
        self.dialog.addonKindComboBox.setCurrentIndex(index)
        if metadata:
            self._populate_dialog(metadata)

        self.dialog.layout().setSizeConstraint(QLayout.SetFixedSize)
        result = self.dialog.exec()
        if result == QDialog.Accepted:
            return self._generate_metadata()
        return None

    def _populate_dialog(self, metadata: FreeCAD.Metadata) -> None:
        """Fill in the dialog with the details from the passed metadata object"""
        addon_kind = self.dialog.addonKindComboBox.currentData()
        if addon_kind == "workbench":
            self.dialog.workbenchClassnameLineEdit.setText(metadata.Classname)
        elif addon_kind == "macro":
            files = self.metadata.File
            if files:
                self.dialog.macroFileLineEdit.setText(files[0])
        elif addon_kind == "preferencepack":
            self.dialog.prefPackNameLineEdit.setText(self.metadata.Name)
        else:
            raise RuntimeError("Invalid data found for selection")

        # Now set the rest of it
        if metadata.Icon:
            self._set_icon(metadata.Icon)
        elif self.toplevel_metadata.Icon:
            if metadata.Subdirectory and metadata.Subdirectory != "./":
                self._set_icon("../" + self.toplevel_metadata.Icon)
            else:
                self._set_icon(self.toplevel_metadata.Icon)
        else:
            self.dialog.iconLabel.hide()
            self.dialog.iconLineEdit.setText("")

        if metadata.Subdirectory:
            self.dialog.subdirectoryLineEdit.setText(metadata.Subdirectory)
        else:
            self.dialog.subdirectoryLineEdit.setText("")

        self.dialog.displayNameLineEdit.setText(metadata.Name)
        self.dialog.descriptionTextEdit.setPlainText(metadata.Description)
        self.dialog.versionLineEdit.setText(metadata.Version)

        self.people_table.show(metadata)
        self.licenses_table.show(metadata, self.path_to_addon)

    def _set_icon(self, icon_relative_path):
        """Load the icon and display it, and its path, in the dialog."""
        icon_path = os.path.join(
            self.path_to_addon, icon_relative_path.replace("/", os.path.sep)
        )
        if os.path.isfile(icon_path):
            icon_data = QIcon(icon_path)
            if not icon_data.isNull():
                self.dialog.iconLabel.setPixmap(icon_data.pixmap(32, 32))
                self.dialog.iconLabel.show()
        else:
            FreeCAD.Console.PrintError(
                translate("AddonsInstaller", "Unable to locate icon at {}").format(
                    icon_path
                )
                + "\n"
            )
        self.dialog.iconLineEdit.setText(icon_relative_path)

    def _generate_metadata(self) -> Tuple[str, FreeCAD.Metadata]:
        """Create and return a new metadata object based on the contents of the dialog."""

        if not self.metadata:
            self.metadata = FreeCAD.Metadata()

        ##########################################################################################
        # Required data:
        current_data: str = self.dialog.addonKindComboBox.currentData()
        if current_data == "preferencepack":
            self.metadata.Name = self.dialog.prefPackNameLineEdit.text()
        elif self.dialog.displayNameLineEdit.text():
            self.metadata.Name = self.dialog.displayNameLineEdit.text()

        if current_data == "workbench":
            self.metadata.Classname = self.dialog.workbenchClassnameLineEdit.text()
        elif current_data == "macro":
            self.metadata.File = [self.dialog.macroFileLineEdit.text()]
        ##########################################################################################

        self.metadata.Subdirectory = self.dialog.subdirectoryLineEdit.text()
        self.metadata.Icon = self.dialog.iconLineEdit.text()

        # Early return if this is the only addon
        if self.dialog.singletonCheckBox.isChecked():
            return current_data, self.metadata

        # Otherwise, process the rest of the metadata (display name is already done)
        self.metadata.Description = (
            self.dialog.descriptionTextEdit.document().toPlainText()
        )
        self.metadata.Version = self.dialog.versionLineEdit.text()

        maintainers = []
        authors = []
        for row in range(self.dialog.peopleTableWidget.rowCount()):
            person_type = self.dialog.peopleTableWidget.item(row, 0).data()
            name = self.dialog.peopleTableWidget.item(row, 1).text()
            email = self.dialog.peopleTableWidget.item(row, 2).text()
            if person_type == "maintainer":
                maintainers.append({"name": name, "email": email})
            elif person_type == "author":
                authors.append({"name": name, "email": email})
        self.metadata.Maintainer = maintainers
        self.metadata.Author = authors

        licenses = []
        for row in range(self.dialog.licensesTableWidget.rowCount()):
            new_license = {
                "name": self.dialog.licensesTableWidget.item(row, 0).text,
                "file": self.dialog.licensesTableWidget.item(row, 1).text(),
            }
            licenses.append(new_license)
        self.metadata.License = licenses

        return self.dialog.addonKindComboBox.currentData(), self.metadata

    ###############################################################################################
    #                                         DIALOG SLOTS
    ###############################################################################################

    def _browse_for_icon_clicked(self):
        """Callback: when the "Browse..." button for the icon field is clicked"""
        subdir = self.dialog.subdirectoryLineEdit.text()
        start_dir = os.path.join(self.path_to_addon, subdir)
        new_icon_path, _ = QFileDialog.getOpenFileName(
            parent=self.dialog,
            caption=translate(
                "AddonsInstaller",
                "Select an icon file for this content item",
            ),
            dir=start_dir,
        )

        if not new_icon_path:
            return

        base_path = self.path_to_addon.replace("/", os.path.sep)
        icon_path = new_icon_path.replace("/", os.path.sep)
        if base_path[-1] != os.path.sep:
            base_path += os.path.sep

        if not icon_path.startswith(base_path):
            FreeCAD.Console.PrintError(
                translate("AddonsInstaller", "{} is not a subdirectory of {}").format(
                    icon_path, base_path
                )
                + "\n"
            )
            return
        self._set_icon(new_icon_path[len(base_path) :])
        self.metadata.Icon = new_icon_path[len(base_path) :]

    def _browse_for_subdirectory_clicked(self):
        """Callback: when the "Browse..." button for the subdirectory field is clicked"""
        subdir = self.dialog.subdirectoryLineEdit.text()
        start_dir = os.path.join(self.path_to_addon, subdir)
        new_subdir_path = QFileDialog.getExistingDirectory(
            parent=self.dialog,
            caption=translate(
                "AddonsInstaller",
                "Select the subdirectory for this content item",
            ),
            dir=start_dir,
        )
        if not new_subdir_path:
            return
        if new_subdir_path[-1] != "/":
            new_subdir_path += "/"

        # Three legal possibilities:
        #   1) This might be the toplevel directory, in which case we want to set
        #      metadata.Subdirectory to "./"
        #   2) This might be a subdirectory with the same name as the content item, in which case
        #      we don't need to set metadata.Subdirectory at all
        #   3) This might be some other directory name, but still contained within the top-level
        #      directory, in which case we want to set metadata.Subdirectory to the relative path

        # First, reject anything that isn't within the appropriate directory structure:
        base_path = self.path_to_addon.replace("/", os.path.sep)
        subdir_path = new_subdir_path.replace("/", os.path.sep)
        if not subdir_path.startswith(base_path):
            FreeCAD.Console.PrintError(
                translate("AddonsInstaller", "{} is not a subdirectory of {}").format(
                    subdir_path, base_path
                )
                + "\n"
            )
            return

        relative_path = subdir_path[len(base_path) :]
        if not relative_path:
            relative_path = "./"
        elif relative_path[-1] == os.path.sep:
            relative_path = relative_path[:-1]
        self.dialog.subdirectoryLineEdit.setText(relative_path)

    def _tags_clicked(self):
        """Show the tag editor"""
        tags = []
        if not self.metadata:
            self.metadata = FreeCAD.Metadata()
        if self.metadata:
            tags = self.metadata.Tag
        dlg = EditTags(tags)
        new_tags = dlg.exec()
        self.metadata.Tag = new_tags

    def _freecad_versions_clicked(self):
        """Show the FreeCAD version editor"""
        if not self.metadata:
            self.metadata = FreeCAD.Metadata()
        dlg = EditFreeCADVersions()
        dlg.exec(self.metadata)

    def _dependencies_clicked(self):
        """Show the dependencies editor"""
        if not self.metadata:
            self.metadata = FreeCAD.Metadata()
        dlg = EditDependencies()
        dlg.exec(self.metadata)  # Modifies metadata directly


class EditTags:
    """A dialog to edit tags"""

    def __init__(self, tags: List[str] = None):
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(os.path.dirname(__file__), "developer_mode_tags.ui")
        )
        self.original_tags = tags
        if tags:
            self.dialog.lineEdit.setText(", ".join(tags))

    def exec(self):
        """Execute the dialog, returning a list of tags (which may be empty, but still represents
        the expected list of tags to be set, e.g. the user may have removed them all).
        """
        result = self.dialog.exec()
        if result == QDialog.Accepted:
            new_tags: List[str] = self.dialog.lineEdit.text().split(",")
            clean_tags: List[str] = []
            for tag in new_tags:
                clean_tags.append(tag.strip())
            return clean_tags
        return self.original_tags


class EditDependencies:
    """A dialog to edit dependency information"""

    def __init__(self):
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(os.path.dirname(__file__), "developer_mode_dependencies.ui")
        )
        self.dialog.addDependencyToolButton.setIcon(
            QIcon.fromTheme("add", QIcon(":/icons/list-add.svg"))
        )
        self.dialog.removeDependencyToolButton.setIcon(
            QIcon.fromTheme("remove", QIcon(":/icons/list-remove.svg"))
        )
        self.dialog.addDependencyToolButton.clicked.connect(
            self._add_dependency_clicked
        )
        self.dialog.removeDependencyToolButton.clicked.connect(
            self._remove_dependency_clicked
        )
        self.dialog.tableWidget.itemDoubleClicked.connect(self._edit_dependency)
        self.dialog.tableWidget.itemSelectionChanged.connect(
            self._current_index_changed
        )

        self.dialog.removeDependencyToolButton.setDisabled(True)
        self.metadata = None

    def exec(self, metadata: FreeCAD.Metadata):
        """Execute the dialog"""
        self.metadata = FreeCAD.Metadata(metadata)  # Make a copy, in case we cancel
        row = 0
        for dep in self.metadata.Depend:
            dep_type = dep["type"]
            dep_name = dep["package"]
            dep_optional = dep["optional"]
            self._add_row(row, dep_type, dep_name, dep_optional)
            row += 1
        result = self.dialog.exec()
        if result == QDialog.Accepted:
            metadata.Depend = self.metadata.Depend

    def _add_dependency_clicked(self):
        """Callback: The add button was clicked"""
        dlg = EditDependency()
        dep_type, dep_name, dep_optional = dlg.exec()
        if dep_name:
            row = self.dialog.tableWidget.rowCount()
            self._add_row(row, dep_type, dep_name, dep_optional)
            self.metadata.addDepend(
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
            )

    def _add_row(self, row, dep_type, dep_name, dep_optional):
        """Utility function to add a row to the table."""
        translations = {
            "automatic": translate("AddonsInstaller", "Automatic"),
            "workbench": translate("AddonsInstaller", "Workbench"),
            "addon": translate("AddonsInstaller", "Addon"),
            "python": translate("AddonsInstaller", "Python"),
        }
        if dep_type and dep_name:
            self.dialog.tableWidget.insertRow(row)
            type_item = QTableWidgetItem(translations[dep_type])
            type_item.setData(Qt.UserRole, dep_type)
            self.dialog.tableWidget.setItem(row, 0, type_item)
            self.dialog.tableWidget.setItem(row, 1, QTableWidgetItem(dep_name))
            if dep_optional:
                self.dialog.tableWidget.setItem(
                    row, 2, QTableWidgetItem(translate("AddonsInstaller", "Yes"))
                )

    def _remove_dependency_clicked(self):
        """Callback: The remove button was clicked"""
        items = self.dialog.tableWidget.selectedItems()
        if items:
            row = items[0].row()
            dep_type = self.dialog.tableWidget.item(row, 0).data(Qt.UserRole)
            dep_name = self.dialog.tableWidget.item(row, 1).text()
            dep_optional = bool(self.dialog.tableWidget.item(row, 2))
            self.metadata.removeDepend(
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
            )
            self.dialog.tableWidget.removeRow(row)

    def _edit_dependency(self, item):
        """Callback: the dependency was double-clicked"""
        row = item.row()
        dlg = EditDependency()
        dep_type = self.dialog.tableWidget.item(row, 0).data(Qt.UserRole)
        dep_name = self.dialog.tableWidget.item(row, 1).text()
        dep_optional = bool(self.dialog.tableWidget.item(row, 2))
        new_dep_type, new_dep_name, new_dep_optional = dlg.exec(
            dep_type, dep_name, dep_optional
        )
        if dep_type and dep_name:
            self.metadata.removeDepend(
                {"package": dep_name, "type": dep_type, "optional": dep_optional}
            )
            self.metadata.addDepend(
                {
                    "package": new_dep_name,
                    "type": new_dep_type,
                    "optional": new_dep_optional,
                }
            )
            self.dialog.tableWidget.removeRow(row)
            self._add_row(row, dep_type, dep_name, dep_optional)

    def _current_index_changed(self):
        if self.dialog.tableWidget.selectedItems():
            self.dialog.removeDependencyToolButton.setDisabled(False)
        else:
            self.dialog.removeDependencyToolButton.setDisabled(True)


class EditDependency:
    """A dialog to edit a single piece of dependency information"""

    def __init__(self):
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(os.path.dirname(__file__), "developer_mode_edit_dependency.ui")
        )

        self.dialog.typeComboBox.addItem(
            translate("AddonsInstaller", "Internal Workbench"), "workbench"
        )
        self.dialog.typeComboBox.addItem(
            translate("AddonsInstaller", "External Addon"), "addon"
        )
        self.dialog.typeComboBox.addItem(
            translate("AddonsInstaller", "Python Package"), "python"
        )

        self.dialog.typeComboBox.currentIndexChanged.connect(
            self._type_selection_changed
        )
        self.dialog.dependencyComboBox.currentIndexChanged.connect(
            self._dependency_selection_changed
        )

        # Expect mostly Python dependencies...
        self.dialog.typeComboBox.setCurrentIndex(2)

        self.dialog.layout().setSizeConstraint(QLayout.SetFixedSize)

    def exec(
        self, dep_type="", dep_name="", dep_optional=False
    ) -> Tuple[str, str, bool]:
        """Execute the dialog, returning a tuple of the type of dependency (workbench, addon, or
        python), the name of the dependency, and a boolean indicating whether this is optional.
        """

        # If we are editing an existing row, set up the dialog:
        if dep_type and dep_name:
            index = self.dialog.typeComboBox.findData(dep_type)
            if index == -1:
                raise RuntimeError(f"Invalid dependency type {dep_type}")
            self.dialog.typeComboBox.setCurrentIndex(index)
            index = self.dialog.dependencyComboBox.findData(dep_name)
            if index == -1:
                index = self.dialog.dependencyComboBox.findData("other")
            self.dialog.dependencyComboBox.setCurrentIndex(index)
            self.dialog.lineEdit.setText(dep_name)
            self.dialog.optionalCheckBox.setChecked(dep_optional)

        # Run the dialog (modal)
        result = self.dialog.exec()
        if result == QDialog.Accepted:
            dep_type = self.dialog.typeComboBox.currentData()
            dep_optional = self.dialog.optionalCheckBox.isChecked()
            dep_name = self.dialog.dependencyComboBox.currentData()
            if dep_name == "other":
                dep_name = self.dialog.lineEdit.text()
            return dep_type, dep_name, dep_optional
        return "", "", False

    def _populate_internal_workbenches(self):
        """Add all known internal FreeCAD Workbenches to the list"""
        self.dialog.dependencyComboBox.clear()
        for display_name, name in INTERNAL_WORKBENCHES.items():
            self.dialog.dependencyComboBox.addItem(display_name, name)
        # No "other" option is supported for this type of dependency

    def _populate_external_addons(self):
        """Add all known addons to the list"""
        self.dialog.dependencyComboBox.clear()
        # pylint: disable=import-outside-toplevel
        from AddonManager import INSTANCE as AM_INSTANCE

        repo_dict = {}
        # We need a case-insensitive sorting of all repo types, displayed and sorted by their
        # display name, but keeping track of their official name as well (stored in the UserRole)
        for repo in AM_INSTANCE.item_model.repos:
            repo_dict[repo.display_name.lower()] = (repo.display_name, repo.name)
        sorted_keys = sorted(repo_dict.keys())
        for item in sorted_keys:
            self.dialog.dependencyComboBox.addItem(
                repo_dict[item][0], repo_dict[item][1]
            )
        self.dialog.dependencyComboBox.addItem(
            translate("AddonsInstaller", "Other..."), "other"
        )

    def _populate_allowed_python_packages(self):
        """Add all allowed python packages to the list"""
        self.dialog.dependencyComboBox.clear()
        # pylint: disable=import-outside-toplevel
        from AddonManager import INSTANCE as AM_INSTANCE

        packages = sorted(AM_INSTANCE.allowed_packages)
        for package in packages:
            self.dialog.dependencyComboBox.addItem(package, package)
        self.dialog.dependencyComboBox.addItem(
            translate("AddonsInstaller", "Other..."), "other"
        )

    def _type_selection_changed(self, _):
        """Callback: The type of dependency has been changed"""
        selection = self.dialog.typeComboBox.currentData()
        if selection == "workbench":
            self._populate_internal_workbenches()
        elif selection == "addon":
            self._populate_external_addons()
        elif selection == "python":
            self._populate_allowed_python_packages()
        else:
            raise RuntimeError("Invalid data found for selection")

    def _dependency_selection_changed(self, _):
        selection = self.dialog.dependencyComboBox.currentData()
        if selection == "other":
            self.dialog.lineEdit.show()
            self.dialog.otherNote.show()
        else:
            self.dialog.lineEdit.hide()
            self.dialog.otherNote.hide()


class EditFreeCADVersions:
    """A dialog to edit minimum and maximum FreeCAD version support"""

    def __init__(self):
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(
                os.path.dirname(__file__), "developer_mode_freecad_versions.ui"
            )
        )

    def exec(self, metadata: FreeCAD.Metadata):
        """Execute the dialog"""
        if metadata.FreeCADMin != "0.0.0":
            self.dialog.minVersionLineEdit.setText(metadata.FreeCADMin)
        if metadata.FreeCADMax != "0.0.0":
            self.dialog.maxVersionLineEdit.setText(metadata.FreeCADMax)
        result = self.dialog.exec()
        if result == QDialog.Accepted:
            if self.dialog.minVersionLineEdit.text():
                metadata.FreeCADMin = self.dialog.minVersionLineEdit.text()
            else:
                metadata.FreeCADMin = None
            if self.dialog.maxVersionLineEdit.text():
                metadata.FreeCADMax = self.dialog.maxVersionLineEdit.text()
            else:
                metadata.FreeCADMax = None


class EditAdvancedVersions:
    """A dialog to support mapping specific git branches, tags, or commits to specific
    versions of FreeCAD."""

    def __init__(self):
        self.dialog = FreeCADGui.PySideUic.loadUi(
            os.path.join(
                os.path.dirname(__file__), "developer_mode_advanced_freecad_versions.ui"
            )
        )

    def exec(self):
        """Execute the dialog"""
        self.dialog.exec()
