import textwrap

import fluent.syntax.ast as FTL
from fluent.syntax.parser import FluentParser, FluentParserStream


fluent_parser = FluentParser(with_spans=False)


def parse(Parser, string):
    if Parser is FluentParser:
        return fluent_parser.parse(string)

    # Parsing a legacy resource.

    # Parse the string into the internal Context.
    parser = Parser()
    # compare-locales expects ASCII strings.
    parser.readContents(string.encode('utf8'))
    # Transform the parsed result which is an iterator into a dict.
    return {ent.key: ent for ent in parser}


def ftl_resource_to_ast(code):
    return fluent_parser.parse(ftl(code))


def ftl_resource_to_json(code):
    return fluent_parser.parse(ftl(code)).to_json()


def ftl_pattern_to_json(code):
    ps = FluentParserStream(ftl(code))
    return fluent_parser.maybe_get_pattern(ps).to_json()


def to_json(merged_iter):
    return {
        path: resource.to_json()
        for path, resource in merged_iter
    }


LOCALIZABLE_ENTRIES = (FTL.Message, FTL.Term)


def get_message(body, ident):
    """Get message called `ident` from the `body` iterable."""
    for entity in body:
        if isinstance(entity, LOCALIZABLE_ENTRIES) and entity.id.name == ident:
            return entity


def get_transform(body, ident):
    """Get entity called `ident` from the `body` iterable."""
    for transform in body:
        if transform.id.name == ident:
            return transform


def skeleton(node):
    """Create a skeleton copy of the given node.

    For localizable entries, the value is None and the attributes are {}.
    That's not a valid Fluent entry, so it requires further manipulation to
    set values and/or attributes.
    """
    if isinstance(node, LOCALIZABLE_ENTRIES):
        return type(node)(id=node.id.clone(), value=None)
    return node.clone()


def ftl(code):
    """Nicer indentation for FTL code.

    The code returned by this function is meant to be compared against the
    output of the FTL Serializer.  The input code will end with a newline to
    match the output of the serializer.
    """

    # The code might be triple-quoted.
    code = code.lstrip('\n')

    return textwrap.dedent(code)


def fold(fun, node, init):
    """Reduce `node` to a single value using `fun`.

    Apply `fun` against an accumulator and each subnode of `node` (in postorder
    traversal) to reduce it to a single value.
    """

    def fold_(vals, acc):
        if not vals:
            return acc

        head = list(vals)[0]
        tail = list(vals)[1:]

        if isinstance(head, FTL.BaseNode):
            acc = fold(fun, head, acc)
        if isinstance(head, list):
            acc = fold_(head, acc)
        if isinstance(head, dict):
            acc = fold_(head.values(), acc)

        return fold_(tail, fun(acc, head))

    return fold_(vars(node).values(), init)
