# utils.py
# Author: <NAME> - MIT License 2017-2018


class IteratorExhausted(Exception):
    """Exception raised when a closed iterator was requested to produce a value"""
    pass


class EmptyIterator(object):
    """An Iterator that yields nothing"""

    def __init__(self):
        super(EmptyIterator, self).__init__()

    def __len__(self):
        return 0

    async def next(self):
        raise IteratorExhausted()

    def peek(self):
        return self.next()

    def has_next(self):
        return False

    @property
    def nb_reads(self):
        return 0

    @property
    def offset(self):
        return 0


class ArrayIterator(object):
    def __init__(self, array):
        super(ArrayIterator, self).__init__()
        self._array = array

    def has_next(self):
        return len(self._array) > 0

    def next(self):
        if not self.has_next():
            raise StopIteration()
        mu = self._array.pop(0)
        return mu


def selection(triple, variables):
    """Apply selection on a RDF triple"""
    bindings = set()
    if variables[0] is not None:
        bindings.add((variables[0], triple[0]))
    if variables[1] is not None:
        bindings.add((variables[1], triple[1]))
    if variables[2] is not None:
        bindings.add((variables[2], triple[2]))
    return bindings


def apply_bindings(elt, bindings={}):
    """Try to apply bindings to a subject, predicate or object"""
    if not elt.startswith('?'):
        return elt
    return bindings[elt] if elt in bindings else elt


def vars_positions(subject, predicate, obj):
    """Find position of SPARQL variables in a triple pattern"""
    return [var if var.startswith('?') else None for var in [subject, predicate, obj]]


def flattenValue(value):
    if type(value[1]) is list:
        return [(value[0], subval) for subval in value[1]]
    else:
        return [value]


def drop_while(relation, predicate):
    """Drop values while a predicate is met by the values read from the iterator.
    Returns the first value that doesn't meet the predicate.
    """
    v = relation.next()
    while v is not None and predicate(v):
        v = relation.next()
    return v


def collect_while(relation, predicate, firstVal=None):
    """Collect values from an iterator while they met a given predicate.
    Returns the collected values and the first value that doesn't meet the predicate
    """
    res = []
    if firstVal is not None:
        res += flattenValue(firstVal)
    v = relation.peek(None)
    while v is not None and predicate(v):
        res += flattenValue(relation.next())
        v = relation.peek(None)
    return res


def tuple_to_triple(s, p, o):
    return {
        'subject': s,
        'predicate': p,
        'object': o
    }

# utils.py
# Author: <NAME> - MIT License 2017-2018


class IteratorExhausted(Exception):
    """Exception raised when a closed iterator was requested to produce a value"""
    pass


class EmptyIterator(object):
    """An Iterator that yields nothing"""

    def __init__(self):
        super(EmptyIterator, self).__init__()

    def __len__(self):
        return 0

    async def next(self):
        raise IteratorExhausted()

    def peek(self):
        return self.next()

    def has_next(self):
        return False

    @property
    def nb_reads(self):
        return 0

    @property
    def offset(self):
        return 0


class ArrayIterator(object):
    def __init__(self, array):
        super(ArrayIterator, self).__init__()
        self._array = array

    def has_next(self):
        return len(self._array) > 0

    def next(self):
        if not self.has_next():
            raise StopIteration()
        mu = self._array.pop(0)
        return mu


def selection(triple, variables):
    """Apply selection on a RDF triple"""
    bindings = set()
    if variables[0] is not None:
        bindings.add((variables[0], triple[0]))
    if variables[1] is not None:
        bindings.add((variables[1], triple[1]))
    if variables[2] is not None:
        bindings.add((variables[2], triple[2]))
    return bindings


def apply_bindings(elt, bindings={}):
    """Try to apply bindings to a subject, predicate or object"""
    if not elt.startswith('?'):
        return elt
    return bindings[elt] if elt in bindings else elt


def vars_positions(subject, predicate, obj):
    """Find position of SPARQL variables in a triple pattern"""
    return [var if var.startswith('?') else None for var in [subject, predicate, obj]]


def flattenValue(value):
    if type(value[1]) is list:
        return [(value[0], subval) for subval in value[1]]
    else:
        return [value]


def drop_while(relation, predicate):
    """Drop values while a predicate is met by the values read from the iterator.
    Returns the first value that doesn't meet the predicate.
    """
    v = relation.next()
    while v is not None and predicate(v):
        v = relation.next()
    return v


def collect_while(relation, predicate, firstVal=None):
    """Collect values from an iterator while they met a given predicate.
    Returns the collected values and the first value that doesn't meet the predicate
    """
    res = []
    if firstVal is not None:
        res += flattenValue(firstVal)
    v = relation.peek(None)
    while v is not None and predicate(v):
        res += flattenValue(relation.next())
        v = relation.peek(None)
    return res


def tuple_to_triple(s, p, o):
    return {
        'subject': s,
        'predicate': p,
        'object': o
    }

# utils.py # Author: <NAME> - MIT License 2017-2018 Exception raised when a closed iterator was requested to produce a value An Iterator that yields nothing Apply selection on a RDF triple Try to apply bindings to a subject, predicate or object Find position of SPARQL variables in a triple pattern Drop values while a predicate is met by the values read from the iterator.     Returns the first value that doesn't meet the predicate. Collect values from an iterator while they met a given predicate.     Returns the collected values and the first value that doesn't meet the predicate