package org.jtwig.parser.parboiled.node;

import org.jtwig.model.expression.ConstantExpression;
import org.jtwig.model.tree.IfNode;
import org.jtwig.model.tree.IfNode.IfConditionNode;
import org.jtwig.parser.parboiled.ParserContext;
import org.jtwig.parser.parboiled.base.*;
import org.jtwig.parser.parboiled.expression.AnyExpressionParser;
import org.jtwig.parser.parboiled.model.Keyword;
import org.parboiled.Rule;

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

import static org.parboiled.Parboiled.createParser;

public class IfNodeParser extends NodeParser<IfNode> {

    public IfNodeParser(ParserContext context) {
        super(IfNodeParser.class, context);
        createParser(IfConditionNodeParser.class, context);
    }

    @Override
    public Rule NodeRule() {
        PositionTrackerParser positionTrackerParser = parserContext().parser(PositionTrackerParser.class);
        IfConditionNodeParser conditionNodeParser = parserContext().parser(IfConditionNodeParser.class);
        return Sequence(
            positionTrackerParser.PushPosition(),
            conditionNodeParser.NodeRule(),
            push(new IfNode(positionTrackerParser.pop(1), conditionNodeParser.pop()))
        );
    }

    public static class IfConditionNodeParser extends BasicParser<Collection<IfConditionNode>> {

        public IfConditionNodeParser(ParserContext context) {
            super(IfConditionNodeParser.class, context);
        }

        Rule NodeRule() {
            PositionTrackerParser positionTrackerParser = parserContext().parser(PositionTrackerParser.class);
            LimitsParser limitsParser = parserContext().parser(LimitsParser.class);
            SpacingParser spacingParser = parserContext().parser(SpacingParser.class);
            AnyExpressionParser anyExpressionParser = parserContext().parser(AnyExpressionParser.class);
            LexicParser lexicParser = parserContext().parser(LexicParser.class);
            CompositeNodeParser compositeNodeParser = parserContext().parser(CompositeNodeParser.class);
            return Sequence(
                push(new ArrayList<IfConditionNode>()),

                // Start
                Sequence(
                    positionTrackerParser.PushPosition(),
                    limitsParser.startCode(), spacingParser.Spacing(),
                    lexicParser.Keyword(Keyword.IF)
                ),

                ifConditionExpression(limitsParser, spacingParser, anyExpressionParser),

                // Content
                compositeNodeParser.NodeRule(),

                peek(3).add(new IfConditionNode(
                    positionTrackerParser.pop(2),
                    anyExpressionParser.pop(1),
                    compositeNodeParser.pop()
                )),

                ZeroOrMore(
                    elseIfCondition()
                ),

                Optional(
                    elseCondition()
                ),

                // End
                Mandatory(Sequence(
                    limitsParser.startCode(), spacingParser.Spacing(),
                    lexicParser.Keyword(Keyword.END_IF), spacingParser.Spacing(),
                    Mandatory(limitsParser.endCode(), "If condition endif code island not closed")
                ), "Missing endif tag")
            );
        }

        public Rule ifConditionExpression(LimitsParser limitsParser, SpacingParser spacingParser, AnyExpressionParser anyExpressionParser) {
            return Sequence(
                spacingParser.Spacing(), Mandatory(anyExpressionParser.ExpressionRule(), "Expecting an expression together with the if construction"),
                spacingParser.Spacing(), Mandatory(limitsParser.endCode(), "If condition code island not closed")
            );
        }

        Rule elseIfCondition() {
            PositionTrackerParser positionTrackerParser = parserContext().parser(PositionTrackerParser.class);
            LimitsParser limitsParser = parserContext().parser(LimitsParser.class);
            SpacingParser spacingParser = parserContext().parser(SpacingParser.class);
            AnyExpressionParser anyExpressionParser = parserContext().parser(AnyExpressionParser.class);
            LexicParser lexicParser = parserContext().parser(LexicParser.class);
            CompositeNodeParser compositeNodeParser = parserContext().parser(CompositeNodeParser.class);
            return Sequence(
                // Start
                Sequence(
                    positionTrackerParser.PushPosition(),

                    limitsParser.startCode(), spacingParser.Spacing(),
                    lexicParser.Keyword(Keyword.ELSE_IF)
                ),

                ifConditionExpression(limitsParser, spacingParser, anyExpressionParser),

                // Content
                compositeNodeParser.NodeRule(),
                peek(3).add(new IfConditionNode(
                    positionTrackerParser.pop(2),
                    anyExpressionParser.pop(1),
                    compositeNodeParser.pop()
                ))
            );
        }


        Rule elseCondition() {
            PositionTrackerParser positionTrackerParser = parserContext().parser(PositionTrackerParser.class);
            LimitsParser limitsParser = parserContext().parser(LimitsParser.class);
            SpacingParser spacingParser = parserContext().parser(SpacingParser.class);
            LexicParser lexicParser = parserContext().parser(LexicParser.class);
            CompositeNodeParser compositeNodeParser = parserContext().parser(CompositeNodeParser.class);
            return Sequence(
                // Start
                Sequence(
                    positionTrackerParser.PushPosition(),
                    limitsParser.startCode(), spacingParser.Spacing(),
                    lexicParser.Keyword(Keyword.ELSE),
                    spacingParser.Spacing(),
                    Mandatory(limitsParser.endCode(), "Expecting ending of else block")
                ),

                compositeNodeParser.NodeRule(),
                peek(2).add(new IfConditionNode(
                    positionTrackerParser.pop(1),
                    new ConstantExpression(positionTrackerParser.currentPosition(), true),
                    compositeNodeParser.pop()
                ))
            );
        }

    }

}
