# Copyright 2014-2020 Free Software Foundation, Inc.
# This file is part of GNU Radio
#
# GNU Radio Companion is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# GNU Radio Companion 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

from __future__ import absolute_import, print_function

# Standard modules
import logging
import textwrap
import os

from qtpy import QtCore, QtWidgets, QtGui, PYQT_VERSION, PYSIDE_VERSION

# Custom modules
from . import components
from .helpers.profiling import StopWatch

# Logging
# Setup the logger to use a different name than the file name
log = logging.getLogger("grc.application")


class Application(QtWidgets.QApplication):
    """
    This is the main QT application for GRC.
    It handles setting up the application components and actions and handles communication between different components in the system.
    """

    def __init__(self, settings, platform, file_path):
        # Note. Logger must have the correct naming convention to share handlers
        log.debug("__init__")
        self.settings = settings
        self.platform = platform
        config = platform.config

        self.qsettings = QtCore.QSettings(config.gui_prefs_file, QtCore.QSettings.IniFormat)
        log.debug(f"Using QSettings from {config.gui_prefs_file}")
        os.environ["QT_SCALE_FACTOR"] = self.qsettings.value('appearance/qt_scale_factor', "1.0", type=str)

        log.debug("Creating QApplication instance")
        QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts, True)
        QtWidgets.QApplication.__init__(self, settings.argv)

        self.theme = "light"
        if self.qsettings.value("appearance/theme", "dark") == "dark":
            try:
                import qdarkstyle

                self.setStyleSheet(qdarkstyle.load_stylesheet())
                self.theme = "dark"
            except ImportError:
                log.warning("Did not find QDarkstyle. Dark mode disabled")

        # Load the main view class and initialize QMainWindow
        log.debug("ARGV - {0}".format(settings.argv))
        log.debug("INSTALL_DIR - {0}".format(settings.path.INSTALL))

        # Global signals
        self.signals = {}

        # Setup the main application window
        log.debug("Creating main application window")
        stopwatch = StopWatch()
        self.MainWindow = components.MainWindow(file_path)
        stopwatch.lap("mainwindow")
        level_str = self.qsettings.value('grc/console_log_level', "info", type=str)
        if level_str == "info":
            self.Console = components.Console(logging.INFO)
        else:  # level_str == "debug"
            self.Console = components.Console(logging.DEBUG)
        stopwatch.lap("console")
        self.BlockLibrary = components.BlockLibrary()
        stopwatch.lap("blocklibrary")
        # self.DocumentationTab = components.DocumentationTab()
        # stopwatch.lap('documentationtab')
        self.WikiTab = components.WikiTab()
        stopwatch.lap("wikitab")
        self.VariableEditor = components.VariableEditor()
        stopwatch.lap("variable_editor")
        self.VariableEditor.set_scene(self.MainWindow.currentFlowgraphScene)
        self.VariableEditor.all_editor_actions.connect(self.MainWindow.handle_editor_action)
        self.MainWindow.ExampleBrowser.set_library(self.BlockLibrary)

        # Debug times
        log.debug(
            "Loaded MainWindow controller - {:.4f}s".format(
                stopwatch.elapsed("mainwindow")
            )
        )
        log.debug(
            "Loaded Console component - {:.4f}s".format(stopwatch.elapsed("console"))
        )
        log.debug(
            "Loaded BlockLibrary component - {:.4}s".format(
                stopwatch.elapsed("blocklibrary")
            )
        )
        # log.debug("Loaded DocumentationTab component - {:.4}s".format(stopwatch.elapsed("documentationtab")))

        # Print Startup information once everything has loaded
        self.Console.enable()

        paths = "\n\t".join(config.block_paths)
        welcome = (
            f"<<< Welcome to {config.name} {config.version} >>>\n\n"
            f"{('PyQt ' + PYQT_VERSION) if PYQT_VERSION else ('PySide ' + PYSIDE_VERSION)}\n"
            f"GUI preferences file: {self.qsettings.fileName()}\n"
            f"Block paths:\n\t{paths}\n"
            f"Using {QtGui.QIcon.themeName()} icon theme\n"
        )
        log.info(textwrap.dedent(welcome))

        log.debug(f'devicePixelRatio {self.MainWindow.screen().devicePixelRatio()}')

        if (self.qsettings.value("appearance/theme", "dark") == "dark") and (self.theme == "light"):
            log.warning("Could not apply dark theme. Is QDarkStyle installed?")

    # Global registration functions
    #  - Handles the majority of child controller interaciton

    def registerSignal(self, signal):
        pass

    def registerDockWidget(self, widget, location=0):
        """Allows child controllers to register a widget that can be docked in the main window"""
        # TODO: Setup the system to automatically add new "Show <View Name>" menu items when a new
        # dock widget is added.
        log.debug(
            "Registering widget ({0}, {1})".format(widget.__class__.__name__, location)
        )
        self.MainWindow.registerDockWidget(location, widget)

    def registerMenu(self, menu):
        """Allows child controllers to register an a menu rather than just a single action"""
        # TODO: Need to have several capabilities:
        #  - Menu's need the ability to have a priority for ordering
        #  - When registering, the controller needs to specific target menu
        #  - Automatically add seporators (unless it is the first or last items)
        #  - Have the ability to add it as a sub menu
        #  - MainWindow does not need to call register in the app controller. It can call directly
        #  - Possibly view sidebars and toolbars as submenu
        #  - Have the ability to create an entirely new menu
        log.debug("Registering menu ({0})".format(menu.title()))
        self.MainWindow.registerMenu(menu)

    def registerAction(self, action, menu):
        """Allows child controllers to register a global action shown in the main window"""
        pass

    def run(self):
        """Launches the main QT event loop"""
        # Show the main window after everything is initialized.
        self.MainWindow.show()
        return self.exec_()
