/*
 * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */
package rife.antlr.v4.runtime;

import rife.antlr.v4.runtime.misc.Interval;
import rife.antlr.v4.runtime.tree.ErrorNode;
import rife.antlr.v4.runtime.tree.ErrorNodeImpl;
import rife.antlr.v4.runtime.tree.ParseTree;
import rife.antlr.v4.runtime.tree.ParseTreeListener;
import rife.antlr.v4.runtime.tree.TerminalNode;
import rife.antlr.v4.runtime.tree.TerminalNodeImpl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/** A rule invocation record for parsing.
 *
 *  Contains all of the information about the current rule not stored in the
 *  RuleContext. It handles parse tree children list, Any ATN state
 *  tracing, and the default values available for rule invocations:
 *  start, stop, rule index, current alt number.
 *
 *  Subclasses made for each rule and grammar track the parameters,
 *  return values, locals, and labels specific to that rule. These
 *  are the objects that are returned from rules.
 *
 *  Note text is not an actual field of a rule return value; it is computed
 *  from start and stop using the input stream's toString() method.  I
 *  could add a ctor to this so that we can pass in and store the input
 *  stream, but I'm not sure we want to do that.  It would seem to be undefined
 *  to get the .text property anyway if the rule matches tokens from multiple
 *  input streams.
 *
 *  I do not use getters for fields of objects that are used simply to
 *  group values such as this aggregate.  The getters/setters are there to
 *  satisfy the superclass interface.
 */
public class ParserRuleContext extends RuleContext {
	public static final ParserRuleContext EMPTY = new ParserRuleContext();

	/** If we are debugging or building a parse tree for a visitor,
	 *  we need to track all of the tokens and rule invocations associated
	 *  with this rule's context. This is empty for parsing w/o tree constr.
	 *  operation because we don't the need to track the details about
	 *  how we parse this rule.
	 */
	public List<ParseTree> children;

	/** For debugging/tracing purposes, we want to track all of the nodes in
	 *  the ATN traversed by the parser for a particular rule.
	 *  This list indicates the sequence of ATN nodes used to match
	 *  the elements of the children list. This list does not include
	 *  ATN nodes and other rules used to match rule invocations. It
	 *  traces the rule invocation node itself but nothing inside that
	 *  other rule's ATN submachine.
	 *
	 *  There is NOT a one-to-one correspondence between the children and
	 *  states list. There are typically many nodes in the ATN traversed
	 *  for each element in the children list. For example, for a rule
	 *  invocation there is the invoking state and the following state.
	 *
	 *  The parser setState() method updates field s and adds it to this list
	 *  if we are debugging/tracing.
	 *
	 *  This does not trace states visited during prediction.
	 */
//	public List<Integer> states;

	public Token start, stop;

	/**
	 * The exception that forced this rule to return. If the rule successfully
	 * completed, this is {@code null}.
	 */
	public RecognitionException exception;

	public ParserRuleContext() { }

	/** COPY a ctx (I'm deliberately not using copy constructor) to avoid
	 *  confusion with creating node with parent. Does not copy children
	 *  (except error leaves).
	 *
	 *  This is used in the generated parser code to flip a generic XContext
	 *  node for rule X to a YContext for alt label Y. In that sense, it is
	 *  not really a generic copy function.
	 *
	 *  If we do an error sync() at start of a rule, we might add error nodes
	 *  to the generic XContext so this function must copy those nodes to
	 *  the YContext as well else they are lost!
	 */
	public void copyFrom(ParserRuleContext ctx) {
		this.parent = ctx.parent;
		this.invokingState = ctx.invokingState;

		this.start = ctx.start;
		this.stop = ctx.stop;

		// copy any error nodes to alt label node
		if ( ctx.children!=null ) {
			this.children = new ArrayList<>();
			// reset parent pointer for any error nodes
			for (ParseTree child : ctx.children) {
				if ( child instanceof ErrorNode ) {
					addChild((ErrorNode)child);
				}
			}
		}
	}

	public ParserRuleContext(ParserRuleContext parent, int invokingStateNumber) {
		super(parent, invokingStateNumber);
	}

	// Double dispatch methods for listeners

	public void enterRule(ParseTreeListener listener) { }
	public void exitRule(ParseTreeListener listener) { }

	/** Add a parse tree node to this as a child.  Works for
	 *  internal and leaf nodes. Does not set parent link;
	 *  other add methods must do that. Other addChild methods
	 *  call this.
	 *
	 *  We cannot set the parent pointer of the incoming node
	 *  because the existing interfaces do not have a setParent()
	 *  method and I don't want to break backward compatibility for this.
	 *
	 *  @since 4.7
	 */
	public <T extends ParseTree> T addAnyChild(T t) {
		if ( children==null ) children = new ArrayList<>();
		children.add(t);
		return t;
	}

	public RuleContext addChild(RuleContext ruleInvocation) {
		return addAnyChild(ruleInvocation);
	}

	/** Add a token leaf node child and force its parent to be this node. */
	public TerminalNode addChild(TerminalNode t) {
		t.setParent(this);
		return addAnyChild(t);
	}

	/** Add an error node child and force its parent to be this node.
	 *
	 * @since 4.7
	 */
	public ErrorNode addErrorNode(ErrorNode errorNode) {
		errorNode.setParent(this);
		return addAnyChild(errorNode);
	}

