"""
Wrapper module for asynchronous task handling.
"""
import asyncio, inspect


class UpdateTask(asyncio.Task):
    """Wrap a coroutine in a future"""

    def __init__(self, coro, desc, loop=None):
        """
        Arguments:
            - coro: awaitable object
            - desc<tuple>: (Resource, key)
        """
        assert asyncio.iscoroutine(coro), coro
        self._desc = desc
        super().__init__(coro)

    def __repr__(self):
        res, pk = self._desc
        return "<UpdateTask for ({}, {})>".format(res.tag, pk)


def gather(jobs):
    "Aggregate and collect jobs"
    return asyncio.gather(*jobs, return_exceptions=False)


def wrap_generator(func):
    """
    Decorator to convert a generator function to an async function which collects
    and returns generator results, returning a list if there are multiple results
    """

    async def _wrapped(*a, **k):
        r, ret = None, []
        gen = func(*a, **k)
        while True:
            try:
                item = gen.send(r)
            except StopIteration:
                break
            if inspect.isawaitable(item):
                r = await item
            else:
                r = item
            ret.append(r)

        if len(ret) == 1:
            return ret.pop()
        return ret

    return _wrapped


def run_task(func):
    """
    Decorator to wrap an async function in an event loop.
    Use for main sync interface methods.
    """

    def _wrapped(*a, **k):
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(func(*a, **k))

    return _wrapped