import re
import ldap
from logging import warning, debug, critical
from lechat.ChangeSet import OP_CREATE, OP_MODIFY, OP_RENAME, OP_DELETE
import watchers
import lechat.Utils
from lechat.DataTree import DataTree
from lechat.LDAPMapping import *
from LDAPProxy import LDAPProxy

class _WatcherListItem(object):
    def __init__(self, watcher, regexp, rank):
        self.watcher = watcher
        self.regexp = re.compile(regexp)
        self.rank = rank

class TreeDataBase(object):
    """LDAP objects are accessed through unix-style path.
    e.g.:
    /host/foo/samba/share/private/browsable maps to
    node=browsable,node=private,node=share,node=samba,node=foo,node=host,ou=lechat,dc=...
    
    /host/fw/firewall/zones/net/interface maps to
    node=interface,node=net,node=zones,node=firewall,node=fw,node=host,ou=lechat,dc=...
    """
    def __init__(self, uri, base_dn):
        self.uri = uri
        self.base_dn = base_dn
        self.watchers = { OP_CREATE : [],
                          OP_MODIFY : [],
                          OP_RENAME : [],
                          OP_DELETE : [] }
        self.connexions = {}

        # Log everything happening on lechat LDAP tree
        self.registerWatcher(OP_CREATE, watchers.Log.onCreate, '.*', 0)
        self.registerWatcher(OP_MODIFY, watchers.Log.onModify, '.*', 0)
        self.registerWatcher(OP_RENAME, watchers.Log.onRename, '.*', 0)
        self.registerWatcher(OP_DELETE, watchers.Log.onDelete, '.*', 0)

        # Actually apply changes to lechat LDAP tree
        self.registerWatcher(OP_CREATE, watchers.LDAP.onCreate, '.*', 1)
        self.registerWatcher(OP_MODIFY, watchers.LDAP.onModify, '.*', 1)
        self.registerWatcher(OP_RENAME, watchers.LDAP.onRename, '.*', 99)
        self.registerWatcher(OP_DELETE, watchers.LDAP.onDelete, '.*', 99)

        # Users branch management
        self.registerWatcher(OP_CREATE, watchers.Users.onCreate, '/people/.+', 2)
        self.registerWatcher(OP_MODIFY, watchers.Users.onModify, '/people/.+', 2)
        self.registerWatcher(OP_RENAME, watchers.Users.onRename, '/people/.+', 2)
        self.registerWatcher(OP_DELETE, watchers.Users.onDelete, '/people/.+', 2)

        # Cyrus mailboxes
        self.registerWatcher(OP_CREATE, watchers.Cyrus.onCreate, '/people/.+', 3)
        self.registerWatcher(OP_MODIFY, watchers.Cyrus.onModify, '/people/.+', 3)
        self.registerWatcher(OP_RENAME, watchers.Cyrus.onRename, '/people/.+', 3)
        self.registerWatcher(OP_DELETE, watchers.Cyrus.onDelete, '/people/.+', 3)

    def _connect(self, cred):
        login = cred.login
        password = cred.password
        debug('login as %s' % login)
        if not self.connexions.has_key(login + '#' + password):
            self.connexions[login + '#' + password] = LDAPProxy(self.uri, login, password)
        return self.connexions[login + '#' + password]

    def getTree(self, cred, base_path, filter = '(objectClass=lechatNode)'):
        """
        Returns tree rooted at base_path as a DataTree.
        """
        dn = lechat.Utils.pathToDN(base_path, self.base_dn)
        c = self._connect(cred)
        data = c.search_s(dn, ldap.SCOPE_SUBTREE, filter)
        result = DataTree()

        for k, v in data:
            _path = lechat.Utils.dnToPath(k, self.base_dn)
            for _k, _v in v.items():
                v[_k] = Attribute(attribute_type = _k, value = _v)
            result[ _path ] = DataTree(v, _path)

        return result
    
    def commit(self, cred, changes):
        """
        Commit changes to backend
        """
        #debug('commit cred: %s\nchanges: %s' %(cred.login, changes))
        c = self._connect(cred)

        # call watchers is 4 passes. TODO: beark

        for op in [OP_DELETE, OP_CREATE, OP_MODIFY, OP_RENAME]:
            for dn in changes.changes.keys():
                for change in changes.changes[dn]:
                    if change.op == op:
                        for w in self.watchers[change.op]:
                            if change.path and w.regexp.match(change.path):
                                w.watcher(self, cred, change)
        
        return True

    def registerWatcher(self, op, watcher, regexp = '.*', rank = 100):
        self.watchers[op].append( _WatcherListItem(watcher, regexp, rank) )
        self.watchers[op].sort( lambda a,b: cmp(a.rank, b.rank) )

