package net.ranides.assira.grammar.impl;

import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.generic.LazyCaller;
import net.ranides.assira.generic.LazyCallers;
import net.ranides.assira.grammar.G4Node;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;

class NodeForRule extends NodeAbstract implements G4Node {

    private final RuleNode node;

    private final LazyCaller<CQuery<G4Node>> children = LazyCallers.shared();

    public NodeForRule(RootAbstract root, G4Node parent, RuleNode node) {
        super(root, parent);
        this.node = node;
    }

    @Override
    public ParseTree unwrap() {
        return node;
    }

    @Override
    public final CQuery<G4Node> children() {
        return children.computeIfAbsent(this::ichildren);
    }

    private CQuery<G4Node> ichildren() {
        return CQuery.from().lazy(unwrap().getChildCount(), i -> root.wrap(this, unwrap().getChild(i)));
    }

    @Override
    public String shortname() {
        int ruleIndex = node.getRuleContext().getRuleIndex();
        return root.parser().ruleName(ruleIndex);
    }

    @Override
    public String qualifier() {
        return root.branchName(this);
    }

    @Override
    public String text() {
        if(node instanceof ParserRuleContext) {
            ParserRuleContext ctx = (ParserRuleContext) node;
            if(hasValidInterval(ctx)) {
                return ctx.getStart().getInputStream().getText(asInterval(ctx));
            }
        }
        return node.getText();
    }

    private static boolean hasValidInterval(ParserRuleContext context) {
        return context.start != null
            && context.start.getStartIndex() >= 0
            && context.stop != null
            && context.stop.getStopIndex() > context.start.getStartIndex();
    }

    private static Interval asInterval(ParserRuleContext context) {
        return new Interval(context.start.getStartIndex(), context.stop.getStopIndex());
    }

}
