# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.
# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import os
import logging
import time
import threading
import sys

import log
from awaitable import Awaitable

try:
    from PySide2.QtWidgets import QApplication
    from PySide2.QtCore import QCoreApplication
    from PySide2.QtCore import QUrl
    from PySide2.QtCore import QMetaObject
    from PySide2.QtCore import Qt
    from PySide2.QtQuick import QQuickView
    from PySide2.QtGui import QFont
except ImportError as e:
    logging.getLogger(__name__).exception("Qt interface disabled - Import error. No libs?")
    qt_found = False
else:
    qt_found = True
    from .core_model import CoreModel
    from .network_model import NetworkModel
    from .printer_model import PrinterModel
    from .wizard_model import WizardModel
    from .files_model import FilesModel


class QtInterface(threading.Thread, Awaitable):
    PACKAGE_PATH = os.path.dirname(__file__)
    QML_PATH = 'qml'
    MAIN_QML_PATH = 'main.qml'
    FONT_FAMILY = "Lato"
    NAME = "QtInterface"

    def __init__(self, app):
        self.logger = logging.getLogger(__name__)
        self.app = app
        self.qt_app = None
        threading.Thread.__init__(self, name=self.NAME)
        Awaitable.__init__(self, app)

    @log.log_exception
    def run(self):
        if not qt_found:
            self.logger.warning("Can't import PySide2. Skipping Qt interface.")
            return
        self.qt_app = QApplication([])
        self.defaultFont = QFont()
        self.defaultFont.setFamily(self.FONT_FAMILY)
        self.qt_app.setFont(self.defaultFont)
        self.qt_app.setQuitOnLastWindowClosed(False)
        self.view = QQuickView()
        self.view.setResizeMode(QQuickView.SizeRootObjectToView)
        self.view.closing.connect(self.app.raise_stop_flag)
        self.view.engine().addImportPath(os.path.join(self.PACKAGE_PATH, self.QML_PATH))
        #TODO automate models loading and initialization
        self.core_model = CoreModel(self.app)
        self.network_model = NetworkModel(self.core_model)
        self.printer_model = PrinterModel(self.core_model)
        #self.wizard_model = WizardModel(self.core_model)
        self.files_model = FilesModel(self.core_model)
        self.view.rootContext().setContextProperty('coreModel', self.core_model)
        self.view.rootContext().setContextProperty('networkModel', self.network_model)
        self.view.rootContext().setContextProperty('printerModel', self.printer_model)
        #self.view.rootContext().setContextProperty('wizardModel', self.wizard_model)
        self.view.rootContext().setContextProperty('filesModel', self.files_model)
        self.view.setSource(QUrl(os.path.join(self.PACKAGE_PATH, self.QML_PATH, self.MAIN_QML_PATH)))
        self.view.show()
        self.logger.info("Starting Qt loop...")
        self.qt_app.exec_()
        self.qt_app.deleteLater()
        self.logger.info("Qt loop finished")

    def close(self):
        if threading.current_thread().name == "QtInterface":
            self.logger.warning("Close called from QtInterface thread. Skipping close Qt interface.")
            return
        if self.qt_app:
            try:
                self.logger.info("Invoking QApplication close")
                QMetaObject.invokeMethod(QApplication.instance(), "closeAllWindows", Qt.BlockingQueuedConnection)
                QMetaObject.invokeMethod(self.view, "deleteLater", Qt.BlockingQueuedConnection)
                QMetaObject.invokeMethod(self.core_model, "deleteLater", Qt.BlockingQueuedConnection)
                QMetaObject.invokeMethod(self.network_model, "deleteLater", Qt.BlockingQueuedConnection)
                QMetaObject.invokeMethod(self.printer_model, "deleteLater", Qt.BlockingQueuedConnection)
                QMetaObject.invokeMethod(self.files_model, "deleteLater", Qt.BlockingQueuedConnection)
                self.logger.info("QApplication close was invoked")
                #self.logger.debug('Active threads:' + str(threading.enumerate()))
                #self.logger.debug('Current thread' + str(threading.currentThread()))
                QCoreApplication.instance().processEvents()
                QMetaObject.invokeMethod(QApplication.instance(), "quit", Qt.BlockingQueuedConnection)
            except RuntimeError:
                pass
            time.sleep(0.1)
            self.logger.info("QApplication had quit")

    def check_function(self):
        return self.qt_app or not qt_found
