# -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2013 SF Isle of Man Limited
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.
# Cache global variables for timeouts
"""
This module exports a set of decorators for caching functions.

It exports:
    * cache: for caching functions without parameters
    * memoize: for caching functions using its arguments as part of the key
    * delete_cached: to remove a cached value
    * delete_memoized: to remove a cached value from the memoize decorator

"""
import os
import hashlib
from functools import wraps
from pybossa.core import sentinel

try:
    import cPickle as pickle
except ImportError:  # pragma: no cover
    import pickle

try:
    import settings_local as settings
except ImportError:  # pragma: no cover
    import pybossa.default_settings as settings
    os.environ['PYBOSSA_REDIS_CACHE_DISABLED'] = '1'

ONE_DAY = 24 * 60 * 60
ONE_HOUR = 60 * 60
HALF_HOUR = 30 * 60
FIVE_MINUTES = 5 * 60


def get_key_to_hash(*args, **kwargs):
    """Return key to hash for *args and **kwargs."""
    key_to_hash = ""
    # First args
    for i in args:
        key_to_hash += ":%s" % i
    # Attach any kwargs
    for key in sorted(kwargs.iterkeys()):
        key_to_hash += ":%s" % kwargs[key]
    return key_to_hash


def get_hash_key(prefix, key_to_hash):
    """Return hash for a prefix and a key to hash."""
    key_to_hash = key_to_hash.encode('utf-8')
    key = prefix + ":" + hashlib.md5(key_to_hash).hexdigest()
    return key


def cache(key_prefix, timeout=300):
    """
    Decorator for caching functions.

    Returns the function value from cache, or the function if cache disabled

    """
    if timeout is None:
        timeout = 300
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            key = "%s::%s" % (settings.REDIS_KEYPREFIX, key_prefix)
            if os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED') is None:
                output = sentinel.slave.get(key)
                if output:
                    return pickle.loads(output)
                output = f(*args, **kwargs)
                sentinel.master.setex(key, timeout, pickle.dumps(output))
                return output
            output = f(*args, **kwargs)
            sentinel.master.setex(key, timeout, pickle.dumps(output))
            return output
        return wrapper
    return decorator


def memoize(timeout=300):
    """
    Decorator for caching functions using its arguments as part of the key.

    Returns the cached value, or the function if the cache is disabled

    """
    if timeout is None:
        timeout = 300
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            key = "%s:%s_args:" % (settings.REDIS_KEYPREFIX, f.__name__)
            key_to_hash = get_key_to_hash(*args, **kwargs)
            key = get_hash_key(key, key_to_hash)
            if os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED') is None:
                output = sentinel.slave.get(key)
                if output:
                    return pickle.loads(output)
                output = f(*args, **kwargs)
                sentinel.master.setex(key, timeout, pickle.dumps(output))
                return output
            output = f(*args, **kwargs)
            sentinel.master.setex(key, timeout, pickle.dumps(output))
            return output
        return wrapper
    return decorator


def delete_cached(key):
    """
    Delete a cached value from the cache.

    Returns True if success or no cache is enabled

    """
    if os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED') is None:
        key = "%s::%s" % (settings.REDIS_KEYPREFIX, key)
        return sentinel.master.delete(key)
    return True


def delete_memoized(function, *args, **kwargs):
    """
    Delete a memoized value from the cache.

    Returns True if success or no cache is enabled

    """
    if os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED') is None:
        key = "%s:%s_args:" % (settings.REDIS_KEYPREFIX, function.__name__)
        if args or kwargs:
            key_to_hash = get_key_to_hash(*args, **kwargs)
            key = get_hash_key(key, key_to_hash)
            return sentinel.master.delete(key)
        keys_to_delete = sentinel.slave.keys(pattern=key + '*')
        if not keys_to_delete:
            return False
        return sentinel.master.delete(*keys_to_delete)
    return True

@@ -4263,32 +4263,37 @@
 %0A        return 
+bool(
 sentinel.master.
@@ -4299,24 +4299,25 @@
 .delete(key)
+)
 %0A    return 
@@ -4769,32 +4769,37 @@
          return 
+bool(
 sentinel.master.
@@ -4805,24 +4805,25 @@
 .delete(key)
+)
 %0A        key
@@ -4946,16 +4946,21 @@
  return 
+bool(
 sentinel
@@ -4990,16 +4990,17 @@
 _delete)
+)
 %0A    ret

Return True and False instead of 1 and 0