"""Simple messaging interface."""
from __future__ import absolute_import, unicode_literals

import socket

from collections import deque

from . import entity
from . import messaging
from .connection import maybe_channel
from .five import Empty, monotonic

__all__ = ['SimpleQueue', 'SimpleBuffer']


class SimpleBase(object):
    Empty = Empty
    _consuming = False

    def __enter__(self):
        return self

    def __exit__(self, *exc_info):
        self.close()

    def __init__(self, channel, producer, consumer, no_ack=False):
        self.channel = maybe_channel(channel)
        self.producer = producer
        self.consumer = consumer
        self.no_ack = no_ack
        self.queue = self.consumer.queues[0]
        self.buffer = deque()
        self.consumer.register_callback(self._receive)

    def get(self, block=True, timeout=None):
        if not block:
            return self.get_nowait()
        self._consume()
        elapsed = 0.0
        remaining = timeout
        while True:
            time_start = monotonic()
            if self.buffer:
                return self.buffer.popleft()
            try:
                self.channel.connection.client.drain_events(
                    timeout=timeout and remaining)
            except socket.timeout:
                raise self.Empty()
            elapsed += monotonic() - time_start
            remaining = timeout and timeout - elapsed or None

    def get_nowait(self):
        m = self.queue.get(no_ack=self.no_ack)
        if not m:
            raise self.Empty()
        return m

    def put(self, message, serializer=None, headers=None, compression=None,
            routing_key=None, **kwargs):
        self.producer.publish(message,
                              serializer=serializer,
                              routing_key=routing_key,
                              headers=headers,
                              compression=compression,
                              **kwargs)

    def clear(self):
        return self.consumer.purge()

    def qsize(self):
        _, size, _ = self.queue.queue_declare(passive=True)
        return size

    def close(self):
        self.consumer.cancel()

    def _receive(self, message_data, message):
        self.buffer.append(message)

    def _consume(self):
        if not self._consuming:
            self.consumer.consume(no_ack=self.no_ack)
            self._consuming = True

    def __len__(self):
        """`len(self) -> self.qsize()`."""
        return self.qsize()

    def __bool__(self):
        return True
    __nonzero__ = __bool__


class SimpleQueue(SimpleBase):
    """Simple API for persistent queues."""

    no_ack = False
    queue_opts = {}
    exchange_opts = {'type': 'direct'}

    def __init__(self, channel, name, no_ack=None, queue_opts=None,
                 exchange_opts=None, serializer=None,
                 compression=None, **kwargs):
        queue = name
        queue_opts = dict(self.queue_opts, **queue_opts or {})
        exchange_opts = dict(self.exchange_opts, **exchange_opts or {})
        if no_ack is None:
            no_ack = self.no_ack
        if not isinstance(queue, entity.Queue):
            exchange = entity.Exchange(name, **exchange_opts)
            queue = entity.Queue(name, exchange, name, **queue_opts)
            routing_key = name
        else:
            name = queue.name
            exchange = queue.exchange
            routing_key = queue.routing_key
        consumer = messaging.Consumer(channel, queue)
        producer = messaging.Producer(channel, exchange,
                                      serializer=serializer,
                                      routing_key=routing_key,
                                      compression=compression)
        super(SimpleQueue, self).__init__(channel, producer,
                                          consumer, no_ack, **kwargs)


class SimpleBuffer(SimpleQueue):
    """Simple API for ephemeral queues."""

    no_ack = True
    queue_opts = dict(durable=False,
                      auto_delete=True)
    exchange_opts = dict(durable=False,
                         delivery_mode='transient',
                         auto_delete=True)

@@ -904,24 +904,25 @@
 et_nowait()%0A
+%0A
         self
@@ -933,24 +933,25 @@
 nsume()%0A
+%0A
         
 elapsed 
@@ -946,21 +946,32 @@
     
-elapsed = 0.0
+time_start = monotonic()
 %0A   
@@ -1019,45 +1019,8 @@
 ue:%0A
-            time_start = monotonic()%0A
     
@@ -1088,16 +1088,17 @@
 pleft()%0A
+%0A
         
@@ -1105,114 +1105,855 @@
     
-try:%0A                self.channel.connection.client.drain_events(%0A                    timeout=timeout and 
+if remaining is not None and remaining %3C= 0.0:%0A                raise self.Empty()%0A%0A            try:%0A                # The %60drain_events%60 method will%0A                # block on the socket connection to rabbitmq. if any%0A                # application-level messages are received, it will put them%0A                # into %60self.buffer%60.%0A                # * The method will block for UP TO %60timeout%60 milliseconds.%0A                # * The method may raise a socket.timeout exception; or...%0A                # * The method may return without having put anything on%0A                #    %60self.buffer%60.  This is because internal heartbeat%0A                #    messages are sent over the same socket; also POSIX makes%0A                #    no guarantees against socket calls returning early.%0A                self.channel.connection.client.drain_events(timeout=
 rema
@@ -2021,32 +2021,75 @@
 se self.Empty()%0A
+%0A            if remaining is not None:%0A    
             elap
@@ -2096,9 +2096,8 @@
 sed 
-+
 = mo
@@ -2123,32 +2123,36 @@
 art%0A            
+    
 remaining = time
@@ -2150,20 +2150,8 @@
 ng =
- timeout and
  tim
@@ -2168,16 +2168,8 @@
 psed
- or None
 %0A%0A  

Address shortcomings with timeout parameter described in issue #720 (#729)