import uuid

from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Slot, Signal, QUrl

from artemis.utils.constants import Constants, Messages
from artemis.utils.sys_utils import open_directory, make_tar, unpack_tar
from artemis.utils.sql_utils import ArtemisDB, ArtemisSIG
from artemis.utils.update_utils import UpdateManager
from artemis.utils.path_utils import normalize_dialog_path
from artemis.utils.path_utils import DATA_DIR
from artemis.utils.config_utils import CONFIGURE_QT

from artemis.ui.preferences import UIPreferences
from artemis.ui.dbmanager import UIdbmanager
from artemis.ui.signaleditor import UIsignaleditor
from artemis.ui.downloader import UIDownloader
from artemis.ui.spaceweather import UIspaceweather
from artemis.ui.documentsmanager import UIdocumentsmanager
from artemis.ui.categoryeditor import UIcategoryeditor
from artemis.ui.filters import FiltersManager


class UIArtemis(QObject):
    # Python > QML Signals
    close_ui = Signal()
    populate_sig_list = Signal(list)
    populate_sig_details = Signal(list)
    
    clear_list = Signal()
    clear_signal_page = Signal()
    lock_audio_player = Signal()
    lock_menu = Signal(bool, bool)

    show_dialog_popup = Signal(str, str, str)
    show_dialog_download_db = Signal(str, str, str)
    show_dialog_update_artemis = Signal(str, str, str, bool)
    update_info_bar = Signal(str, str)


    def __init__(self):
        super().__init__()

        # Main UI initialization
        self._engine = QQmlApplicationEngine()
        self._engine.rootContext().setContextProperty('APPLICATION_VERSION', Constants.APPLICATION_VERSION)
        self._engine.rootContext().setContextProperty('PYTHON_VERSION', Constants.PYTHON_VERSION)
        self._engine.rootContext().setContextProperty('QT_VERSION', Constants.QT_VERSION)
        self._engine.load('qrc:/ui/Artemis.qml')
        self._window = self._engine.rootObjects()[0]

        self._window_signal = self._window.findChild(QObject, "signalPageObj")
        self._window_signal_list = self._window.findChild(QObject, "signalListObj")

        self.loaded_db = None
        self.loaded_sig = None

        self._connect()

        # Creating istances for other windows
        self.preferences = UIPreferences(self)
        self.dbmanager = UIdbmanager(self)
        self.spaceweather = UIspaceweather(self)
        self.docmanager = UIdocumentsmanager(self)
        self.sigeditor = UIsignaleditor(self)
        self.cateditor = UIcategoryeditor(self)
        self.downloader = UIDownloader(self)

        self.filters_manager = FiltersManager(self)
        self.update_manager = UpdateManager(self)

        self.autoload_db()


    def _connect(self):
        # QML > Python connections
        self._window.showDBmanager.connect(self.show_dbmanager_ui)
        self._window.loadSignal.connect(self.load_sig)
        self._window.showPref.connect(self.show_pref_ui)
        self._window.openSigEditor.connect(self.open_sig_editor)
        self._window.checkForUpdate.connect(self.check_for_update)
        self._window.updateDb.connect(self.update_db)
        self._window.updateArtemis.connect(self.update_artemis)
        self._window.showSpaceWeather.connect(self.show_space_weather_ui)
        self._window.openDbDirectory.connect(self.open_db_directory)
        self._window.showCatManager.connect(self.open_cat_manager)
        
        self._window.newDb.connect(self.new_db)
        self._window.exportDb.connect(self.export_db)
        self._window.importDb.connect(self.import_db)

        self._window_signal.openDocManager.connect(self.show_documentsmanager_ui)
        self._window_signal.openSigEditor.connect(self.open_sig_editor)
        self._window_signal.deleteCatTag.connect(self.delete_cat_tag)
        self._window_signal.addCatTag.connect(self.add_cat_tag)

        # Python > QML connections
        self.close_ui.connect(self._window.close)
        self.update_info_bar.connect(self._window.bottomInfoBar)
        self.show_dialog_popup.connect(self._window.openGeneralDialog)
        self.show_dialog_download_db.connect(self._window.openDialogDownloadDb)
        self.show_dialog_update_artemis.connect(self._window.openDialogUpdateArtemis)
        self.lock_menu.connect(self._window.lockMenu)

        self.populate_sig_list.connect(self._window_signal_list.populateList)
        self.clear_list.connect(self._window_signal_list.clearList)

        self.populate_sig_details.connect(self._window_signal.populateSignalParam)
        self.lock_audio_player.connect(self._window_signal.lockPlayer)
        self.clear_signal_page.connect(self._window_signal.resetAll)


    def load_db(self, db_dir_name):
        """ Load the DB and populate the signals list

        Args:
            db_dir_name (str): folder name in the data folder
        """
        # Loading DB
        self.loaded_db = ArtemisDB(db_dir_name)
        self.loaded_db.load()
        # Clearing UI
        self.lock_menu.emit(False, self.loaded_db.is_sigid)
        self.clear_signal_page.emit()
        # Populating UI
        self.filters_manager.load_filter_lists()
        self.populate_sig_list.emit(self.loaded_db.all_signals)
        # Updating status bar
        self.bottom_info_bar(f"{self.loaded_db.name} v{self.loaded_db.version} | {self.loaded_db.count_signals} signals", "info")


    @Slot(int)
    def load_sig(self, sig_id):
        """ Load the selected signal and populate the SignalPage

        Args:
            sig_id (int): SIG_ID of the signal to be loaded 
        """
        self.loaded_sig = ArtemisSIG(self.loaded_db)
        self.loaded_sig.load(sig_id)

        self.populate_sig_details.emit([self.loaded_sig.summary])


    def show_pref_ui(self):
        """ Load the preference windows
        """
        self.preferences.load_preferences_ui()


    def show_dbmanager_ui(self):
        """ Load the DB manager windows
        """
        self.dbmanager.load_dbmanager_ui()


    @Slot(str, list, bool)
    def open_sig_editor(self, type, sig_param, is_new):
        """ Open the signal editor windows
            Called when the user want to add, edit or delete the signal or its parametes.
        """
        self.sigeditor.load_signaleditor_ui(type, sig_param, is_new)


    def show_space_weather_ui(self):
        """ Open the space weather windows
        """
        self.spaceweather.load_spaceweather_ui()


    def show_documentsmanager_ui(self):
        """ Open the documents manager windows
        """
        self.docmanager.load_documentsmanager_ui()


    def check_for_update(self):
        """ User manual check for updates updates
        """
        self.update_manager.update(show_popup=True)


    def set_update_available(self, available):
        """ Set the blinking light when updates are available
        """
        self._window.setProperty("updateAvailable", available)


    def dialog_download_db(self, message_type, title, message):
        """ Dialog popup for DB download confirmation
        """
        self.show_dialog_download_db.emit(message_type, title, message)


    def dialog_update_artemis(self, message_type, title, message, auto=False):
        """ Dialog popup for Artemis download confirmation
        """
        self.show_dialog_update_artemis.emit(message_type, title, message, auto)


    @Slot()
    def update_db(self):
        """ Start the download of the sigID DB
        """
        self.update_manager.download_db()


    @Slot()
    def update_artemis(self):
        """ Start the download of Artemis
        """
        self.update_manager.download_artemis()


    def open_db_directory(self):
        """ Open the local folder of the loaded DB
        """
        open_directory(self.loaded_db.db_dir)


    @Slot(str)
    def new_db(self, name):
        """ Create a new local DB

        Args:
            name (str): name of the new DB, hardcoded in sql info table
        """
        try:
            new_db = ArtemisDB(str(uuid.uuid4()))
            new_db.create(name)
            self.load_db(new_db.db_dir_name)
            self.dialog_popup(
                Messages.DIALOG_TYPE_INFO,
                Messages.GENERIC_SUCCESS,
                Messages.DB_CREATION_SUCCESS_MSG
            )
        except Exception as e:
            self.dialog_popup(
                Messages.DIALOG_TYPE_ERROR,
                Messages.GENERIC_ERROR,
                Messages.GENERIC_ERROR_MSG.format(e)
            )


    @Slot(str)
    def export_db(self, save_path):
        """ Export the load DB in a tar file. Does not use compression

        Args:
            save_path (str): destination path of the generated .tar file
        """
        try:
            url = QUrl(save_path)
            dest_path = url.toLocalFile()

            make_tar(dest_path, self.loaded_db.db_dir)
            self.dialog_popup(
                Messages.DIALOG_TYPE_INFO,
                Messages.GENERIC_SUCCESS,
                Messages.EXPORTING_SUCCESS_MSG
            )
        except Exception as e:
            self.dialog_popup(
                Messages.DIALOG_TYPE_ERROR,
                Messages.GENERIC_ERROR,
                Messages.GENERIC_ERROR_MSG.format(e)
            )


    @Slot(str)
    def import_db(self, tar_path):
        """ Import a new DB in the Artemis data folder

        Args:
            tar_path (str): Path of the archive to be imported
        """
        try:
            origin_path = normalize_dialog_path(tar_path)
            save_path = DATA_DIR / str(uuid.uuid4())
            unpack_tar(origin_path, save_path)
            self.dialog_popup(
                Messages.DIALOG_TYPE_INFO,
                Messages.GENERIC_SUCCESS,
                Messages.IMPORTING_SUCCESS_MSG
            )
        except Exception as e:
            self.dialog_popup(
                Messages.DIALOG_TYPE_ERROR,
                Messages.GENERIC_ERROR,
                Messages.GENERIC_ERROR_MSG.format(e)
            )


    @Slot(int)
    def add_cat_tag(self, clb_id):
        self.loaded_sig.insert_category(clb_id)
        self.load_db(self.loaded_db.db_dir_name)


    @Slot(int)
    def delete_cat_tag(self, cat_id):
        self.loaded_sig.delete_category(cat_id)
        self.load_db(self.loaded_db.db_dir_name)


    def open_cat_manager(self):
        """ Open the category manager windows
        """
        self.cateditor.load_cateditor_ui()


    def autoload_db(self):
        """ This will autoload the latest local sigID DB, if present
            according to the user settings
        """
        sig_id_db = self.dbmanager.get_latest_local_sigid_db()
        autoload = CONFIGURE_QT.value("Database", "autoload", 0)
        if sig_id_db is not None and int(autoload):
            self.load_db(sig_id_db.db_dir_name)


    def dialog_popup(self, message_type, title, message):
        """ Opens a general dialog popup

        Args:
            message_type (str): 'info', 'question', 'warn', 'error'
            title (str): header of the dialoog
            message (sstr): description inside the dialog
        """
        self.show_dialog_popup.emit(message_type, title, message)


    @Slot(str, str)
    def bottom_info_bar(self, message, message_type):
        """ Manage the footer info bar

        Args:
            message (str): text to be shown in the info bar
            message_type (str): 'info', 'warning'
        """
        self.update_info_bar.emit(message, message_type)
