from typing import OrderedDict


def normalize_record(pairs, aligned_fields, strip=True):
    """
    Return a list of pairs of (marker, value) from `pairs`, where values
    with the same marker are recombined (i.e. unwrapped). If the marker
    is in `aligned_fields`, spacing will also be normalized (taking the
    length of the longest token) so that the tokens still align visually
    in columns.

    Args:
        pairs: An iterable of (marker, value) pairs.
        aligned_fields: A container of markers that are aligned.
    Return:
        The list of pairs with aligned fields normalized.

    Example:

    >>> data = [
    ...     ('\\t', 'inu=ga   ippiki'),
    ...     ('\\m', 'inu =ga  ichi -hiki'),
    ...     ('\\g', 'dog =NOM one  -CLF.ANIMAL'),
    ...     ('\\t', 'hoeru'),
    ...     ('\\m', 'hoe  -ru'),
    ...     ('\\g', 'bark -IPFV'),
    ...     ('\\f', 'One dog barks.')
    ... ]
    >>> for (mkr, val) in normalize_record(data, set(['\\t', \\g', '\\m'])):
    ...     print(mkr, val)
    \t inu=ga   ippiki           hoeru
    \m inu =ga  ichi -hiki       hoe  -ru
    \g dog =NOM one  -CLF.ANIMAL bark -IPFV
    \f One dog barks.
    """
    field_data = OrderedDict()
    # gather lines with the same marker, and keep track of the longest
    # aligned fields at each position
    maxlens = {}
    for mkr, val in pairs:
        if mkr not in field_data:
            field_data[mkr] = []
        if val is None:
            continue
        field_data[mkr].append(val)
        i = len(field_data[mkr]) - 1
        # this string length counts unicode combining characters, so
        # the lengths may appear off when printed
        if mkr in aligned_fields and len(val) > maxlens.get(i, -1):
            maxlens[i] = len(val)
    # join and normalize spacing (use longest length for each position)
    mkrs = list(field_data.keys())
    for mkr in mkrs:
        data = field_data[mkr]
        if data == []:
            joined = None
        elif mkr in aligned_fields:
            joined = ' '.join(s.ljust(maxlens[i]) for i, s in enumerate(data))
        else:
            joined = ' '.join(data)
        if strip and joined is not None:
            joined = joined.rstrip()
        field_data[mkr] = joined
    return list(field_data.items())