# -*- coding: utf-8 -*-
"""Full-featured O(1) LRU cache backported from Python3.3. The full
Py3.3 API is supported (thread safety, maxsize, keyword args, type
checking, __wrapped__, and cache_info). Includes Py3.3 optimizations
for better memory utilization, fewer dependencies, and fewer dict
lookups.

http://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/

Added persistence capability

"""

from __future__ import absolute_import, division, print_function, unicode_literals

from collections import namedtuple
from functools import update_wrapper
from threading import RLock

from ..exceptions import HGVSDataNotAvailableError, HGVSVerifyFailedError

_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])


class _HashedSeq(list):
    __slots__ = ['hashvalue']

    def __init__(self, tup, hash=hash):
        self[:] = tup
        self.hashvalue = hash(tup)

    def __hash__(self):
        return self.hashvalue

    def __getstate__(self):
        return True    # needs to be truthy for __setstate__ to be called

    def __setstate__(self, _):
        self.hashvalue = hash(tuple(self))

    def __repr__(self):
        return '_HashedSeq({tuple!r})'.format(self=self, tuple=tuple(self))


def _make_key(func,
              args,
              kwds,
              typed,
              kwd_mark=(object(), ),
              fasttypes={int, str, frozenset, type(None)},
              sorted=sorted,
              tuple=tuple,
              type=type,
              len=len):
    'Make a cache key from optionally typed positional and keyword arguments'
    key = args
    key += kwd_mark
    key += ('__func__', func)
    if kwds:
        sorted_items = sorted(kwds.items())
        for item in sorted_items:
            key += item
    if typed:
        key += tuple(type(v) for v in args)
        if kwds:
            key += tuple(type(v) for k, v in sorted_items)
    elif len(key) == 1 and type(key[0]) in fasttypes:
        return key[0]
    return _HashedSeq(key)


LEARN = 1
RUN = 2
VERIFY = 3


def lru_cache(maxsize=100, typed=False, mode=None, cache=None):
    """Least-recently-used cache decorator.

    If *maxsize* is set to None, the LRU features are disabled and the cache
    can grow without bound.

    If *typed* is True, arguments of different types will be cached separately.
    For example, f(3.0) and f(3) will be treated as distinct calls with
    distinct results.

    Arguments to the cached function must be hashable.

    View the cache statistics named tuple (hits, misses, maxsize, currsize) with
    f.cache_info().  Clear the cache and statistics with f.cache_clear().
    Access the underlying function with f.__wrapped__.