# -*- coding: utf-8 -*-

"""
***************************************************************************
*                                                                         *
*   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
import sys

from PyQt5.QtCore import QFile, QUrl, QEventLoop
from PyQt5.QtGui import QIcon
from qgis.core import QgsProcessingProvider, QgsMessageLog, Qgis, QgsFileDownloader
from processing.core.ProcessingConfig import Setting, ProcessingConfig


from .CalculateCorridor import CalculateCorridor
from .CreateLinkset import CreateLinkset
from .CreateGraph import CreateGraph
from .CreateProject import CreateProject
from .CalculateLocalMetric import CalculateLocalMetric
from .CalculateGlobalMetric import CalculateGlobalMetric

# For rasterize with OsRaster
from .OsRaster.Rasterize import Rasterize


class GraphabProvider(QgsProcessingProvider):

    DEFAULT_VERSION = '2.8.0'  # The version must be with 3 digits to avoid conversion in float by QGIS UI...

    def __init__(self, plugin):
        super().__init__()
        self.plugin = plugin

    def load(self):
        """In this method we add settings needed to configure our
        provider.
        """
        ProcessingConfig.settingIcons[self.name()] = self.icon()

        ProcessingConfig.addSetting(Setting(self.name(), 'ACTIVATE_GRAPHAB',
                                            self.plugin.translate('py', 'Activate'), True))
        ProcessingConfig.addSetting(Setting(self.name(), 'GRAPHAB_VERSION',
                                            self.plugin.translate('py', 'Graphab version'), self.DEFAULT_VERSION))
        ProcessingConfig.addSetting(Setting(self.name(), 'MEMORY_GRAPHAB',
                                            self.plugin.translate('py', 'Max memory for Java in Gb'), 0))
        ProcessingConfig.addSetting(Setting(self.name(), 'PROC_GRAPHAB',
                                            self.plugin.translate('py', 'Processors/Cores used'), 0))
        ProcessingConfig.addSetting(Setting(self.name(), 'JAVA_GRAPHAB',
                                            self.plugin.translate('py', 'Java path executable'), self.getJavaCommand(False)))

        ProcessingConfig.readSettings()
        self.refreshAlgorithms()
        return True

    def unload(self):
        """Setting should be removed here, so they do not appear anymore
        when the plugin is unloaded.
        """
        ProcessingConfig.removeSetting('ACTIVATE_GRAPHAB')
        ProcessingConfig.removeSetting('GRAPHAB_VERSION')
        ProcessingConfig.removeSetting('MEMORY_GRAPHAB')
        ProcessingConfig.removeSetting('PROC_GRAPHAB')
        ProcessingConfig.removeSetting('JAVA_GRAPHAB')

    def isActive(self):
        """Return True if the provider is activated and ready to run algorithms"""
        return ProcessingConfig.getSetting('ACTIVATE_GRAPHAB')

    def getMaxMem(self):
        return int(ProcessingConfig.getSetting('MEMORY_GRAPHAB'))

    def getNbProc(self):
        return int(ProcessingConfig.getSetting('PROC_GRAPHAB'))

    def getGraphabVersion(self):
        version = ProcessingConfig.getSetting('GRAPHAB_VERSION')
        if version < self.DEFAULT_VERSION:
            ProcessingConfig.setSettingValue('GRAPHAB_VERSION', self.DEFAULT_VERSION)
            version = self.DEFAULT_VERSION
        return version

    def getJavaPath(self):
        return ProcessingConfig.getSetting('JAVA_GRAPHAB')

    def setJavaPath(self, path):
        ProcessingConfig.setSettingValue('JAVA_GRAPHAB', path)

    def setActive(self, active):
        ProcessingConfig.setSettingValue('ACTIVATE_GRAPHAB', active)

    def id(self):
        """This is the name that will appear on the toolbox group.

        It is also used to create the command line name of all the
        algorithms from this provider.
        """
        return 'graphab'

    def name(self):
        """This is the localised full name.
        """
        return 'Graphab'

    def icon(self):
        """We return the default icon.
        """
        return QIcon(':/plugins/graphab-for-qgis/icons/icon.png')

    def loadAlgorithms(self):
        """Here we fill the list of algorithms in self.algs.

        This method is called whenever the list of algorithms should
        be updated. If the list of algorithms can change (for instance,
        if it contains algorithms from user-defined scripts and a new
        script might have been added), you should create the list again
        here.

        In this case, since the list is always the same, we assign from
        the pre-made list. This assignment has to be done in this method
        even if the list does not change, since the self.algs list is
        cleared before calling this method.
        """
        for alg in [CreateProject(self.plugin), CreateLinkset(self.plugin), CreateGraph(self.plugin),
                    CalculateGlobalMetric(self.plugin), CalculateLocalMetric(self.plugin),
                    CalculateCorridor(self.plugin), Rasterize()]:
            self.addAlgorithm(alg)


    def getGraphabCommand(self):
        inputJar = os.path.normcase(r'' + os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                                       'graphab-' + self.getGraphabVersion() + '.jar'))
        jarFile = QFile(inputJar)
        if not jarFile.exists():
            self.downloadGraphab(inputJar)
            if not jarFile.exists():
                raise IOError("Impossible to download Graphab software. Check log messages")

        command = [self.getJavaCommand(), "-Djava.awt.headless=true"]
        if self.getMaxMem() > 0:
            command.append("-Xmx" + str(self.getMaxMem()) + "g")
        command += ["-jar", inputJar]
        if self.getNbProc() > 0:
            command += ["-proc", str(self.getNbProc())]
        return command

    def downloadGraphab(self, jarFile):
        url = QUrl(
            "http://thema.univ-fcomte.fr/productions/download.php?name=graphab&version=" +
            self.getGraphabVersion() + "&username=Graph4QGis&institution=Python")

        def downloadFailed(error):
            errorMessage = "Impossible to download Graphab " + self.getGraphabVersion() + \
                           "software, check your internet connexion.\nDetails : ".join(error)
            QgsMessageLog.logMessage(errorMessage, 'Processing', Qgis.Critical)

        loop = QEventLoop()
        downloader = QgsFileDownloader(url, jarFile, delayStart=True)

        # connecting signals to functions
        downloader.downloadExited.connect(loop.quit)
        downloader.downloadError.connect(downloadFailed)

        downloader.startDownload()
        loop.exec_()

    def getJavaCommand(self, useConfig = True):

        if useConfig and os.path.isfile(self.getJavaPath()):
            return self.getJavaPath()

        java = 'java'
        if sys.platform == 'win32':
            java = 'javaw.exe'

        java_exec = None
        java_path = os.getenv("JAVA_HOME")
        if java_path != None:
            java_exec = os.path.join(java_path, 'bin', java)
        else:
            if sys.platform == 'win32':
                java_exec = self.getJavaWin(java)
                if java_exec is None:
                    for java_dir in ["Java", "AdoptOpenJDK"]:
                        java_path = "C:\\Program Files\\" + java_dir
                        if os.path.isdir(java_path):
                            for jdir in os.listdir(java_path):
                                jexec = os.path.join(java_path, jdir, "bin", java)
                                if os.path.isfile(jexec):
                                    java_exec = jexec
        if java_exec is None:
            return java
        else:
            if useConfig:
                self.setJavaPath(java_exec)
            return java_exec

    def getJavaWin(self, program):
        import os
        def is_exe(fpath):
            return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

        for path in self.getJavaHomesWin():
            path = path.strip('"')
            exe_file = os.path.join(path, 'bin', program)
            if is_exe(exe_file):
                return exe_file

        for path in self.getenv_system("PATH").split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file
        return None

    # This function gets a system variable
    # it was necessary to use this instead of os.environ["PATH"] because QGIS overwrites the path variable
    # the win32 libraries appear not to be part of the standard python install, but they are included in the
    # python version that comes with QGIS
    def getenv_system(self, varname):
        v = ''
        try:
            import win32api
            import win32con
        except:
            return v

        try:
            rkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,
                                       'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment')
            try:
                v = str(win32api.RegQueryValueEx(rkey, varname)[0])
                v = win32api.ExpandEnvironmentStrings(v)
            except:
                pass
        finally:
            win32api.RegCloseKey(rkey)
        return v

    def getJavaHomesWin(self):

        javahomes = []
        try:
            import win32api
            import win32con
        except:
            return javahomes

        try:
            rkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\JavaSoft\\Java Runtime Environment')
            versions = win32api.RegEnumKeyEx(rkey)
            for version in versions:
                try:
                    vkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,
                                               'SOFTWARE\\JavaSoft\\Java Runtime Environment\\' + version[0])
                    javahomes.append(str(win32api.RegQueryValueEx(vkey, 'JavaHome')[0]))
                    win32api.RegCloseKey(vkey)
                except:
                    pass
            win32api.RegCloseKey(rkey)
        except:
            pass

        return javahomes