# License?

"""A U1DB implementation that uses OpenStack Swift as its persistence layer."""

from backends.leap import *
from backends.openstack import *

import gnupg

class GPGWrapper():
    """
    This is a temporary class for handling GPG requests, and should be
    replaced by a more general class used throughout the project.
    """

    GNUPG_HOME    = "~/.config/leap/gnupg"
    GNUPG_BINARY  = "/usr/bin/gpg" # this has to be changed based on OS

    def __init__(self, gpghome=GNUPG_HOME, gpgbinary=GNUPG_BINARY):
        self.gpg = gnupg.GPG(gnupghome=gpghome, gpgbinary=gpgbinary)

    def find_key(self, email):
        """
        Find user's key based on their email.
        """
        for key in self.gpg.list_keys():
            for uid in key['uids']:
                if re.search(email, uid):
                    return key
        raise LookupError("GnuPG public key for %s not found!" % email)

    def encrypt(self, data, recipient, sign=None, always_trust=False,
                passphrase=None, symmetric=False):
        return self.gpg.encrypt(data, recipient, sign=sign,
                                always_trust=always_trust,
                                passphrase=passphrase, symmetric=symmetric)

    def decrypt(self, data, always_trust=False, passphrase=None):
        return self.gpg.decrypt(data, always_trust=always_trust,
                                passphrase=passphrase)

    def import_keys(self, data):
        return self.gpg.import_keys(data)


#----------------------------------------------------------------------------
# u1db Transaction and Sync logs as JSON structures.
#----------------------------------------------------------------------------

class SimpleLog(object):
    def __init__(self):
        self._log = []

    def _set_log(self, log):
        self._log = log

    def _get_log(self):
        return self._log

    log = property(
        _get_log, _set_log, doc="Log contents.")

    def append(self, msg):
        self._log.append(msg)

    def reduce(self, func, initializer=None):
        return reduce(func, self.log, initializer)

    def map(self, func):
        return map(func, self.log)

    def filter(self, func):
        return filter(func, self.log)


class TransactionLog(SimpleLog):
    """
    An ordered list of (generation, doc_id, transaction_id) tuples.
    """

    def _set_log(self, log):
        self._log = log

    def _get_log(self):
        return sorted(self._log, reverse=True)

    log = property(
        _get_log, _set_log, doc="Log contents.")

    def get_generation(self):
        """
        Return the current generation.
        """
        gens = self.map(lambda x: x[0])
        if not gens:
            return 0
        return max(gens)

    def get_generation_info(self):
        """
        Return the current generation and transaction id.
        """
        if not self._log:
            return(0, '')
        info = self.map(lambda x: (x[0], x[2]))
        return reduce(lambda x, y: x if (x[0] > y[0]) else y, info)

    def get_trans_id_for_gen(self, gen):
        """
        Get the transaction id corresponding to a particular generation.
        """
        log = self.reduce(lambda x, y: y if y[0] == gen else x)
        if log is None:
            return None
        return log[2]

    def whats_changed(self, old_generation):
        """
        Return a list of documents that have changed since old_generation.
        """
        results = self.filter(lambda x: x[0] > old_generation)
        seen = set()
        changes = []
        newest_trans_id = ''
        for generation, doc_id, trans_id in results:
            if doc_id not in seen:
                changes.append((doc_id, generation, trans_id))
                seen.add(doc_id)
        if changes:
            cur_gen = changes[0][1]  # max generation
            newest_trans_id = changes[0][2]
            changes.reverse()
        else:
            results = self.log
            if not results:
                cur_gen = 0
                newest_trans_id = ''
            else:
                cur_gen, _, newest_trans_id = results[0]

        return cur_gen, newest_trans_id, changes
        


class SyncLog(SimpleLog):
    """
    A list of (replica_id, generation, transaction_id) tuples.
    """

    def find_by_replica_uid(self, replica_uid):
        if not self.log:
            return ()
        return self.reduce(lambda x, y: y if y[0] == replica_uid else x)

    def get_replica_gen_and_trans_id(self, other_replica_uid):
        """
        Return the last known generation and transaction id for the other db
        replica.
        """
        info = self.find_by_replica_uid(other_replica_uid)
        if not info:
            return (0, '')
        return (info[1], info[2])

    def set_replica_gen_and_trans_id(self, other_replica_uid,
                                      other_generation, other_transaction_id):
        """
        Set the last-known generation and transaction id for the other
        database replica.
        """
        self.log = self.filter(lambda x: x[0] != other_replica_uid)
        self.append((other_replica_uid, other_generation,
                     other_transaction_id))