/* 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.
 */


/// 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.
///
open class ParserRuleContext: RuleContext {
    public static let EMPTY = ParserRuleContext()

    public var visited = false

    /// 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 var children: [ParseTree]?

    /// 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 var start: Token?, stop: Token?

    ///
    /// The exception that forced this rule to return. If the rule successfully
    /// completed, this is `null`.
    ///
    public var exception: RecognitionException?

    public override init() {
        super.init()
    }

    public init(_ parent: ParserRuleContext?, _ invokingStateNumber: Int) {
        super.init(parent, invokingStateNumber)
    }

    /// COPY a ctx (I'm deliberately not using copy constructor) to avoid
    /// confusion with creating node with parent. Does not copy children.
    ///
    /// 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!
    ///
    open func copyFrom(_ ctx: ParserRuleContext) {
        self.parent = ctx.parent
        self.invokingState = ctx.invokingState
        self.start = ctx.start
        self.stop = ctx.stop

        // copy any error nodes to alt label node
        if let ctxChildren = ctx.children {
            self.children = [ParseTree]()
            // reset parent pointer for any error nodes
            for child in ctxChildren {
                if let errNode = child as? ErrorNode {
                    addChild(errNode)
                }
            }
        }
    }

    // Double dispatch methods for listeners

    open func enterRule(_ listener: ParseTreeListener) {
    }

    open func exitRule(_ listener: ParseTreeListener) {
    }

    /// 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
    ///
    open func addAnyChild(_ t: ParseTree) {
        if children == nil {
            children = [ParseTree]()
        }
        children!.append(t)
    }

    open func addChild(_ ruleInvocation: RuleContext) {
        addAnyChild(ruleInvocation)
    }

    /// Add a token leaf node child and force its parent to be this node.
    open func addChild(_ t: TerminalNode) {
        t.setParent(self)
        addAnyChild(t)
    }

    /// Add an error node child and force its parent to be this node.
    open func addErrorNode(_ errorNode: ErrorNode) {
        errorNode.setParent(self)
        addAnyChild(errorNode)
    }


    /// 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.
    ///
    open func removeLastChild() {
        children?.removeLast()
    }


    override
    open func getChild(_ i: Int) -> Tree? {
        guard let children = children, i >= 0 && i < children.count else {
            return nil
        }
        return children[i]
    }

    open func getChild<T: ParseTree>(_ ctxType: T.Type, i: Int) -> T? {
        guard let children = children, i >= 0 && i < children.count else {
            return nil
        }
        var j = -1 // what element have we found with ctxType?
        for o in children {
            if let o = o as? T {
                j += 1
                if j == i {
                    return o
                }
            }
        }

        return nil
    }

    open func getToken(_ ttype: Int, _ i: Int) -> TerminalNode? {
        guard let children = children, i >= 0 && i < children.count else {
            return nil
        }
        var j = -1 // what token with ttype have we found?
        for o in children {
            if let tnode = o as? TerminalNode {
                let symbol = tnode.getSymbol()!
                if symbol.getType() == ttype {
                    j += 1
                    if j == i {
                        return tnode
                    }
                }
            }
        }

        return nil
    }

    open func getTokens(_ ttype: Int) -> [TerminalNode] {
        guard let children = children else {
            return [TerminalNode]()
        }

        return children.compactMap {
            if let tnode = $0 as? TerminalNode, let symbol = tnode.getSymbol(), symbol.getType() == ttype {
                return tnode
            }
            else {
                return nil
            }
        }
    }

    open func getRuleContext<T: ParserRuleContext>(_ ctxType: T.Type, _ i: Int) -> T? {
        return getChild(ctxType, i: i)
    }

    open func getRuleContexts<T: ParserRuleContext>(_ ctxType: T.Type) -> [T] {
        guard let children = children else {
            return [T]()
        }
        return children.compactMap { $0 as? T }
    }

    override
    open func getChildCount() -> Int {
        return children?.count ?? 0
    }

    override
    open subscript(index: Int) -> ParseTree {
        return children![index]
    }

    override
    open func getSourceInterval() -> Interval {
        guard let start = start, let stop = stop else {
             return Interval.INVALID
        }
        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.
    ///
    open func getStart() -> Token? {
        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.
    ///
    open func getStop() -> Token? {
        return stop
    }

    /// Used for rule context info debugging during parse-time, not so much for ATN debugging
    open func toInfoString(_ recognizer: Parser) -> String {
        let rules = Array(recognizer.getRuleInvocationStack(self).reversed())
        let startStr = start?.description ?? "<unknown>"
        let stopStr = stop?.description ?? "<unknown>"
        return "ParserRuleContext\(rules){start=\(startStr)), stop=\(stopStr)}"
    }
}
