# A crazy buffer cache implementation from:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/164274
import weakref

class DoubleNode:
    """node for a double linked list """
    previous = None
    next = None
    def __init__(self, value):
        self.value = value

    def extract(self):
        previous = self.previous
        next = self.next
        if previous:
            previous.next = next
        if next:
            next.previous = previous
        self.next = self.previous = None

    def insert_after(self, node):
        self.next = node.next
        self.previous = node
        node.next.previous = self
        node.next = self


class BufferCache:
    """Store data in a double linked list..."""
    count = 0
    def __init__(self, function, capacity=50):
        self.cache = weakref.WeakValueDictionary()
        self.next = self.previous = self
        self.function = function
        self.capacity = capacity

    def _append(self, value):
        """ append a node for a new value """
        node = DoubleNode(value)
        node.insert_after(self.previous)
        if self.count == self.capacity:
            self.next.extract()
        else:
            self.count += 1
        return node

    def _value(self, node):
        """return the value and put the node as the first one"""
        node.extract()
        node.insert_after(self.previous)
        return node.value

    def __call__(self, *args):
        try:
            node = self.cache[args]
        except KeyError:
            x = self.function(*args)
            self.cache[args] = self._append(x)
        else:
            x = self._value(node)
        return x

    def __repr__(self):
        i = self.previous
        s = ""
        while i!=self:
            s = s + repr(i.value) + " "
            i = i.previous
        return s
