# Copyright (c) 2022 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
# Modified by BigRep GmbH 2024
from typing import List, Optional

from PyQt6.QtCore import QObject, pyqtProperty

from UM import i18nCatalog
from UM.FileHandler.FileHandler import FileHandler
from UM.Scene.SceneNode import SceneNode

from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType

from .BigRepConnectOutputDeviceResponse import BigRepConnectOutputDeviceResponse
from .BigRepConnectApiClient import BigRepConnectApiClient


I18N_CATALOG = i18nCatalog("cura")


class BigRepConnectOutputDevice(NetworkedPrinterOutputDevice):
    """Output device class that forms the basis of Ultimaker networked printer output devices.

    Currently used for local networking and cloud printing using Ultimaker Connect.
    This base class primarily contains all the Qt properties and slots needed for the monitor page to work.
    """
    def __init__(self, api_client: BigRepConnectApiClient, output_device_data: BigRepConnectOutputDeviceResponse, parent: QObject = None) -> None:
        """Creates a new cloud output device

        :param api_client: The client that will run the API calls
        :param cluster: The device response received from the cloud API.
        :param parent: The optional parent of this output device.
        """

        # The following properties are expected on each networked output device.
        # Because the cloud connection does not off all of these, we manually construct this version here.
        # An example of why this is needed is the selection of the compatible file type when exporting the tool path.
        properties = {
            b"address": b"",
            b"name": output_device_data.name.encode() if output_device_data.name else b"",
            b"firmware_version": output_device_data.firmware_version.encode() if output_device_data.firmware_version else b"",
            b"printer_type": output_device_data.machine_type.encode() if output_device_data.machine_type else b""
        }

        super().__init__(
            device_id=output_device_data.serial_number,
            address="",
            connection_type=ConnectionType.CloudConnection,
            properties=properties,
            parent=parent
        )

        self.setName(self.getProperty("name"))
        # Set the display name of the printer type.
        definitions = CuraApplication.getInstance().getContainerRegistry().findContainers(id = self.printerType)
        self._printer_type_name = definitions[0].getName() if definitions else ""

        self._api = api_client
        self._account = api_client.account
        self._authentication_state = AuthState.NotAuthenticated
        self.setAuthenticationState(AuthState.NotAuthenticated)
        self._setInterfaceElements()
    
    @pyqtProperty(str, constant=True)
    def printerTypeName(self) -> str:
        """The display name of the printer."""

        return self._printer_type_name
    
    def _setInterfaceElements(self) -> None:
        """Set all the interface elements and texts for this output device."""

        # Set the output device priority; The higher the better
        self.setPriority(2)  # Make sure we end up below the local networking and above 'save to file'.
        self.setShortDescription(I18N_CATALOG.i18nc("@action:button", "Print via CONNECT"))
        self.setDescription(I18N_CATALOG.i18nc("@properties:tooltip", "Print via CONNECT"))
        self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via CONNECT"))

    def _onWriteFinished(self) -> None:
        CuraApplication.getInstance().getPreferences().setValue("local_file/last_used_type", "text/x-gcode")
        self.writeFinished.emit(self)

    # TODO: nodes := List[SceneNode] vs. Scene used in the requestUpload
    def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
                     file_handler: Optional[FileHandler] = None, filter_by_machine: bool = False, **kwargs) -> None:

        """Called when Cura requests an output device to receive a (G-code) file."""

        # TODO: [FW] Show progress
        # # Show an error message if we're already sending a job.
        # if self._progress.visible:
        #     PrintJobUploadBlockedMessage().show()
        #     return
        # self._progress.show()

        # Indicate we have started sending a job.
        self.writeStarted.emit(self)
        self._api.requestUpload(self.key, self._onWriteFinished)
        