import collections


class LRUCacheDict(collections.OrderedDict):
    """
    LRUCacheDict implements the "Least recently used" cache strategy.
    LRUCacheDict removes last used elements, if the time they were lastly used exceeds the specified `max_duration` or the cache dictionary exceeds `max_size`.
    For more information, please see: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)

    The following example illustrates how to use LRUCacheDict with MySQLLookup:

            self.MySQLLookup =  MySQLLookup(self,
                    connection=mysql_connection,
                    id="MySQLLookup",
                    cache=bspump.cache.LRUCacheDict(app, max_size=1000, max_duration=1000)
            )

    """

    def __init__(self, app, max_size=1000, max_duration=None, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.App = app

        self.MaxSize = max_size
        self.MaxDuration = max_duration
        self.refresh()

    def refresh(self):
        # Remove items that overflow the dictionary
        if self.MaxSize:
            for i in range(max(0, len(self) - self.MaxSize)):
                self.popitem(last=False)

        # Remove items by max time
        if self.MaxDuration:
            limit = self.App.time() - self.MaxDuration
            while self:
                value, lru = next(iter(super().values()))
                if lru > limit:
                    break
                self.popitem(last=False)

    def __getitem__(self, key):
        value = super().__getitem__(key)[0]
        super().__setitem__(key, (value, self.App.time()))
        self.move_to_end(key)
        return value

    def __setitem__(self, key, value):
        super().__setitem__(key, (value, self.App.time()))
        self.refresh()

    def items(self):
        return ((key, value) for key, (value, _) in super().items())

    def values(self):
        return (value for value, _ in super().values())
