from __future__ import absolute_import

import os
import shutil
import locket
import string
from toolz import memoize
from contextlib import contextmanager
from .utils import nested_get, flatten



# http://stackoverflow.com/questions/295135/turn-a-string-into-a-valid-filename-in-python
valid_chars = "-_.() " + string.ascii_letters + string.digits + os.path.sep


def escape_filename(fn):
    """ Escape text so that it is a valid filename

    >>> escape_filename('Foo!bar?')
    'Foobar'

    """
    return ''.join(filter(valid_chars.__contains__, fn))


def filename(path, key):
    return os.path.join(path, escape_filename(token(key)))


def token(key):
    """

    >>> token('hello')
    'hello'
    >>> token(('hello', 'world'))  # doctest: +SKIP
    'hello/world'
    """
    if isinstance(key, str):
        return key
    elif isinstance(key, tuple):
        return os.path.join(*map(token, key))
    else:
        return str(key)


class Interface(object):
    def __init__(self):
        self._iset_seen = set()

    def __setstate__(self, state):
        self.__dict__.update(state)
        self._iset_seen = set()

    def iset(self, key, value, **kwargs):
        if key in self._iset_seen:
            return
        else:
            self._iset(key, value, **kwargs)
            self._iset_seen.add(key)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.drop()

    def iget(self, key):
        return self._get([key], lock=False)[0]

    def get(self, keys, **kwargs):
        if not isinstance(keys, list):
            return self.get([keys], **kwargs)[0]
        elif any(isinstance(key, list) for key in keys):  # nested case
            flatkeys = list(flatten(keys))
            result = self.get(flatkeys, **kwargs)
            return nested_get(keys, dict(zip(flatkeys, result)))
        else:
            return self._get(keys, **kwargs)

    def delete(self, keys, **kwargs):
        if not isinstance(keys, list):
            return self._delete([keys], **kwargs)
        else:
            return self._delete(keys, **kwargs)

    def pop(self, keys, **kwargs):
        with self.partd.lock:
            result = self.partd.get(keys, lock=False)
            self.partd.delete(keys, lock=False)
        return result

