# -*- coding: iso-8859-1 -*-
# Copyright (C) 2012-2014 Bastian Kleineidam
"""
Functions to load plugin modules.

Example usage:
    modules = loader.get_modules('plugins')
    plugins = loader.get_plugins(modules, PluginClass)
"""
import os
import sys
import zipfile
import importlib
from .output import out


def is_frozen ():
    """Return True if running inside a py2exe- or py2app-generated
    executable."""
    return hasattr(sys, "frozen")


def get_modules(folder):
    """Find all valid modules in the given folder which must be in
    in the same directory as this loader.py module. A valid module
    has a .py extension, and is importable.
    @return: all loaded valid modules
    @rtype: iterator of module
    """
    if is_frozen():
        # find modules in library.zip filename
        zipname = os.path.dirname(os.path.dirname(__file__))
        parentmodule = os.path.basename(os.path.dirname(__file__))
        with zipfile.ZipFile(zipname, 'r') as f:
            prefix = "%s/%s/" % (parentmodule, folder)
            modnames = [os.path.splitext(n[len(prefix):])[0]
              for n in f.namelist()
              if n.startswith(prefix) and "__init__" not in n]
    else:
        dirname = os.path.join(os.path.dirname(__file__), folder)
        modnames = get_importable_modules(dirname)
    for modname in modnames:
        try:
            name ="..%s.%s" % (folder, modname)
            yield importlib.import_module(name, __name__)
        except ImportError as msg:
            out.error("could not load module %s: %s" % (modname, msg))


def get_importable_modules(folder):
    """Find all module files in the given folder that end with '.py' and
    don't start with an underscore.
    @return module names
    @rtype: iterator of string
    """
    for fname in os.listdir(folder):
        if fname.endswith('.py') and not fname.startswith('_'):
            yield fname[:-3]


def get_plugins(modules, classobj):
    """Find all class objects in all modules.
    @param modules: the modules to search
    @ptype modules: iterator of modules
    @return: found classes
    @rytpe: iterator of class objects
    """
    for module in modules:
        for plugin in get_module_plugins(module, classobj):
            yield plugin


def get_module_plugins(module, classobj):
    """Return all subclasses of a class in the module.
    If the module defines __all__, only those entries will be searched,
    otherwise all objects not starting with '_' will be searched.
    """
    try:
        names = module.__all__
    except AttributeError:
        names = [x for x in vars(module) if not x.startswith('_')]
    for name in names:
        try:
            obj = getattr(module, name)
        except AttributeError:
            continue
        try:
            if issubclass(obj, classobj):
                yield obj
        except TypeError:
            continue
