import os
import sys
import zc.recipe.egg

class EZScript:
    """
    Buildout recipe creating bin script for arbitrary callable.
    """
    def __init__(self, buildout, name, options):
        # Collect script args.
        self.script_args = {}
        self.script_args['module'] = options['module']
        self.script_args['module_callable'] = options['callable']

        for key, value in options.items():
            if key not in ['recipe', 'eggs', 'module', 'callable']:
                self.script_args[key] = value

        # General buildout setup.
        self.name = name
        self.buildout = buildout
        self.options = options
        options['location'] = os.path.join(buildout['buildout']['parts-directory'], name)
        if 'extra-paths' in options:
            options['pythonpath'] = options['extra-paths']
        else:
            options.setdefault('extra-paths', options.get('pythonpath', ''))
        self.egg = zc.recipe.egg.Egg(buildout, options['recipe'], options)
    
    def get_extra_paths(self):
        extra_paths = [self.options['location'],
                       self.buildout['buildout']['directory']
                       ]

        # Add libraries found by a site .pth files to our extra-paths.
        if 'pth-files' in self.options:
            import site
            for pth_file in self.options['pth-files'].splitlines():
                pth_libs = site.addsitedir(pth_file, set())
                if not pth_libs:
                    self.log.warning(
                        "No site *.pth libraries found for pth_file=%s" % (
                         pth_file,))
                else:
                    self.log.info("Adding *.pth libraries=%s" % pth_libs)
                    self.options['extra-paths'] += '\n' + '\n'.join(pth_libs)

        pythonpath = [p.replace('/', os.path.sep) for p in
                      self.options['extra-paths'].splitlines() if p.strip()]

        extra_paths.extend(pythonpath)
        return extra_paths

    def create_script(self, ws):
        args = []
        for k,v in self.script_args.items():
            if v.__class__ == str:
                args.append('%s="%s"' % (k, v))
            else:
                args.append('%s=%s' % (k, v))

        return zc.buildout.easy_install.scripts(
            [(self.name, 'shaunsephton.recipe.ezscript', 'script')],
            ws, 
            sys.executable, 
            self.options['bin-directory'],
            arguments=', '.join(args),
            extra_paths = self.get_extra_paths(),
        )
    
    def install(self):
        requirements, ws = self.egg.working_set(['shaunsephton.recipe.ezscript'])
        script_paths = []

        # Create script.
        script_paths.extend(self.create_script(ws))

        return script_paths

    def update(self):
        return

def import_module(name, package=None):
    """Import a module.

    The 'package' argument is required when performing a relative import. It
    specifies the package to use as the anchor point from which to resolve the
    relative import to an absolute import.

    """
    if name.startswith('.'):
        if not package:
            raise TypeError("relative imports require the 'package' argument")
        level = 0
        for character in name:
            if character != '.':
                break
            level += 1
        name = _resolve_name(name[level:], package, level)
    __import__(name)
    return sys.modules[name]

def script(module, module_callable, *args, **kwargs):
    """
    Args:
    module: Module in which your callable is defined. Required.
    module_callable: Callable to call in ``module`` with additionally provided parameters. Required.
    **kwargs: Additional keyword arguments to pass to callable. Optional.
    """
    module = import_module(module)
    return getattr(module, module_callable)(**kwargs)
