# -*- coding:utf-8 -*-
from functools import wraps


def coroutine(func):
    '''
    Wraps a generator which intended to be used as a pure coroutine by
    .send()ing it values. The only thing that the wrapper does is calling
    .next() for the first time which is required by Python generator protocol.
    '''
    @wraps(func)
    def wrapper(*args, **kwargs):
        g = func(*args, **kwargs)
        next(g)
        return g
    return wrapper

@coroutine
def foreach(coroutine_func):
    '''
    Dispatches each JSON array item to a handler coroutine. A coroutine is
    created anew for each item by calling `coroutine_func` callable. The
    resulting coroutine should accept value in the form of tuple of values
    generated by rich JSON parser: (prefix, event, value).

    First event received by foreach should be a "start_array" event.
    '''
    g = None
    base, event, value = yield
    if event != 'start_array':
        raise Exception('foreach requires "start_array" as the first event, got %s' % repr((base, event, value)))
    START_EVENTS = set(['start_map', 'start_array', 'null', 'boolean', 'number', 'string'])
    itemprefix = base + '.item' if base else 'item'
    while True:
        prefix, event, value = yield
        if prefix == itemprefix and event in START_EVENTS:
            g = coroutine_func()
        if (prefix, event) != (base, 'end_array'):
            g.send((prefix, event, value))

@coroutine
def dispatcher(targets):
    '''
    Dispatches JSON parser events into several handlers depending on event
    prefixes.

    Accepts a list of tuples (base_prefix, coroutine). A coroutine then
    receives all the events with prefixes starting with its base_prefix.
    '''
    while True:
        prefix, event, value = yield
        for base, target in targets:
            if prefix.startswith(base):
                target.send((prefix, event, value))
                break
