import asyncio

from aioredis.connection import create_connection
from aioredis.pool import create_pool
from aioredis.util import _NOTSET
from .generic import GenericCommandsMixin
from .string import StringCommandsMixin
from .hash import HashCommandsMixin
from .hyperloglog import HyperLogLogCommandsMixin
from .set import SetCommandsMixin
from .sorted_set import SortedSetCommandsMixin
from .transaction import TransactionsCommandsMixin, Pipeline, MultiExec
from .list import ListCommandsMixin
from .scripting import ScriptingCommandsMixin
from .server import ServerCommandsMixin
from .pubsub import PubSubCommandsMixin
from .cluster import ClusterCommandsMixin
from .geo import GeoCommandsMixin, GeoPoint, GeoMember

__all__ = [
    'create_redis', 'Redis',
    'Pipeline', 'MultiExec',
    'GeoPoint', 'GeoMember',
]


# class AutoConnector(object):
#     closed = False
#
#     def __init__(self, *conn_args, **conn_kwargs):
#         self._conn_args = conn_args
#         self._conn_kwargs = conn_kwargs
#         self._conn = None
#         self._loop = conn_kwargs.get('loop')
#         self._lock = asyncio.Lock(loop=self._loop)
#
#     def __repr__(self):
#         return '<AutoConnector {!r}>'.format(self._conn)
#
#     @asyncio.coroutine
#     def execute(self, *args, **kwargs):
#         conn = yield from self.get_atomic_connection()
#         return (yield from conn.execute(*args, **kwargs))
#
#     @asyncio.coroutine
#     def get_atomic_connection(self):
#         if self._conn is None or self._conn.closed:
#             with (yield from self._lock):
#                 if self._conn is None or self._conn.closed:
#                     conn = yield from create_connection(
#                         *self._conn_args, **self._conn_kwargs)
#                     self._conn = conn
#         return self._conn


class Redis(GenericCommandsMixin, StringCommandsMixin,
            HyperLogLogCommandsMixin, SetCommandsMixin,
            HashCommandsMixin, TransactionsCommandsMixin,
            SortedSetCommandsMixin, ListCommandsMixin,
            ScriptingCommandsMixin, ServerCommandsMixin,
            PubSubCommandsMixin, ClusterCommandsMixin,
            GeoCommandsMixin):
    """High-level Redis interface.

    Gathers in one place Redis commands implemented in mixins.

    For commands details see: http://redis.io/commands/#connection
    """
    def __init__(self, pool_or_conn):
        self._pool_or_conn = pool_or_conn

    def __repr__(self):
        return '<Redis {!r}>'.format(self._pool_or_conn)

    def execute(self, command, *args, **kwargs):
        return self._pool_or_conn.execute(command, *args, **kwargs)

    def close(self):
        self._pool_or_conn.close()

    @asyncio.coroutine
    def wait_closed(self):
        yield from self._pool_or_conn.wait_closed()

    @property
    def db(self):
        """Currently selected db index."""
        return self._pool_or_conn.db

    @property
    def encoding(self):
        """Current set codec or None."""
        return self._pool_or_conn.encoding

    @property
    def connection(self):
        """Either :class:`aioredis.RedisConnection`,
        or :class:`aioredis.ConnectionsPool` instance.
        """
        return self._pool_or_conn

    @property
    def in_transaction(self):
        """Set to True when MULTI command was issued."""
        # XXX: this must be bound to real connection
        return self._pool_or_conn.in_transaction

    @property
    def closed(self):
        """True if connection is closed."""
        return self._pool_or_conn.closed

    def auth(self, password):
        """Authenticate to server.

        This method wraps call to :meth:`aioredis.RedisConnection.auth()`
        """
        return self._pool_or_conn.auth(password)

    def echo(self, message, *, encoding=_NOTSET):
        """Echo the given string."""
        return self.execute('ECHO', message, encoding=encoding)

    def ping(self, *, encoding=_NOTSET):
        """Ping the server."""
        return self.execute('PING', encoding=encoding)

    def quit(self):
        """Close the connection."""
        # TODO: warn when using pool
        return self.execute('QUIT')

    def select(self, db):
        """Change the selected database for the current connection.

        This method wraps call to :meth:`aioredis.RedisConnection.select()`
        """
        return self._pool_or_conn.select(db)


@asyncio.coroutine
def create_redis(address, *, db=None, password=None, ssl=None,
                 encoding=None, commands_factory=Redis,
                 loop=None):
    """Creates high-level Redis interface.

    This function is a coroutine.
    """
    conn = yield from create_connection(address, db=db,
                                        password=password,
                                        ssl=ssl,
                                        encoding=encoding,
                                        loop=loop)
    return commands_factory(conn)


@asyncio.coroutine
def create_redis_pool(address, *, db=0, password=None, ssl=None,
                      encoding=None, commands_factory=Redis,
                      loop=None):
    """Creates high-level Redis interface.

    This function is a coroutine.
    """
    pool = yield from create_pool(address, db=db,
                                  password=password,
                                  ssl=ssl,
                                  encoding=encoding,
                                  loop=loop)
    return commands_factory(pool)


create_reconnecting_redis = create_redis_pool
# @asyncio.coroutine
# def create_reconnecting_redis(address, *, db=None, password=None, ssl=None,
#                               encoding=None, commands_factory=Redis,
#                               loop=None):
#     """Creates high-level Redis interface.
#
#     This function is a coroutine.
#     """
#     # Note: this is not coroutine, but we may make it such. We may start
#     # a first connection in it, or just resolve DNS. So let's keep it
#     # coroutine for forward compatibility
#     conn = AutoConnector(address,
#                          db=db, password=password, ssl=ssl,
#                          encoding=encoding, loop=loop)
#     return commands_factory(conn)


# make pyflakes happy
(Pipeline, MultiExec)

@@ -819,1016 +819,8 @@
 %5D%0A%0A%0A
-# class AutoConnector(object):%0A#     closed = False%0A#%0A#     def __init__(self, *conn_args, **conn_kwargs):%0A#         self._conn_args = conn_args%0A#         self._conn_kwargs = conn_kwargs%0A#         self._conn = None%0A#         self._loop = conn_kwargs.get('loop')%0A#         self._lock = asyncio.Lock(loop=self._loop)%0A#%0A#     def __repr__(self):%0A#         return '%3CAutoConnector %7B!r%7D%3E'.format(self._conn)%0A#%0A#     @asyncio.coroutine%0A#     def execute(self, *args, **kwargs):%0A#         conn = yield from self.get_atomic_connection()%0A#         return (yield from conn.execute(*args, **kwargs))%0A#%0A#     @asyncio.coroutine%0A#     def get_atomic_connection(self):%0A#         if self._conn is None or self._conn.closed:%0A#             with (yield from self._lock):%0A#                 if self._conn is None or self._conn.closed:%0A#                     conn = yield from create_connection(%0A#                         *self._conn_args, **self._conn_kwargs)%0A#                     self._conn = conn%0A#         return self._conn%0A%0A%0A
 clas

drop AutoConnector