from functools import wraps

from .entries import DueCreditEntry

import logging
lgr = logging.getLogger('duecredit.collector')

class DueCreditCollector(object):
    """Collect the references

    The mighty beast which will might become later a proxy on the way to
    talk to a real collector
    """
    def __init__(self, entries=None, citations=None):
        self._entries = entries or {}
        self.citations = citations or {}

    def add(self, entry):
        """entry should be a DueCreditEntry object"""
        if isinstance(entry, list):
            for e in entry:
                self.add(e)
        else:
            key = entry.get_key()
            self._entries[key] = entry

    def load(self, src):
        """Loads references from a file or other recognizable source

        ATM supported only
        - .bib files
        """
        # raise NotImplementedError
        if isinstance(src, str):
            if src.endswith('.bib'):
                self._load_bib(src)
            else:
                raise NotImplementedError('Format not yet supported')
        else:
            raise ValueError('Must be a string')

    def _load_bib(self, src):
        lgr.debug("Loading %s" % src)

    # TODO: figure out what would be the optimal use for the __call__
    def __call__(self, *args, **kwargs):
        # TODO: how to determine and cite originating module???
        #       - we could use inspect but many people complain
        #         that it might not work with other Python
        #         implementations
        pass # raise NotImplementedError

    def cite(self, entry, use=None, level=None):
        """Decorator for references

        Parameters
        ----------
        entry: str or DueCreditEntry
          The entry to use, either identified by its id or a new one (to be added)
        """
        if isinstance(entry, DueCreditEntry):
            # new one -- add it
            self.add(entry)
            entry_ = entry
        else:
            entry_ = self._entries[entry]
        entry_key = entry_.get_key()

        if entry_key not in self.citations:
            self.citations[entry_key] = Citation(entry_, use, level)
        citation = self.citations[entry_key]
        citation.count += 1
        # TODO: update level and use here?

        return citation

    def dcite(self, *args, **kwargs):
        """Decorator for references.  Wrap a function or

        Parameters
        ----------
        args, kwargs
          Arguments to be passed to cite.  If no "level" provided, we deduce it
          from the wrapped function/method

        Examples
        --------

        @due.dcite('XXX00', use="Provides an answer for meaningless existence")
        def purpose_of_life():
            return None

        """
        def func_wrapper(func):
            if 'level' not in kwargs:
                # deduce level from the actual function which was decorated
                kwargs['level'] = 'func %s.%s' % (func.__module__, func.func_name)

            @wraps(func)
            def cite_wrapper(*fargs, **fkwargs):
                citation = self.cite(*args, **kwargs)
                return func(*fargs, **fkwargs)
            return cite_wrapper
        return func_wrapper

    def __repr__(self):
        args = []
        if self.citations:
            args.append("citations={0}".format(repr(self.citations)))
        if self._entries:
            args.append("entries={0}".format(repr(self._entries)))

        if args:
            args = ", ".join(args)
        else:
            args = ""
        return self.__class__.__name__ + '({0})'.format(args)

    def __del__(self):
        # TODO: spit out stuff
        lgr.info("EXPORTING")


class InactiveDueCreditCollector(object):
    """A short construct which should serve a stub in the modules were we insert it"""
    def _donothing(self, *args, **kwargs):  pass
    def dcite(self, *args, **kwargs):
        def nondecorating_decorator(func):
             return func
        return nondecorating_decorator
    cite = load = add = _donothing


class Citation(object):
    """Encapsulates citations and information on their use"""

    def __init__(self, entry, use, level):
        self._entry = entry
        self._use = use
        self._level = level
        self.count = 0

    def __repr__(self):
        return self.__class__.__name__

    @property
    def level(self):
        return self._level

@@ -3633,16 +3633,194 @@
 (args)%0A%0A
+    def __str__(self):%0A        return self.__class__.__name__ + %5C%0A            ' %7B0:d%7D entries, %7B1:d%7D citations'.format(%0A                len(self._entries), len(self.citations))%0A%0A
     def 

Add str for DueCreditCollector