# -*- coding: utf-8 -*-

# This file is part of yampdqmgr.
# Copyright © 2013 Peter Schnebel <pschnebel@gmx.de>
#
# yampdqmgr is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# yampdqmgr is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# yampdqmgr.  If not, see <http://www.gnu.org/licenses/>.

from sortedset import SortedSet
from datetime import datetime

class Cache:
  """ This is a simple class to provide for easy caching of entries.  You can
  define a minium amount of entries and a maximum amount of entries to keep in
  the cache.  Once the cache size is greater then maximum amount, the cache is
  shrinked down to minimum amount keeping only the entries that have been
  accessed recently.

  >>> c = Cache(1, 1)
  >>> c.set(1, 0)
  >>> c.get(1)
  0
  >>> c.cache_size()
  1
  >>> c.set(2, 1)
  >>> c.get(2)
  1
  >>> c.get(1) == None
  True
  >>> c.cache_size()
  1
  """

  def __init__(self, min_cache=100, max_cache=1000):
    """initialize the cache and set min_cache / max_cache.

    >>> c = Cache(123, 456)
    >>> c.min_cache(), c.max_cache()
    (123, 456)
    """
    self.__cache = dict()
    self.__cache_access = SortedSet()
    self.min_cache(min_cache)
    self.max_cache(max_cache)

  def cache_size(self):
    """return the current size of the cache.

    >>> c = Cache(123, 456)
    >>> c.cache_size()
    0
    >>> for i in range(5): c.set(i, i)
    >>> c.cache_size()
    5
    >>> c.clear()
    >>> c.cache_size()
    0
    """
    return len(self.__cache)

  def min_cache(self, min_cache=None):
    """set and return the minimum cache size.

    >>> c = Cache(123, 456)
    >>> c.min_cache()
    123
    >>> c.min_cache(321)
    321
    >>> c.min_cache()
    321
    """
    if not min_cache is None:
      self.__min_cache = min_cache
    return self.__min_cache

  def max_cache(self, max_cache=None):
    """set and return the maximum cache size.

    >>> c = Cache(123, 456)
    >>> c.max_cache()
    456
    >>> c.max_cache(654)
    654
    >>> c.max_cache()
    654
    """
    if not max_cache is None:
      self.__max_cache = max_cache
    return self.__max_cache

  def has_key(self, key):
    """is key cached?

    >>> c = Cache()
    >>> c.has_key(1)
    False
    >>> c.set(1, 1)
    >>> c.has_key(1)
    True
    """
    return self.__cache.has_key(key)

  def set(self, key, value):
    """store the key / value pair in the cache.

    >>> c = Cache(100, 100)
    >>> for i in range(0, 100):
    ...   c.set(i, i)
    >>> c.get(0)
    0
    >>> c.get(99)
    99
    """
    self.__cache[key] = value
    self.__cache_access.add(datetime.now(), key)
    if self.__cache_access.count() > self.__max_cache:
      # remove old entries from the cache
      for k in self.__cache_access.range(0,
          self.__cache_access.count() - self.__min_cache):
        del self.__cache[k]
      self.__cache_access.remrange(0, self.__cache_access.count() -
            self.__min_cache)

  def get(self, key):
    """return the value for key from the cache.  returns None if key is not
    cached.

    >>> c = Cache(100, 100)
    >>> c.set(0, "zero")
    >>> c.get(0)
    'zero'
    >>> c.get(99) == None
    True
    """
    if self.__cache.has_key(key):
      self.__cache_access.add(datetime.now(), key)
      return self.__cache[key]
    return None

  def clear(self):
    """remove all entries from the cache.

    >>> c = Cache(100, 100)
    >>> for i in range(0, 100):
    ...   c.set(i, i)
    >>> c.cache_size()
    100
    >>> c.clear()
    >>> c.cache_size()
    0
    """
    self.__cache.clear()
    self.__cache_access.remrange()

if __name__ == '__main__':
    import doctest
    doctest.testmod()

# vim: set ai ts=2 sw=2 sts=2 expandtab:
# eof
