# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
import datetime
from urlparse import urlparse
from decimal import Decimal

from redis import StrictRedis
from msgpack import packb, unpackb

from trytond.config import config
from trytond.cache import BaseCache
from trytond.transaction import Transaction

__version__ = '0.1'
__all__ = ['RedisCache']


def encode_hook(o):
    if isinstance(o, Decimal):
        return {
            '__decimal__': True,
            'data': str(o)
        }
    if isinstance(o, datetime.datetime):
        return {
            '__datetime__': True,
            'data': (o.year, o.month, o.day, o.hour, o.minute, o.second,
                o.microsecond)
        }
    if isinstance(o, datetime.date):
        return {
            '__date__': True,
            'data': (o.year, o.month, o.day)
        }
    if isinstance(o, datetime.time):
        return {
            '__time__': True,
            'data': (o.hour, o.minute, o.second, o.microsecond)
        }
    if isinstance(o, datetime.timedelta):
        return {
            '__timedelta__': True,
            'data': o.total_seconds()
        }
    if isinstance(o, set):
        return {
            '__set__': True,
            'data': tuple(o)
        }
    return o


def decode_hook(o):
    if '__decimal__' in o:
        return Decimal(o['data'])
    elif '__datetime__' in o:
        return datetime.datetime(*o['data'])
    elif '__date__' in o:
        return datetime.date(*o['data'])
    elif '__time__' in o:
        return datetime.time(*o['data'])
    elif '__timedelta__' in o:
        return datetime.timedelta(o['data'])
    elif '__set__' in o:
        return set(o['data'])
    return o


class RedisCache(BaseCache):
    "Redis Cache implementation for trytond"
    _client = None

    def __init__(self, name, size_limit=1024, context=True):
        super(RedisCache, self).__init__(name, size_limit, context)
        redis_url = config.get('cache', 'uri',
            default='redis://127.0.0.1:6379/0')
        url = urlparse(redis_url)
        if self._client is None:
            assert url.scheme == 'redis', ('invalid redis uri, it should use'
                'the redis schema')
            host = url.hostname
            port = url.port
            db = url.path.strip('/')
            self._client = StrictRedis(host=host, port=port, db=db)

    def _namespace(self, dbname=None):
        if dbname is None:
            dbname = Transaction().database.name
        return '%s:%s' % (dbname, self._name)

    def get(self, key, default=None):
        namespace = self._namespace()
        key = self._key(key)
        result = self._client.hget(namespace, key)
        if result is None:
            return default
        else:
            # Maybe we should allow to configure it or avoid the dependency
            return unpackb(result, encoding='utf-8', object_hook=decode_hook)

    def set(self, key, value):
        namespace = self._namespace()
        key = self._key(key)
        value = packb(value, use_bin_type=True, default=encode_hook)
        self._client.hset(namespace, key, value)

    def clear(self):
        namespace = self._namespace()
        self._client.delete(namespace)

    @classmethod
    def clean(cls, dbname):
        pass

    @classmethod
    def resets(cls, dbname):
        pass

    @classmethod
    def drop(cls, dbname):
        for inst in cls._cache_instance:
            cls._client.delete(inst._namespace(dbname))
