import nilmdb
from nilmdb.utils.printf import *

import nose
from nose.tools import *
from nose.tools import assert_raises
import threading
import time
import inspect

from testutil.helpers import *

@nilmdb.utils.lru_cache(size = 3)
def foo1(n):
    return n

@nilmdb.utils.lru_cache(size = 5)
def foo2(n):
    return n

def foo3d(n):
    foo3d.destructed.append(n)
foo3d.destructed = []
@nilmdb.utils.lru_cache(size = 3, onremove = foo3d)
def foo3(n):
    return n

class Foo:
    def __init__(self):
        self.calls = 0
    @nilmdb.utils.lru_cache(size = 3, keys = slice(1, 2))
    def foo(self, n, **kwargs):
        self.calls += 1

class TestLRUCache(object):
    def test(self):

        [ foo1(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo1.cache_info(), (6, 3))
        [ foo1(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo1.cache_info(), (15, 3))
        [ foo1(n) for n in [ 4, 2, 1, 1, 4 ] ]
        eq_(foo1.cache_info(), (18, 5))

        [ foo2(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo2.cache_info(), (6, 3))
        [ foo2(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo2.cache_info(), (15, 3))
        [ foo2(n) for n in [ 4, 2, 1, 1, 4 ] ]
        eq_(foo2.cache_info(), (19, 4))

        [ foo3(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo3.cache_info(), (6, 3))
        [ foo3(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
        eq_(foo3.cache_info(), (15, 3))
        [ foo3(n) for n in [ 4, 2, 1, 1, 4 ] ]
        eq_(foo3.cache_info(), (18, 5))
        eq_(foo3d.destructed, [1, 3])
        with assert_raises(KeyError):
            foo3.cache_remove(1,2,3)
        foo3.cache_remove(1)
        eq_(foo3d.destructed, [1, 3, 1])
        foo3.cache_remove_all()
        eq_(foo3d.destructed, [1, 3, 1, 2, 4 ])

        foo = Foo()
        foo.foo(5)
        foo.foo(6)
        foo.foo(7)
        foo.foo(5)
        eq_(foo.calls, 3)

        # Can't handle keyword arguments right now
        with assert_raises(NotImplementedError):
            foo.foo(3, asdf = 7)

        # Verify that argspecs were maintained
        eq_(inspect.getargspec(foo1),
            inspect.ArgSpec(args=['n'],
                            varargs=None, keywords=None, defaults=None))
        eq_(inspect.getargspec(foo.foo),
            inspect.ArgSpec(args=['self', 'n'],
                            varargs=None, keywords="kwargs", defaults=None))
