# -*- coding: utf-8 -*-
#
# This file is part of EventGhost.
# Copyright © 2005-2019 EventGhost Project <http://www.eventghost.org/>
#
# EventGhost 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.
#
# EventGhost 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 EventGhost. If not, see <http://www.gnu.org/licenses/>.

"""

import os
import sys
import warnings
from os.path import join

# Local imports
import builder

MODULES_TO_IGNORE = [
    "__phello__.foo",
    "antigravity",
    "unittest",
    "win32com.propsys.propsys",
    "wx.lib.graphics",
    "wx.lib.rpcMixin",
    "wx.lib.wxcairo",
    "wx.build.config",
]

HEADER = """\
# -*- coding: utf-8 -*-
#
# This file is part of EventGhost.
# Copyright © 2005-2019 EventGhost Project <http://www.eventghost.org/>
#
# EventGhost 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.
#
# EventGhost 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 EventGhost. If not, see <http://www.gnu.org/licenses/>.

#-----------------------------------------------------------------------------
# This file was automatically created by the BuildImports.py script.
# Don't try to edit this file yourself.
#-----------------------------------------------------------------------------
#pylint: disable-msg=W0611,W0622,W0402,E0611,F0401
"""

warnings.simplefilter('error', DeprecationWarning)

class BuildImports(builder.Task):
    description = "Build Imports.py"

    def Setup(self):
        self.outFileName = join(self.buildSetup.pyVersionDir, "Imports.py")
        if self.buildSetup.showGui:
            if os.path.exists(self.outFileName):
                self.activated = False
        else:
            self.activated = bool(self.buildSetup.args.build)

    def DoTask(self):
        """
        Starts the actual work.
        """
        buildSetup = self.buildSetup
        MODULES_TO_IGNORE.extend(buildSetup.excludeModules)

        globalModuleIndex, badModules = ReadGlobalModuleIndex(
            join(buildSetup.pyVersionDir, "Global Module Index.txt")
        )
        MODULES_TO_IGNORE.extend(badModules)

        pyDir = sys.real_prefix if hasattr(sys, "real_prefix") else sys.prefix
        stdLibModules = (
            FindModulesInPath(join(pyDir, "DLLs"), "", True) +
            FindModulesInPath(join(pyDir, "Lib"), "", True)
        )

        notFoundModules = []
        for module in globalModuleIndex:
            if module in stdLibModules:
                continue
            if module in sys.builtin_module_names:
                continue
            if ShouldBeIgnored(module):
                continue
            notFoundModules.append(module)
        if notFoundModules:
            print "    Modules found in global module index but not in scan:"
            for module in notFoundModules:
                print "       ", module

        #print "Modules found in scan but not in global module index:"
        #for module in stdLibModules:
        #    if module not in globalModuleIndex:
        #        print "   ", module

        outfile = open(self.outFileName, "wt")
        outfile.write(HEADER)
        for module in stdLibModules:
            outfile.write("import %s\n" % module)
        # add every .pyd of the current directory
        for package in buildSetup.includeModules:
            outfile.write("\n# modules found for package '%s'\n" % package)
            for module in GetPackageModules(package):
                outfile.write("import %s\n" % module)
        outfile.write("\n")
        outfile.close()


class DummyStdOut:  #IGNORE:W0232 class has no __init__ method
    """
    Just a dummy stdout implementation, that suppresses all output.
    """
    def write(self, dummyData):  #IGNORE:C0103
        """
        A do-nothing write.
        """
        pass


def FindModulesInPath(path, prefix="", includeDeprecated=False):
    """
    Find modules and packages for a given filesystem path.
    """
    if prefix:
        prefix += "."
    print "    Scanning:", path
    modules = []
    for root, dirs, files in os.walk(path):
        package = root[len(path) + 1:].replace("\\", ".")
        package = prefix + package
        for directory in dirs[:]:
            if (
                not os.path.exists(join(root, directory, "__init__.py")) or
                ShouldBeIgnored(package + "." + directory)
            ):
                dirs.remove(directory)
        if ShouldBeIgnored(package) or package.rfind(".test") > 0:
            continue
        if package != prefix:
            isOk, eType, eMesg = TestImport(package)
            if isOk:
                modules.append(package)
            package += "."
        for filename in files:
            name, extension = os.path.splitext(filename)
            if extension.lower() not in (".py", ".pyd"):
                continue
            moduleName = package + name
            if ShouldBeIgnored(moduleName) or moduleName.endswith(".__init__"):
                continue
            if moduleName == "MimeWrite":
                print "found"
            isOk, eType, eMesg = TestImport(moduleName, includeDeprecated)
            if not isOk:
                if not eType == "DeprecationWarning":
                    print "       ", moduleName, eType, eMesg
                continue
            modules.append(moduleName)
    return modules

def GetPackageModules(package):
    """
    Returns a list with all modules of the package.
    """
    moduleList = []
    tail = join("Lib", "site-packages", package) + ".pth"
    pthPaths = [join(sys.prefix, tail)]
    if hasattr(sys, "real_prefix"):
        pthPaths.append(join(sys.real_prefix, tail))
    for pthPath in pthPaths:
        if os.path.exists(pthPath):
            for path in ReadPth(pthPath):
                moduleList.extend(FindModulesInPath(path))
            break
    else:
        mod = __import__(package)
        moduleList.append(package)
        if hasattr(mod, "__path__"):
            paths = mod.__path__
        else:
            if mod.__file__.endswith(".pyd"):
                return moduleList
            paths = [os.path.dirname(mod.__file__)]
        for path in paths:
            moduleList.extend(FindModulesInPath(path, package))
    return moduleList

def GetPydFiles(path):
    """
    Returns a list of all .pyd modules in supplied path.
    """
    files = []
    for filepath in os.listdir(path):
        moduleName, extension = os.path.splitext(os.path.basename(filepath))
        if extension.lower() == ".pyd":
            files.append(moduleName)
    return files

def ReadGlobalModuleIndex(infile):
    """
    Read the global module index file (created by copy&paste from the Python
    documentation) and sort out all modules that are not available on Windows.
    """
    modules = []
    badModules = []
    inFile = open(infile, "r")
    for line in inFile.readlines():
        if line.startswith("#"):
            continue
        parts = line.strip().split(" ", 1)
        if len(parts) > 1:
            if parts[1].startswith("(") and parts[1].find("Windows") < 0:
                badModules.append(parts[0])
                continue
#            if parts[1].find("Deprecated:") >= 0:
#                print line
        modules.append(parts[0])
    inFile.close()
    return modules, badModules

def ReadPth(path):
    """
    Read a .PTH file and return the paths inside as a list
    """
    result = []
    pthFile = open(path, "rt")
    for line in pthFile:
        if line.strip().startswith("#"):
            continue
        result.append(join(os.path.dirname(path), line.strip()))
    return result

def ShouldBeIgnored(moduleName):
    """
    Return True if the supplied module should be ignored, because it is a
    module or submodule in MODULES_TO_IGNORE.
    """
    moduleParts = moduleName.split(".")
    modulePartsLength = len(moduleParts)
    for module in MODULES_TO_IGNORE:
        ignoreParts = module.split(".")
        ignorePartsLength = len(ignoreParts)
        if ignorePartsLength > modulePartsLength:
            continue
        if moduleParts[:ignorePartsLength] == ignoreParts:
            return True
    return False

def TestImport(moduleName, includeDeprecated=False):
    """
    Test if the given module can be imported without error.
    """
    #print "Testing", moduleName
    oldStdOut = sys.stdout
    oldStdErr = sys.stderr
    sys.stdout = DummyStdOut()
    try:
        __import__(moduleName)
        return (True, "", "")
    except DeprecationWarning, exc:
        return includeDeprecated, "DeprecationWarning", str(exc)
    except ImportError, exc:
        return False, "ImportError", str(exc)
    except SyntaxError, exc:
        return False, "SyntaxError", str(exc)
    except Exception, exc:
        return False, "Exception", str(exc)
    finally:
        sys.stdout = oldStdOut
        sys.stderr = oldStdErr
"""
Builds a file that would import all used modules.

This way we trick py2exe to include all standard library files and some more
packages and modules.