import asyncio
import logging
import sys

from .exceptions import DeleteMessage

logger = logging.getLogger(__name__)


class LoaferDispatcher:

    def __init__(self, routes, max_jobs=None):
        self.routes = routes
        jobs = max_jobs or len(routes) * 10
        self._semaphore = asyncio.Semaphore(jobs)

    async def dispatch_message(self, message, route):
        logger.debug('dispatching message to route={}'.format(route))
        confirm_message = False
        if not message:
            logger.warning('message will be ignored:\n{!r}\n'.format(message))
            return confirm_message

        with await self._semaphore:
            try:
                confirm_message = await route.deliver(message)
            except DeleteMessage as exc:
                logger.info('explicit message deletion\n{}\n'.format(message))
                confirm_message = True
            except asyncio.CancelledError as exc:
                msg = '"{!r}" was cancelled, the message will not be acknowledged:\n{}\n'
                logger.warning(msg.format(route.handler, message))
            except Exception as exc:
                logger.exception('{!r}'.format(exc))
                exc_info = sys.exc_info()
                confirm_message = await route.error_handler(exc_info, message)

        return confirm_message

    async def _process_message(self, message, route):
        confirmation = await self.dispatch_message(message, route)
        if confirmation:
            provider = route.provider
            await provider.confirm_message(message)
        return confirmation

    async def _dispatch_tasks(self, loop):
        tasks = []
        for route in self.routes:
            provider = route.provider
            messages = await provider.fetch_messages()
            for message in messages:
                task = self._process_message(message, route)
                tasks.append(task)

        if not tasks:
            return []

        done, _ = await asyncio.wait(tasks, loop=loop)
        # If any unhandled error happened, this will bring up the exception
        return [t.result() for t in done]

    async def dispatch_providers(self, loop, forever=True):
        if not forever:
            return await self._dispatch_tasks(loop)

        while True:
            await self._dispatch_tasks(loop)

    def stop(self):
        for route in self.routes:
            route.stop()

@@ -1610,29 +1610,194 @@
 nc def _
-dispatch_task
+get_route_messages(self, route):%0A        messages = await route.provider.fetch_messages()%0A        return %7B'route': route, 'messages': messages%7D%0A%0A    async def _get_routes_message
 s(self, 
@@ -1822,16 +1822,17 @@
 ks = %5B%5D%0A
+%0A
         
@@ -1873,46 +1873,248 @@
     
-provider = route.provider%0A    
+task = self._get_route_messages(route)%0A            tasks.append(task)%0A%0A        done, _ = await asyncio.wait(tasks, loop=loop)%0A        return %5Bt.result() for t in done%5D%0A%0A    async def _dispatch_tasks(self, loop):%0A        tasks = %5B%5D%0A%0A
         
 mess
@@ -2101,32 +2101,39 @@
 s = %5B%5D%0A%0A        
+routes_
 messages = await
@@ -2137,33 +2137,180 @@
 ait 
-provider.fetch_messages()
+self._get_routes_messages(loop)%0A        for route_messages in routes_messages:%0A            route = route_messages%5B'route'%5D%0A            messages = route_messages%5B'messages'%5D
 %0A   

Improve performance by get route messages in parallel (#34)