package org.jtwig.parser.parboiled.node;

import org.jtwig.model.tree.CompositeNode;
import org.jtwig.model.tree.Node;
import org.jtwig.parser.addon.AddonParserProvider;
import org.jtwig.parser.parboiled.ParserContext;
import org.jtwig.parser.parboiled.base.BasicParser;
import org.jtwig.parser.parboiled.base.PositionTrackerParser;
import org.parboiled.Rule;
import org.parboiled.annotations.Label;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import static org.parboiled.Parboiled.createParser;

public class CompositeNodeParser extends NodeParser<CompositeNode> {
    public CompositeNodeParser(ParserContext context) {
        super(CompositeNodeParser.class, context);
        createParser(NodeListParser.class, context);
    }

    @Override
    @Label("Composite Node")
    public Rule NodeRule() {
        PositionTrackerParser positionTrackerParser = parserContext().parser(PositionTrackerParser.class);
        NodeListParser nodeListParser = parserContext().parser(NodeListParser.class);
        return Sequence(
                positionTrackerParser.PushPosition(),
                nodeListParser.List(),
                push(new CompositeNode(
                        positionTrackerParser.pop(1),
                        nodeListParser.pop()
                ))
        );
    }

    public static class NodeListParser extends BasicParser<Collection<Node>> {
        final Collection<Class> contentParsers;

        public NodeListParser(ParserContext context) {
            super(NodeListParser.class, context);

            contentParsers = new ArrayList<>(Arrays.<Class>asList(
                    BlockNodeParser.class,
                    EmbedNodeParser.class,
                    ForLoopNodeParser.class,
                    IfNodeParser.class,
                    ImportSelfNodeParser.class,
                    ImportNodeParser.class,
                    IncludeNodeParser.class,
                    MacroNodeParser.class,
                    OutputNodeParser.class,
                    SetNodeParser.class,
                    DoNodeParser.class,
                    FlushNodeParser.class,
                    VerbatimNodeParser.class,
                    AutoEscapeNodeParser.class,
                    ContentEscapeNodeParser.class,
                    FilterNodeParser.class,
                    TextNodeParser.class
            ));

            contentParsers.addAll(extractExtraParsers(context.getAddOnParsers()));
        }

        private Collection<? extends Class> extractExtraParsers(Collection<AddonParserProvider> addOnParsers) {
            Collection<Class> result = new ArrayList<>();
            for (AddonParserProvider provider : addOnParsers) {
                result.add(provider.parser());
            }
            return result;
        }

        Rule List() {
            Rule[] rules = new Rule[contentParsers.size()];
            int i = 0;
            for (Class contentParser : contentParsers) {
                NodeParser<Node> parser = (NodeParser<Node>) parserContext().parser(contentParser);
                rules[i++] = Sequence(
                        parser.NodeRule(),
                        peek(1).add(parser.pop())
                );
            }
            return Sequence(
                    push(new ArrayList<Node>()),

                    ZeroOrMore(
                            FirstOf(
                                    FirstOf(rules),
                                    parserContext().parser(CommentParser.class).Comment()
                            )
                    )
            );
        }


    }
}
