"""
/***************************************************************************
 Roll
                                 A QGIS plugin
 Design and analysis of 3D seismic survey geometry
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-10-09
        git sha              : $Format:%H$
        copyright            : (C) 2022 - present by Duijndam.Dev
        email                : bart.duijndam@ziggo.nl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os.path
import re
import shutil
import sys

from qgis.core import Qgis, QgsMessageLog
from qgis.PyQt.QtCore import QCoreApplication, QSettings, QTranslator
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction

from .app_settings import readStoredDebugpySetting
from .cursor_utils import clearBusyCursorState

try:
    haveDebugpy = True
    import debugpy
except ImportError:
    haveDebugpy = False

# Import statement to acces the main window
from .roll_main_window import RollMainWindow

currentDir = os.path.dirname(os.path.abspath(__file__))
resourceDir = os.path.join(currentDir, 'resources')

MESSAGE_CATEGORY = 'Messages'
_debugpyConfigured = False
_debugpyListenerStarted = False


def enableRemoteDebugging():

    global _debugpyConfigured, _debugpyListenerStarted

    # See: https://medium.com/@45364/debugging-qgis-plugin-using-vs-code-33319de9d638
    # See: https://duijndam.dev/debugging-qgis-python-plugins/
    # See: https://gist.github.com/AsgerPetersen/9ea79ae4139f4977c31dd6ede2297f90?permalink_comment_id=3482053

    # I don't know if you were able to solve this. But here is how I did it:

    # Find your QGIS plugins folder, it generally is somewhere like:
    # C:\Users\user\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins
    # Look for debug_vs and open it's init.py file.
    # Where debugpy is imported, add the line: self.debugpy.configure(python = 'python3')
    # The result should look something like this:
    #         import debugpy

    #         self.debugpy = debugpy
    #         self.debugpy.configure(python = 'python3')
    # Reload the plugin (or QGIS altogether), it should work now.

    debugpyEnabled = readStoredDebugpySetting()

    if not (haveDebugpy and debugpyEnabled):
        return False

    if _debugpyListenerStarted:
        return True

    # Configure Python interpreter
    pythonPath = shutil.which("python")
    if not pythonPath:
        pythonPath = re.sub(r'[^/\\]+(?:\.exe)?$', 'python', sys.executable)

    # Print current environment for debugging
    print(f"Current directory   : {os.getcwd()}")
    print(f"Module path         : {__file__}")
    print(f"sys.executable      : {sys.executable}")
    print(f"devised python path : {pythonPath}")
    print(f"Python  on PATH     : {shutil.which('python')}")
    print(f"Python3 on PATH     : {shutil.which('python3')}")

    if not _debugpyConfigured:
        try:
            # This can help if you're running different Python versions
            # Cross-platform method to replace executable with python

            QgsMessageLog.logMessage(f"Configure debugpy with python path: {pythonPath}", MESSAGE_CATEGORY, Qgis.Info)
            print(f"Configure debugpy with python path: {pythonPath}")

            debugpy.configure(python=pythonPath)
            _debugpyConfigured = True
        except (OSError, RuntimeError, ValueError) as exc:
            QgsMessageLog.logMessage(f"Could not configure debugpy: {exc}", MESSAGE_CATEGORY, Qgis.Info)
            print(f"Could not configure debugpy: {exc}")
            return False

    try:
        QgsMessageLog.logMessage("Try to listen", MESSAGE_CATEGORY, Qgis.Info)
        print("Try to listen")

        debugpy.listen(("localhost", 5678))
        _debugpyListenerStarted = True

        print("Debugpy server started, waiting for connection...")
        # debugpy.wait_for_client()
        # print("Debugger connected!")
    except (OSError, RuntimeError) as exc:
        excText = str(exc)
        if 'already' in excText.lower() and 'listen' in excText.lower():
            _debugpyListenerStarted = True
            QgsMessageLog.logMessage('Debugpy server already active in this QGIS session', MESSAGE_CATEGORY, Qgis.Info)
            print('Debugpy server already active in this QGIS session')
            return True

        QgsMessageLog.logMessage(f"Couldn't start debugpy server: {exc}", MESSAGE_CATEGORY, Qgis.Info)
        print(f"Couldn't start debugpy server: {exc}")
        return False

    return True


class Roll:
    """QGIS Plugin Implementation."""

    # required for a minimal QGIS plugin
    # See: https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/plugins/plugins.html
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """

        # enable debugging
        enableRemoteDebugging()

        # Save reference to the QGIS interface
        self.iface = iface

        # initialize plugin directory
        self.pluginDir = os.path.dirname(__file__)

        # initialize locale
        locale = QSettings().value('locale/userLocale', 'en_US')
        if not locale:
            locale = 'en_US'
        locale = str(locale)[0:2]
        localePath = os.path.join(self.pluginDir, 'i18n', f'Roll_{locale}.qm')

        if os.path.exists(localePath):
            self.translator = QTranslator()
            self.translator.load(localePath)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr('&Roll')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.firstStart = None

        # define mainWindow in __init__ , to keep Pylint happy
        self.mainWindow = None

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Roll', message)

    def addAction(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None,
    ):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    # required for a minimal QGIS plugin
    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        iconFile = os.path.join(resourceDir, 'icon.png')

        self.addAction(
            iconFile,
            text=self.tr('Roll 3D survey design'),
            callback=self.run,
            parent=self.iface.mainWindow(),
        )

        # will be set False in run()
        self.firstStart = True

    # required for a minimal QGIS plugin
    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        clearBusyCursorState(self.iface.mainWindow())
        for action in self.actions:
            self.iface.removePluginMenu(self.tr('&Roll'), action)
            self.iface.removeToolBarIcon(action)

    # See: https://gis.stackexchange.com/questions/354346/qgis-plugin-with-dockwidget-and-mainwindow

    # required for a minimal QGIS plugin
    def run(self):
        """Run method that performs all the real work"""

        enableRemoteDebugging()
        clearBusyCursorState(self.iface.mainWindow())

        # Added Bart.
        # See: https://pyqtgraph.readthedocs.io/en/latest/getting_started/how_to_use.html
        # os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1"
        # QApplication.setHighDpiScaleFactorRoundingPolicy(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
        # QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
        # QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.firstStart:
            self.firstStart = False
            self.mainWindow = RollMainWindow(iface=self.iface)                # Bart: pass iface to mainWindow, so that it can access QGIS interface

        if self.mainWindow is None or self.mainWindow.killMe is True:           # Bart: Restart the GUI from scratch, when previously closed
            self.mainWindow = RollMainWindow(iface=self.iface)                # Bart: pass iface to mainWindow, so that it can access QGIS interface

        # show the main window
        self.mainWindow.show()                                                  # bring window to top on OSX and windows
        if self.mainWindow.isMinimized():
            self.mainWindow.raise_()                                            # bring window from minimized state on OSX
            self.mainWindow.showNormal()                                        # bring window from minimized state on windows
        self.mainWindow.activateWindow()                                        # bring window to front on OSX and windows
        clearBusyCursorState(self.iface.mainWindow(), self.mainWindow)
        # self.mainWindow.setFocus()                                            # No need to set the focus
