# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from PyQt6.QtCore import Qt, QTimer

import cura.CuraApplication  # Imported this way to prevent circular dependencies.
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel
from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.Models.MachineModelUtils import fetchLayerHeight


class QualityProfilesDropDownMenuModel(ListModel):
    """QML Model for all built-in quality profiles. This model is used for the drop-down quality menu."""

    NameRole = Qt.ItemDataRole.UserRole + 1
    QualityTypeRole = Qt.ItemDataRole.UserRole + 2
    LayerHeightRole = Qt.ItemDataRole.UserRole + 3
    LayerHeightUnitRole = Qt.ItemDataRole.UserRole + 4
    AvailableRole = Qt.ItemDataRole.UserRole + 5
    QualityGroupRole = Qt.ItemDataRole.UserRole + 6
    QualityChangesGroupRole = Qt.ItemDataRole.UserRole + 7
    IsExperimentalRole = Qt.ItemDataRole.UserRole + 8

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

        self.addRoleName(self.NameRole, "name")
        self.addRoleName(self.QualityTypeRole, "quality_type")
        self.addRoleName(self.LayerHeightRole, "layer_height")
        self.addRoleName(self.LayerHeightUnitRole, "layer_height_unit")
        self.addRoleName(self.AvailableRole, "available") #Whether the quality profile is available in our current nozzle + material.
        self.addRoleName(self.QualityGroupRole, "quality_group")
        self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
        self.addRoleName(self.IsExperimentalRole, "is_experimental")

        application = cura.CuraApplication.CuraApplication.getInstance()
        machine_manager = application.getMachineManager()

        application.globalContainerStackChanged.connect(self._onChange)
        machine_manager.activeQualityGroupChanged.connect(self._onChange)
        machine_manager.activeMaterialChanged.connect(self._onChange)
        machine_manager.activeVariantChanged.connect(self._onChange)

        self._layer_height_unit = ""  # This is cached

        self._update_timer = QTimer()  # type: QTimer
        self._update_timer.setInterval(100)
        self._update_timer.setSingleShot(True)
        self._update_timer.timeout.connect(self._update)

        self._onChange()

    def _onChange(self) -> None:
        self._update_timer.start()

    def _update(self):
        Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))

        # CURA-6836
        # LabelBar is a repeater that creates labels for quality layer heights. Because of an optimization in
        # UM.ListModel, the model will not remove all items and recreate new ones every time there's an update.
        # Because LabelBar uses Repeater with Labels anchoring to "undefined" in certain cases, the anchoring will be
        # kept the same as before.
        self.setItems([])

        global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if global_stack is None:
            self.setItems([])
            Logger.log("d", "No active GlobalStack, set quality profile model as empty.")
            return

        if not self._layer_height_unit:
            unit = global_stack.definition.getProperty("layer_height", "unit")
            if not unit:
                unit = ""
            self._layer_height_unit = unit

        # Check for material compatibility
        if not cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMaterialsCompatible():
            Logger.log("d", "No active material compatibility, set quality profile model as empty.")
            self.setItems([])
            return

        quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()

        item_list = []
        for quality_group in quality_group_dict.values():
            layer_height = fetchLayerHeight(quality_group)

            item = {"name": quality_group.name,
                    "quality_type": quality_group.quality_type,
                    "layer_height": layer_height,
                    "layer_height_unit": self._layer_height_unit,
                    "available": quality_group.is_available,
                    "quality_group": quality_group,
                    "is_experimental": quality_group.is_experimental}

            item_list.append(item)

        # Sort items based on layer_height
        item_list = sorted(item_list, key = lambda x: x["layer_height"])

        self.setItems(item_list)
