"""
    instantiating, managing and rsyncing to hosts

"""

import py
import sys, os
from py.__.execnet.channel import RemoteError

NO_ENDMARKER_WANTED = object()

class GatewayManager:
    RemoteError = RemoteError

    def __init__(self, specs, defaultchdir="pyexecnetcache"):
        self.gateways = []
        self.specs = []
        for spec in specs:
            if not isinstance(spec, py.execnet.XSpec):
                spec = py.execnet.XSpec(spec)
            if not spec.chdir and not spec.popen:
                spec.chdir = defaultchdir
            self.specs.append(spec)

    def trace(self, msg):
        self.notify("trace", "gatewaymanage", msg)

    def notify(self, eventname, *args, **kwargs):
        py._com.pyplugins.notify(eventname, *args, **kwargs)

    def makegateways(self):
        assert not self.gateways
        for spec in self.specs:
            gw = py.execnet.makegateway(spec)
            self.gateways.append(gw)
            gw.id = "[%s]" % len(self.gateways)
            self.notify("gwmanage_newgateway", gw, gw._rinfo())

    def getgateways(self, remote=True, inplacelocal=True):
        if not self.gateways and self.specs:
            self.makegateways()
        l = []
        for gw in self.gateways:
            if gw.spec._samefilesystem():
                if inplacelocal:
                    l.append(gw)
            else:
                if remote:
                    l.append(gw)
        return py.execnet.MultiGateway(gateways=l)

    def multi_exec(self, source, inplacelocal=True):
        """ remote execute code on all gateways. 
            @param inplacelocal=False: don't send code to inplacelocal hosts. 
        """
        multigw = self.getgateways(inplacelocal=inplacelocal)
        return multigw.remote_exec(source)

    def multi_chdir(self, basename, inplacelocal=True):
        """ perform a remote chdir to the given path, may be relative. 
            @param inplacelocal=False: don't send code to inplacelocal hosts. 
        """ 
        self.multi_exec("import os ; os.chdir(%r)" % basename, 
                        inplacelocal=inplacelocal).waitclose()

    def rsync(self, source, notify=None, verbose=False, ignores=None):
        """ perform rsync to all remote hosts. 
        """ 
        rsync = HostRSync(source, verbose=verbose, ignores=ignores)
        seen = {}
        for gateway in self.gateways:
            spec = gateway.spec
            if not spec._samefilesystem():
                if spec in seen:
                    continue 
                def finished():
                    if notify:
                        notify("rsyncrootready", spec, source)
                rsync.add_target_host(gateway, finished=finished)
                seen[spec] = gateway
        if seen:
            self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values())
            rsync.send()
            self.notify("gwmanage_rsyncfinish", source=source, gateways=seen.values())
        else:
            self.trace("rsync: nothing to do.")

    def exit(self):
        while self.gateways:
            gw = self.gateways.pop()
            self.trace("exiting gateway %s" % gw)
            gw.exit()

class HostRSync(py.execnet.RSync):
    """ RSyncer that filters out common files 
    """
    def __init__(self, sourcedir, *args, **kwargs):
        self._synced = {}
        ignores= None
        if 'ignores' in kwargs:
            ignores = kwargs.pop('ignores')
        self._ignores = ignores or []
        super(HostRSync, self).__init__(sourcedir=sourcedir, **kwargs)

    def filter(self, path):
        path = py.path.local(path)
        if not path.ext in ('.pyc', '.pyo'):
            if not path.basename.endswith('~'): 
                if path.check(dotfile=0):
                    for x in self._ignores:
                        if path == x:
                            break
                    else:
                        return True

    def add_target_host(self, gateway, finished=None):
        remotepath = os.path.basename(self._sourcedir)
        super(HostRSync, self).add_target(gateway, remotepath, 
                                          finishedcallback=finished,
                                          delete=True,)

    def _report_send_file(self, gateway, modified_rel_path):
        if self._verbose:
            path = os.path.basename(self._sourcedir) + "/" + modified_rel_path
            remotepath = gateway.spec.chdir
            print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path)
