import ast
import re
from typing import Iterator, Set, Union


def grep(text: str, value: str):
    """An easy 'grep -i' that yields lines where value is found."""
    for line in text.splitlines():
        if value in line:
            yield line


def between(text: str, begin='(', end=')'):
    """Dead easy text between two characters.
    Not recursive or repetitions.
    """
    return text.split(begin)[-1].split(end)[0]


def numbers(text: str) -> Iterator[Union[int, float]]:
    """Gets numbers in strings with other characters.

    Integer Numbers: 1 2 3 987 +4 -8
    Decimal Numbers: 0.1 2. .3 .987 +4.0 -0.8
    Scientific Notation: 1e2 0.2e2 3.e2 .987e2 +4e-1 -8.e+2
    Numbers with percentages: 49% 32.39%

    This returns int or float.
    """
    # From https://regexr.com/33jqd
    for x in re.finditer(r'[+-]?(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?', text):
        yield ast.literal_eval(x.group())


def positive_percentages(
    text: str, lengths: Set[int] = None, decimal_numbers: int = None
) -> Iterator[Union[int, float]]:
    """Gets numbers postfixed with a '%' in strings with other characters.

    1)100% 2)56.78% 3)56 78.90% 4)34.6789% some text

    :param text: The text to search for.
    :param lengths: A set of lengths that the percentage
                    number should have to be considered valid.
                    Ex. {5,6} would validate '90.32' and '100.00'
    """
    # From https://regexr.com/3aumh
    for x in re.finditer(r'[\d|\.]+%', text):
        num = x.group()[:-1]
        if lengths:
            if not len(num) in lengths:
                continue
        if decimal_numbers:
            try:
                pos = num.rindex('.')
            except ValueError:
                continue
            else:
                if len(num) - pos - 1 != decimal_numbers:
                    continue
        yield float(num)


def macs(text: str) -> Iterator[str]:
    """Find MACs in strings with other characters."""
    for x in re.finditer('{0}:{0}:{0}:{0}:{0}:{0}'.format(r'[a-fA-F0-9.+_-]+'), text):
        yield x.group()


def clean(text: str) -> str:
    """Trims the text and replaces multiple spaces with a single space."""
    return ' '.join(text.split())