import shutil

import maya.mel as mel
import maya.api.OpenMaya as om2
import os
import json
from . import const
from . import utils
import sys
from maya import cmds

MOD_TEXT = "+ zootoolspro 2.0 XXXXX/zootoolspro/install/core/extensions/maya\n" \
           "ZOOTOOLS_PRO_ROOT := ../../\n" \
           "scripts: ./Scripts"
ZOO_MOD = "zootoolspro.mod"

MODULES_DIR = "modules"
SCRIPTS_DIR = "scripts"
PREFS_DIR = "~/zoo_preferences"
ZOO_FOLDER = "zootoolspro"

CLEANUP_PYC_CALLBACK = """
# %s
from maya import cmds
import os, time, threading
def cleanpycs():
    time.sleep(0.5)
    [os.remove(os.path.join(dir,f)) for (dir, subdirs, fs) in os.walk('{}') for f in fs if f.endswith(".pyc")]

t = threading.Thread(target=cleanpycs)
t.start()"""


# ----------------------------------------
# Main Logic
# ----------------------------------------

class InstallerCore(object):
    def __init__(self):
        """ Installer core code

        """
        self.prefsDirectory = self.getMayaPrefsPath()
        self.scriptsDirectory = os.path.join(self.prefsDirectory, SCRIPTS_DIR)
        self.moduleDirectory = os.path.join(self.prefsDirectory, MODULES_DIR)
        self.zooPrefsDirectory = os.path.expanduser(PREFS_DIR)
        self.currentDirectory = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

    def install(self, scriptsDirectory, moduleDirectory, prefDirectory):
        """Install or upgrades Zoo Tools Pro 2 from the "maya" directory next to this python file.

        :param scriptsDirectory: Usually the full path to /maya/scripts
        :type scriptsDirectory: str
        :param moduleDirectory: Usually the full path to /maya/modules
        :type moduleDirectory: str
        :param prefDirectory:
        """
        upgrade = "installed"
        installedVersion = self.installedVersionPretty()
        version = self.versionPretty()
        newer = self.isNewerVersion()

        zooNewMod = os.path.join(scriptsDirectory, "zootoolspro", "install", "core", "extensions", "maya")
        os.environ['MAYA_MODULE_PATH'] += ":{}".format(zooNewMod)


        if os.path.isdir(os.path.join(scriptsDirectory, ZOO_FOLDER)):
            if self.isNewerVersion() == "YES":
                upgrade = "upgraded"
            elif self.isNewerVersion() == "NO":
                upgrade = "downgraded"
            elif self.isNewerVersion() == "EQUAL":
                upgrade = "replaced"
        om2.MGlobal.displayInfo("Copy Location Path: {}".format(self.currentDirectory))

        # Copy files across to maya/scripts/zootoolpro
        self.installedScripts = self.copyZooFolder(scriptsDirectory)  # errors already reported
        if not self.installedScripts:
            return
        om2.MGlobal.displayInfo("Scripts copied: {}/zootoolspro".format(scriptsDirectory))

        # Create maya/modules/zootoolspro.mod
        if not self.createModFile(moduleDirectory, scriptsDirectory):  # errors already reported
            return
        om2.MGlobal.displayInfo("Module created: {}/zootoolspro.mod".format(moduleDirectory))

        if not self.setPreferenceDirectory(prefDirectory):
            return
        om2.MGlobal.displayInfo("Preference Directory set: {}".format(moduleDirectory))
        # Success message
        if installedVersion and newer != "EQUAL":
            message = "Zoo Tools Pro 2 has been {} successfully. " \
                      "\n\nFrom Version {} --> {}\n\n" \
                      "Please restart Maya for the changes to take effect.".format(upgrade, installedVersion,version)
            utils.successPopup(message)
        elif newer == "EQUAL":
            message = "Zoo Tools Pro 2 has been {} successfully.\n\n" \
                      "Please restart Maya for the changes to take effect.".format(upgrade, installedVersion,version)
            utils.successPopup(message)
        else:
            message = "Zoo Tools Pro 2 has been {} successfully. ".format(upgrade)
            self.loadZooPlugin(os.path.join(scriptsDirectory, "zootoolspro", "install", "core"))
            utils.successPopup(message)

        om2.MGlobal.displayInfo(message.replace("\n", ""))

        return True

    def loadZooPlugin(self, rootDirectory):
        """ Load the zoo plugin

        :param rootDirectory:
        :return:
        """
        os.environ["ZOOTOOLS_PRO_ROOT"] = rootDirectory
        rootDirectory = rootDirectory.replace("\\", "/")

        # Callback to clean up after installing
        callback = CLEANUP_PYC_CALLBACK.format(rootDirectory)

        from maya import cmds

        sys.path.append(os.path.join(os.environ["ZOOTOOLS_PRO_ROOT"], "python"))
        cmds.loadPlugin(os.path.join(os.environ["ZOOTOOLS_PRO_ROOT"],
                                     "extensions",
                                     "maya", "plug-ins", "zootools.py"), addCallback=callback)

        cmds.loadPlugin(os.path.join(os.environ["ZOOTOOLS_PRO_ROOT"],
                                     "extensions",
                                     "maya", "plug-ins", "zooundo.py"))


    def setPreferenceDirectory(self, prefDirectory):
        """ Set preferences directory

        :param prefDirectory:
        :return:
        """
        scriptsDir = self.installedScripts
        if not self.installedScripts:
            return False

        if prefDirectory == self.zooPrefsDirectory:  # No need for changes so return true
            return True

        prefRootsPath = os.path.join(scriptsDir, "zootoolspro",
                                     "config", "env",
                                     "preference_roots.config")

        with open(prefRootsPath, 'r') as f:
            prefRoot = json.load(f)

        prefRoot['user_preferences'] = prefDirectory
        with open(prefRootsPath, 'w') as f:
            f.write(json.dumps(prefRoot, indent=4))

        return True

    def userPreferencePath(self, zooPath):
        """ User preference path found from the zootools pro path

        :param zooPath:
        :return:
        """
        userPrefs = self.preferenceRoots(zooPath)['user_preferences']

        if userPrefs == PREFS_DIR:
            userPrefs = os.path.expanduser(userPrefs)
        return userPrefs

    def preferenceRoots(self, zooPath):
        prefRootsPath = os.path.join(zooPath,
                                     "config", "env",
                                     "preference_roots.config")

        with open(prefRootsPath, 'r') as f:
            prefRoot = json.load(f)

        return prefRoot

    @classmethod
    def getMayaPrefsPath(cls):
        """Returns the users maya prefs folder full path eg:

            C:/Users/userName/Documents/maya

        :return prefsDirectory: The full path to the users maya prefs location
        :rtype prefsDirectory:
        """
        versionPrefsFolder = mel.eval("internalVar -upd")  # Eg. path to maya/2020/prefs folder
        return os.path.abspath(os.path.join(versionPrefsFolder, "../.."))  # up two directories /maya only

    def isNewerVersion(self):
        """

        :return: True if newer, false if not newer. None if zoo isn't installed
        """

        version = self.version()
        installedVersion = self.installedVersion()
        if installedVersion is None:
            return "NOT_INSTALLED"

        if version > installedVersion:
            return "YES"
        elif version == installedVersion:
            return "EQUAL"
        else:
            return "NO"

    def version(self):
        """ Build version of this installer

        :return:
        """
        with open(os.path.join(self.currentDirectory, "maya", "scripts", "zootoolspro", "zoo_package.json")) as f:
            zooJson = json.load(f)

        return tuple(zooJson['version'].split("."))

    def versionPretty(self):
        return ".".join(self.version())

    def installedVersion(self):
        """ Installed build version

        :return:
        """
        if not self.isZooInstalled():
            return

        if self.zooLoaded():
            from zoo.core.api import currentConfig
            return tuple(currentConfig().buildVersion().split("."))
        else:
            zootoolsproPath = self.installedZooPath()
            with open(os.path.join(zootoolsproPath, "zoo_package.json")) as f:
                zooJson = json.load(f)
            return tuple(zooJson['version'].split("."))

    def installedVersionPretty(self):
        version = self.installedVersion()
        if version:
            return ".".join(self.installedVersion())

    def installedZooPath(self):
        """ Installed zoo path

        :return:
        """
        if not self.isZooInstalled():
            return

        if self.zooLoaded():
            from zoo.core.api import currentConfig
            return currentConfig().rootPath
        else:
            return utils.parentDir(cmds.pluginInfo("zootools.py", q=1, path=1), 6)

    def zooLoaded(self):
        try:
            return cmds.pluginInfo("zootools.py", q=1, l=1)
        except:
            return False

    def isZooInstalled(self):
        """ Checks if zoo is installed

        :return:
        """

        try:
            cmds.pluginInfo("zootools.py", q=1, p=1)
            return True
        except:
            return False

    def createModFile(self, moduleDirectory, scriptsDirectory):
        """Creates the zootoolspro.mod file creating the path correctly.

        :param moduleDirectory: The modules folder full path
        :type moduleDirectory: str

        :return moduleDirectory: the full path to the /maya/modules/ directory
        :rtype moduleDirectory: str
        """
        moduleText = MOD_TEXT.replace("XXXXX", scriptsDirectory)
        # Check if "maya/modules" folder already exists
        moduleZooFile = os.path.join(moduleDirectory, ZOO_MOD)
        if not os.path.isdir(moduleDirectory):
            if not utils.makeDirectory(moduleDirectory):  # error reported
                return False
        # Check if file "zootoolspro.mod" exists
        if os.path.isfile(moduleZooFile) and ZOO_MOD in moduleZooFile:
            om2.MGlobal.displayInfo("Upgrading: Rebuilding file as it exists {}".format(moduleZooFile))
            try:
                os.remove(moduleZooFile)
            except:
                message = "The file could not be deleted {} \n\n{}".format(moduleZooFile, const.DID_NOT_INSTALL)
                om2.MGlobal.displayInfo(message)
                utils.errorPopup(message)
                return False
        # Create "zootoolspro.mod" file in the modules directory
        utils.saveFileTxt(moduleText, moduleZooFile)
        # # Add the mod file to the pref_roots
        # zooPath = os.path.join(scriptsDirectory, "zootoolspro")
        # prefRoots = self.preferenceRoots(os.path.join(scriptsDirectory, "zootoolspro"))
        # prefRoots["maya_mod"] = moduleDirectory
        # with open(self.prefRootPath(zooPath), 'w') as f:
        #     json.dump(prefRoots, fp=f, indent=4)

        return True

    def installedPrefRootPath(self):
        installed = self.installedZooPath()
        if installed:
            return os.path.join(installed,
                                "config", "env",
                                "preference_roots.config")

    def prefRootPath(self, zooPath):
        return os.path.join(zooPath,
                            "config", "env",
                            "preference_roots.config")

    def installedPrefRoots(self):
        installed = self.installedZooPath()
        if installed:
            return self.preferenceRoots(installed)

    def installedModPath(self):
        """

        :return:
        """
        import glob
        for path in os.getenv("MAYA_MODULE_PATH").split(":"):
            if glob.glob(os.path.join(path, "zootoolspro.mod")):
                return path

    def _installedModPathLegacy(self):
        installed = self.installedZooPath()
        if not installed:
            return
        print("installed mod path", installed)

        return self.preferenceRoots(installed).get("maya_mod", "")

    def copyZooFolder(self, scriptsDirectory):
        """Copies the maya/scripts/zootoolpro folder from the unzipped source to the destination in the user prefs

        :return scriptsDirectory: The scripts directory maya/scripts if successful
        :rtype scriptsDirectory: str
        """
        zooTargetDir = os.path.join(scriptsDirectory, "zootoolspro")
        if not os.path.isdir(scriptsDirectory):
            if not utils.makeDirectory(scriptsDirectory):  # error reported
                return False
        # Get the zoo folder to copy
        zooSourceDir = os.path.join(self.currentDirectory, "maya/scripts/zootoolspro")
        # Check if exists already
        if not os.path.isdir(zooSourceDir):
            message = "Cannot find the source `maya` folder, be sure that the files have been unzipped " \
                      "with the `maya` folder in the same folder as the " \
                      "drag and drop python file.\n\n{}".format(const.DID_NOT_INSTALL)
            om2.MGlobal.displayError(message)
            utils.errorPopup(message)
            return False
        # Check upgrading
        if os.path.isdir(zooTargetDir) and "zootoolspro" in zooTargetDir:  # it exists already so delete for the upgrade
            om2.MGlobal.displayInfo("Upgrading: Deleting and remaking directory {} ".format(zooTargetDir))
            try:
                shutil.rmtree(zooTargetDir)
            except:
                message = "Could not delete folder {} \n\n{}".format(zooTargetDir, const.DID_NOT_INSTALL)
                import traceback
                traceback.print_exc()
                om2.MGlobal.displayError(message)
                utils.errorPopup(message)
                return False
        # Copy files to new location
        if not utils.copyDirectory(zooSourceDir, zooTargetDir, (".gitignore", "*.git", "__pycache__", "*.pyc", ".vscode", ".idea")):  # message already reported
            return False
        return scriptsDirectory


def deletePycFiles(path):
    files = pycFiles(path)
    for f in files:
        os.remove(f)


def pycFiles(path):
    return [os.path.join(dir, f) for (dir, subdirs, fs) in os.walk(path) for f in fs if f.endswith(".pyc")]