	/** Add a child to this node based upon matchedToken. It
	 *  creates a TerminalNodeImpl rather than using
	 *  {@link Parser#createTerminalNode(ParserRuleContext, Token)}. I'm leaving this
     *  in for compatibility but the parser doesn't use this anymore.
	 */
	@Deprecated
	public TerminalNode addChild(Token matchedToken) {
		TerminalNodeImpl t = new TerminalNodeImpl(matchedToken);
		addAnyChild(t);
		t.setParent(this);
		return t;
	}

	/** Add a child to this node based upon badToken.  It
	 *  creates a ErrorNodeImpl rather than using
	 *  {@link Parser#createErrorNode(ParserRuleContext, Token)}. I'm leaving this
	 *  in for compatibility but the parser doesn't use this anymore.
	 */
	@Deprecated
	public ErrorNode addErrorNode(Token badToken) {
		ErrorNodeImpl t = new ErrorNodeImpl(badToken);
		addAnyChild(t);
		t.setParent(this);
		return t;
	}

//	public void trace(int s) {
//		if ( states==null ) states = new ArrayList<Integer>();
//		states.add(s);
//	}

	/** Used by enterOuterAlt to toss out a RuleContext previously added as
	 *  we entered a rule. If we have # label, we will need to remove
	 *  generic ruleContext object.
	 */
	public void removeLastChild() {
		if ( children!=null ) {
			children.remove(children.size()-1);
		}
	}

	@Override
	/** Override to make type more specific */
	public ParserRuleContext getParent() {
		return (ParserRuleContext)super.getParent();
	}

	@Override
	public ParseTree getChild(int i) {
		return children!=null && i>=0 && i<children.size() ? children.get(i) : null;
	}

	public <T extends ParseTree> T getChild(Class<? extends T> ctxType, int i) {
		if ( children==null || i < 0 || i >= children.size() ) {
			return null;
		}

		int j = -1; // what element have we found with ctxType?
		for (ParseTree o : children) {
			if ( ctxType.isInstance(o) ) {
				j++;
				if ( j == i ) {
					return ctxType.cast(o);
				}
			}
		}
		return null;
	}

	public TerminalNode getToken(int ttype, int i) {
		if ( children==null || i < 0 || i >= children.size() ) {
			return null;
		}

		int j = -1; // what token with ttype have we found?
		for (ParseTree o : children) {
			if ( o instanceof TerminalNode ) {
				TerminalNode tnode = (TerminalNode)o;
				Token symbol = tnode.getSymbol();
				if ( symbol.getType()==ttype ) {
					j++;
					if ( j == i ) {
						return tnode;
					}
				}
			}
		}

		return null;
	}

	public List<TerminalNode> getTokens(int ttype) {
		if ( children==null ) {
			return Collections.emptyList();
		}

		List<TerminalNode> tokens = null;
		for (ParseTree o : children) {
			if ( o instanceof TerminalNode ) {
				TerminalNode tnode = (TerminalNode)o;
				Token symbol = tnode.getSymbol();
				if ( symbol.getType()==ttype ) {
					if ( tokens==null ) {
						tokens = new ArrayList<TerminalNode>();
					}
					tokens.add(tnode);
				}
			}
		}

		if ( tokens==null ) {
			return Collections.emptyList();
		}

		return tokens;
	}

	public <T extends ParserRuleContext> T getRuleContext(Class<? extends T> ctxType, int i) {
		return getChild(ctxType, i);
	}

	public <T extends ParserRuleContext> List<T> getRuleContexts(Class<? extends T> ctxType) {
		if ( children==null ) {
			return Collections.emptyList();
		}

		List<T> contexts = null;
		for (ParseTree o : children) {
			if ( ctxType.isInstance(o) ) {
				if ( contexts==null ) {
					contexts = new ArrayList<T>();
				}

				contexts.add(ctxType.cast(o));
			}
		}

		if ( contexts==null ) {
			return Collections.emptyList();
		}

		return contexts;
	}

	@Override
	public int getChildCount() { return children!=null ? children.size() : 0; }

	@Override
	public Interval getSourceInterval() {
		if ( start == null ) {
			return Interval.INVALID;
		}
		if ( stop==null || stop.getTokenIndex()<start.getTokenIndex() ) {
			return Interval.of(start.getTokenIndex(), start.getTokenIndex()-1); // empty
		}
		return Interval.of(start.getTokenIndex(), stop.getTokenIndex());
	}

	/**
	 * Get the initial token in this context.
	 * Note that the range from start to stop is inclusive, so for rules that do not consume anything
	 * (for example, zero length or error productions) this token may exceed stop.
	 */
	public Token getStart() { return start; }
	/**
	 * Get the final token in this context.
	 * Note that the range from start to stop is inclusive, so for rules that do not consume anything
	 * (for example, zero length or error productions) this token may precede start.
	 */
	public Token getStop() { return stop; }

	/** Used for rule context info debugging during parse-time, not so much for ATN debugging */
	public String toInfoString(Parser recognizer) {
		List<String> rules = recognizer.getRuleInvocationStack(this);
		Collections.reverse(rules);
		return "ParserRuleContext"+rules+"{" +
			"start=" + start +
			", stop=" + stop +
			'}';
	}
}

