// Copyright (c) 2012-2022 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 antlr

import (
	"fmt"
	"strconv"
)

type Parser interface {
	Recognizer

	GetInterpreter() *ParserATNSimulator

	GetTokenStream() TokenStream
	GetTokenFactory() TokenFactory
	GetParserRuleContext() ParserRuleContext
	SetParserRuleContext(ParserRuleContext)
	Consume() Token
	GetParseListeners() []ParseTreeListener

	GetErrorHandler() ErrorStrategy
	SetErrorHandler(ErrorStrategy)
	GetInputStream() IntStream
	GetCurrentToken() Token
	GetExpectedTokens() *IntervalSet
	NotifyErrorListeners(string, Token, RecognitionException)
	IsExpectedToken(int) bool
	GetPrecedence() int
	GetRuleInvocationStack(ParserRuleContext) []string
}

type BaseParser struct {
	*BaseRecognizer

	Interpreter     *ParserATNSimulator
	BuildParseTrees bool

	input           TokenStream
	errHandler      ErrorStrategy
	precedenceStack IntStack
	ctx             ParserRuleContext

	tracer         *TraceListener
	parseListeners []ParseTreeListener
	_SyntaxErrors  int
}

// NewBaseParser contains all the parsing support code to embed in parsers. Essentially most of it is error
// recovery stuff.
//
//goland:noinspection GoUnusedExportedFunction
func NewBaseParser(input TokenStream) *BaseParser {

	p := new(BaseParser)

	p.BaseRecognizer = NewBaseRecognizer()

	// The input stream.
	p.input = nil

	// The error handling strategy for the parser. The default value is a new
	// instance of {@link DefaultErrorStrategy}.
	p.errHandler = NewDefaultErrorStrategy()
	p.precedenceStack = make([]int, 0)
	p.precedenceStack.Push(0)

	// The ParserRuleContext object for the currently executing rule.
	// p.is always non-nil during the parsing process.
	p.ctx = nil

	// Specifies whether the parser should construct a parse tree during
	// the parsing process. The default value is {@code true}.
	p.BuildParseTrees = true

	// When setTrace(true) is called, a reference to the
	// TraceListener is stored here, so it can be easily removed in a
	// later call to setTrace(false). The listener itself is
	// implemented as a parser listener so p.field is not directly used by
	// other parser methods.
	p.tracer = nil

	// The list of ParseTreeListener listeners registered to receive
	// events during the parse.
	p.parseListeners = nil

	// The number of syntax errors Reported during parsing. p.value is
	// incremented each time NotifyErrorListeners is called.
	p._SyntaxErrors = 0
	p.SetInputStream(input)

	return p
}

// This field maps from the serialized ATN string to the deserialized [ATN] with
// bypass alternatives.
//
// [ATNDeserializationOptions.isGenerateRuleBypassTransitions]
//
//goland:noinspection GoUnusedGlobalVariable
var bypassAltsAtnCache = make(map[string]int)

// reset the parser's state//
func (p *BaseParser) reset() {
	if p.input != nil {
		p.input.Seek(0)
	}
	p.errHandler.reset(p)
	p.ctx = nil
	p._SyntaxErrors = 0
	p.SetTrace(nil)
	p.precedenceStack = make([]int, 0)
	p.precedenceStack.Push(0)
	if p.Interpreter != nil {
		p.Interpreter.reset()
	}
}

func (p *BaseParser) GetErrorHandler() ErrorStrategy {
	return p.errHandler
}

func (p *BaseParser) SetErrorHandler(e ErrorStrategy) {
	p.errHandler = e
}

// Match current input symbol against {@code ttype}. If the symbol type
// Matches, {@link ANTLRErrorStrategy//ReportMatch} and {@link //consume} are
// called to complete the Match process.
//
// <p>If the symbol type does not Match,
// {@link ANTLRErrorStrategy//recoverInline} is called on the current error
// strategy to attempt recovery. If {@link //getBuildParseTree} is
// {@code true} and the token index of the symbol returned by
// {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to
// the parse tree by calling {@link ParserRuleContext//addErrorNode}.</p>
//
// @param ttype the token type to Match
// @return the Matched symbol
// @panics RecognitionException if the current input symbol did not Match
// {@code ttype} and the error strategy could not recover from the
// mismatched symbol

func (p *BaseParser) Match(ttype int) Token {

	t := p.GetCurrentToken()

	if t.GetTokenType() == ttype {
		p.errHandler.ReportMatch(p)
		p.Consume()
	} else {
		t = p.errHandler.RecoverInline(p)
		if p.HasError() {
			return nil
		}
		if p.BuildParseTrees && t.GetTokenIndex() == -1 {

			// we must have conjured up a new token during single token
			// insertion if it's not the current symbol
			p.ctx.AddErrorNode(t)
		}
	}

	return t
}

// Match current input symbol as a wildcard. If the symbol type Matches
// (i.e. has a value greater than 0), {@link ANTLRErrorStrategy//ReportMatch}
// and {@link //consume} are called to complete the Match process.
//
// <p>If the symbol type does not Match,
// {@link ANTLRErrorStrategy//recoverInline} is called on the current error
// strategy to attempt recovery. If {@link //getBuildParseTree} is
// {@code true} and the token index of the symbol returned by
// {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to
// the parse tree by calling {@link ParserRuleContext//addErrorNode}.</p>
//
// @return the Matched symbol
// @panics RecognitionException if the current input symbol did not Match
// a wildcard and the error strategy could not recover from the mismatched
// symbol

func (p *BaseParser) MatchWildcard() Token {
	t := p.GetCurrentToken()
	if t.GetTokenType() > 0 {
		p.errHandler.ReportMatch(p)
		p.Consume()
	} else {
		t = p.errHandler.RecoverInline(p)
		if p.BuildParseTrees && t.GetTokenIndex() == -1 {
			// we must have conjured up a new token during single token
			// insertion if it's not the current symbol
			p.ctx.AddErrorNode(t)
		}
	}
	return t
}

func (p *BaseParser) GetParserRuleContext() ParserRuleContext {
	return p.ctx
}

func (p *BaseParser) SetParserRuleContext(v ParserRuleContext) {
	p.ctx = v
}

func (p *BaseParser) GetParseListeners() []ParseTreeListener {
	if p.parseListeners == nil {
		return make([]ParseTreeListener, 0)
	}
	return p.parseListeners
}

// AddParseListener registers listener to receive events during the parsing process.
//
// To support output-preserving grammar transformations (including but not
// limited to left-recursion removal, automated left-factoring, and
// optimized code generation), calls to listener methods during the parse
// may differ substantially from calls made by
// [ParseTreeWalker.DEFAULT] used after the parse is complete. In
// particular, rule entry and exit events may occur in a different order
// during the parse than after the parser. In addition, calls to certain
// rule entry methods may be omitted.
//
// With the following specific exceptions, calls to listener events are
// deterministic, i.e. for identical input the calls to listener
// methods will be the same.
//
//   - Alterations to the grammar used to generate code may change the
//     behavior of the listener calls.
//   - Alterations to the command line options passed to ANTLR 4 when
//     generating the parser may change the behavior of the listener calls.
//   - Changing the version of the ANTLR Tool used to generate the parser
//     may change the behavior of the listener calls.
func (p *BaseParser) AddParseListener(listener ParseTreeListener) {
	if listener == nil {
		panic("listener")
	}
	if p.parseListeners == nil {
		p.parseListeners = make([]ParseTreeListener, 0)
	}
	p.parseListeners = append(p.parseListeners, listener)
}

// RemoveParseListener removes listener from the list of parse listeners.
//
// If listener is nil or has not been added as a parse
// listener, this func does nothing.
func (p *BaseParser) RemoveParseListener(listener ParseTreeListener) {

	if p.parseListeners != nil {

		idx := -1
		for i, v := range p.parseListeners {
			if v == listener {
				idx = i
				break
			}
		}

		if idx == -1 {
			return
		}

		// remove the listener from the slice
		p.parseListeners = append(p.parseListeners[0:idx], p.parseListeners[idx+1:]...)

		if len(p.parseListeners) == 0 {
			p.parseListeners = nil
		}
	}
}

// Remove all parse listeners.
func (p *BaseParser) removeParseListeners() {
	p.parseListeners = nil
}

// TriggerEnterRuleEvent notifies all parse listeners of an enter rule event.
func (p *BaseParser) TriggerEnterRuleEvent() {
	if p.parseListeners != nil {
		ctx := p.ctx
		for _, listener := range p.parseListeners {
			listener.EnterEveryRule(ctx)
			ctx.EnterRule(listener)
		}
	}
}

// TriggerExitRuleEvent notifies any parse listeners of an exit rule event.
func (p *BaseParser) TriggerExitRuleEvent() {
	if p.parseListeners != nil {
		// reverse order walk of listeners
		ctx := p.ctx
		l := len(p.parseListeners) - 1

		for i := range p.parseListeners {
			listener := p.parseListeners[l-i]
			ctx.ExitRule(listener)
			listener.ExitEveryRule(ctx)
		}
	}
}

func (p *BaseParser) GetInterpreter() *ParserATNSimulator {
	return p.Interpreter
}

func (p *BaseParser) GetATN() *ATN {
	return p.Interpreter.atn
}

func (p *BaseParser) GetTokenFactory() TokenFactory {
	return p.input.GetTokenSource().GetTokenFactory()
}

// setTokenFactory is used to tell our token source and error strategy about a new way to create tokens.
func (p *BaseParser) setTokenFactory(factory TokenFactory) {
	p.input.GetTokenSource().setTokenFactory(factory)
}

// GetATNWithBypassAlts - the ATN with bypass alternatives is expensive to create, so we create it
// lazily.
func (p *BaseParser) GetATNWithBypassAlts() {

	// TODO - Implement this?
	panic("Not implemented!")

	//	serializedAtn := p.getSerializedATN()
	//	if (serializedAtn == nil) {
	//		panic("The current parser does not support an ATN with bypass alternatives.")
	//	}
	//	result := p.bypassAltsAtnCache[serializedAtn]
	//	if (result == nil) {
	//		deserializationOptions := NewATNDeserializationOptions(nil)
	//		deserializationOptions.generateRuleBypassTransitions = true
	//		result = NewATNDeserializer(deserializationOptions).deserialize(serializedAtn)
	//		p.bypassAltsAtnCache[serializedAtn] = result
	//	}
	//	return result
}

// The preferred method of getting a tree pattern. For example, here's a
// sample use:
//
// <pre>
// ParseTree t = parser.expr()
// ParseTreePattern p = parser.compileParseTreePattern("&ltID&gt+0",
// MyParser.RULE_expr)
// ParseTreeMatch m = p.Match(t)
// String id = m.Get("ID")
// </pre>

//goland:noinspection GoUnusedParameter
func (p *BaseParser) compileParseTreePattern(pattern, patternRuleIndex, lexer Lexer) {

	panic("NewParseTreePatternMatcher not implemented!")
	//
	//	if (lexer == nil) {
	//		if (p.GetTokenStream() != nil) {
	//			tokenSource := p.GetTokenStream().GetTokenSource()
	//			if _, ok := tokenSource.(ILexer); ok {
	//				lexer = tokenSource
	//			}
	//		}
	//	}
	//	if (lexer == nil) {
	//		panic("Parser can't discover a lexer to use")
	//	}

	//	m := NewParseTreePatternMatcher(lexer, p)
	//	return m.compile(pattern, patternRuleIndex)
}

func (p *BaseParser) GetInputStream() IntStream {
	return p.GetTokenStream()
}

func (p *BaseParser) SetInputStream(input TokenStream) {
	p.SetTokenStream(input)
}

func (p *BaseParser) GetTokenStream() TokenStream {
	return p.input
}

// SetTokenStream installs input as the token stream and resets the parser.
func (p *BaseParser) SetTokenStream(input TokenStream) {
	p.input = nil
	p.reset()
	p.input = input
}

// GetCurrentToken returns the current token at LT(1).
//
// [Match] needs to return the current input symbol, which gets put
// into the label for the associated token ref e.g., x=ID.
func (p *BaseParser) GetCurrentToken() Token {
	return p.input.LT(1)
}

func (p *BaseParser) NotifyErrorListeners(msg string, offendingToken Token, err RecognitionException) {
	if offendingToken == nil {
		offendingToken = p.GetCurrentToken()
	}
	p._SyntaxErrors++
	line := offendingToken.GetLine()
	column := offendingToken.GetColumn()
	listener := p.GetErrorListenerDispatch()
	listener.SyntaxError(p, offendingToken, line, column, msg, err)
}

func (p *BaseParser) Consume() Token {
	o := p.GetCurrentToken()
	if o.GetTokenType() != TokenEOF {
		p.GetInputStream().Consume()
	}
	hasListener := p.parseListeners != nil && len(p.parseListeners) > 0
	if p.BuildParseTrees || hasListener {
		if p.errHandler.InErrorRecoveryMode(p) {
			node := p.ctx.AddErrorNode(o)
			if p.parseListeners != nil {
				for _, l := range p.parseListeners {
					l.VisitErrorNode(node)
				}
			}

		} else {
			node := p.ctx.AddTokenNode(o)
			if p.parseListeners != nil {
				for _, l := range p.parseListeners {
					l.VisitTerminal(node)
				}
			}
		}
		//        node.invokingState = p.state
	}

	return o
}

func (p *BaseParser) addContextToParseTree() {
	// add current context to parent if we have a parent
	if p.ctx.GetParent() != nil {
		p.ctx.GetParent().(ParserRuleContext).AddChild(p.ctx)
	}
}

func (p *BaseParser) EnterRule(localctx ParserRuleContext, state, _ int) {
	p.SetState(state)
	p.ctx = localctx
	p.ctx.SetStart(p.input.LT(1))
	if p.BuildParseTrees {
		p.addContextToParseTree()
	}
	if p.parseListeners != nil {
		p.TriggerEnterRuleEvent()
	}
}

func (p *BaseParser) ExitRule() {
	p.ctx.SetStop(p.input.LT(-1))
	// trigger event on ctx, before it reverts to parent
	if p.parseListeners != nil {
		p.TriggerExitRuleEvent()
	}
	p.SetState(p.ctx.GetInvokingState())
	if p.ctx.GetParent() != nil {
		p.ctx = p.ctx.GetParent().(ParserRuleContext)
	} else {
		p.ctx = nil
	}
}

func (p *BaseParser) EnterOuterAlt(localctx ParserRuleContext, altNum int) {
	localctx.SetAltNumber(altNum)
	// if we have a new localctx, make sure we replace existing ctx
	// that is previous child of parse tree
	if p.BuildParseTrees && p.ctx != localctx {
		if p.ctx.GetParent() != nil {
			p.ctx.GetParent().(ParserRuleContext).RemoveLastChild()
			p.ctx.GetParent().(ParserRuleContext).AddChild(localctx)
		}
	}
	p.ctx = localctx
}

// Get the precedence level for the top-most precedence rule.
//
// @return The precedence level for the top-most precedence rule, or -1 if
// the parser context is not nested within a precedence rule.

func (p *BaseParser) GetPrecedence() int {
	if len(p.precedenceStack) == 0 {
		return -1
	}

	return p.precedenceStack[len(p.precedenceStack)-1]
}

func (p *BaseParser) EnterRecursionRule(localctx ParserRuleContext, state, _, precedence int) {
	p.SetState(state)
	p.precedenceStack.Push(precedence)
	p.ctx = localctx
	p.ctx.SetStart(p.input.LT(1))
	if p.parseListeners != nil {
		p.TriggerEnterRuleEvent() // simulates rule entry for
		// left-recursive rules
	}
}

//
// Like {@link //EnterRule} but for recursive rules.

func (p *BaseParser) PushNewRecursionContext(localctx ParserRuleContext, state, _ int) {
	previous := p.ctx
	previous.SetParent(localctx)
	previous.SetInvokingState(state)
	previous.SetStop(p.input.LT(-1))

	p.ctx = localctx
	p.ctx.SetStart(previous.GetStart())
	if p.BuildParseTrees {
		p.ctx.AddChild(previous)
	}
	if p.parseListeners != nil {
		p.TriggerEnterRuleEvent() // simulates rule entry for
		// left-recursive rules
	}
}

func (p *BaseParser) UnrollRecursionContexts(parentCtx ParserRuleContext) {
	_, _ = p.precedenceStack.Pop()
	p.ctx.SetStop(p.input.LT(-1))
	retCtx := p.ctx // save current ctx (return value)
	// unroll so ctx is as it was before call to recursive method
	if p.parseListeners != nil {
		for p.ctx != parentCtx {
			p.TriggerExitRuleEvent()
			p.ctx = p.ctx.GetParent().(ParserRuleContext)
		}
	} else {
		p.ctx = parentCtx
	}
	// hook into tree
	retCtx.SetParent(parentCtx)
	if p.BuildParseTrees && parentCtx != nil {
		// add return ctx into invoking rule's tree
		parentCtx.AddChild(retCtx)
	}
}

func (p *BaseParser) GetInvokingContext(ruleIndex int) ParserRuleContext {
	ctx := p.ctx
	for ctx != nil {
		if ctx.GetRuleIndex() == ruleIndex {
			return ctx
		}
		ctx = ctx.GetParent().(ParserRuleContext)
	}
	return nil
}

func (p *BaseParser) Precpred(_ RuleContext, precedence int) bool {
	return precedence >= p.precedenceStack[len(p.precedenceStack)-1]
}

//goland:noinspection GoUnusedParameter
func (p *BaseParser) inContext(context ParserRuleContext) bool {
	// TODO: useful in parser?
	return false
}

// IsExpectedToken checks whether symbol can follow the current state in the
// {ATN}. The behavior of p.method is equivalent to the following, but is
// implemented such that the complete context-sensitive follow set does not
// need to be explicitly constructed.
//
//	return getExpectedTokens().contains(symbol)
func (p *BaseParser) IsExpectedToken(symbol int) bool {
	atn := p.Interpreter.atn
	ctx := p.ctx
	s := atn.states[p.state]
	following := atn.NextTokens(s, nil)
	if following.contains(symbol) {
		return true
	}
	if !following.contains(TokenEpsilon) {
		return false
	}
	for ctx != nil && ctx.GetInvokingState() >= 0 && following.contains(TokenEpsilon) {
		invokingState := atn.states[ctx.GetInvokingState()]
		rt := invokingState.GetTransitions()[0]
		following = atn.NextTokens(rt.(*RuleTransition).followState, nil)
		if following.contains(symbol) {
			return true
		}
		ctx = ctx.GetParent().(ParserRuleContext)
	}
	if following.contains(TokenEpsilon) && symbol == TokenEOF {
		return true
	}

	return false
}

// GetExpectedTokens and returns the set of input symbols which could follow the current parser
// state and context, as given by [GetState] and [GetContext],
// respectively.
func (p *BaseParser) GetExpectedTokens() *IntervalSet {
	return p.Interpreter.atn.getExpectedTokens(p.state, p.ctx)
}

func (p *BaseParser) GetExpectedTokensWithinCurrentRule() *IntervalSet {
	atn := p.Interpreter.atn
	s := atn.states[p.state]
	return atn.NextTokens(s, nil)
}

// GetRuleIndex get a rule's index (i.e., RULE_ruleName field) or -1 if not found.
func (p *BaseParser) GetRuleIndex(ruleName string) int {
	var ruleIndex, ok = p.GetRuleIndexMap()[ruleName]
	if ok {
		return ruleIndex
	}

	return -1
}

// GetRuleInvocationStack returns a list of the rule names in your parser instance
// leading up to a call to the current rule. You could override if
// you want more details such as the file/line info of where
// in the ATN a rule is invoked.
func (p *BaseParser) GetRuleInvocationStack(c ParserRuleContext) []string {
	if c == nil {
		c = p.ctx
	}
	stack := make([]string, 0)
	for c != nil {
		// compute what follows who invoked us
		ruleIndex := c.GetRuleIndex()
		if ruleIndex < 0 {
			stack = append(stack, "n/a")
		} else {
			stack = append(stack, p.GetRuleNames()[ruleIndex])
		}

		vp := c.GetParent()

		if vp == nil {
			break
		}

		c = vp.(ParserRuleContext)
	}
	return stack
}

// GetDFAStrings returns a list of all DFA states used for debugging purposes
func (p *BaseParser) GetDFAStrings() string {
	return fmt.Sprint(p.Interpreter.decisionToDFA)
}

// DumpDFA prints the whole of the DFA for debugging
func (p *BaseParser) DumpDFA() {
	seenOne := false
	for _, dfa := range p.Interpreter.decisionToDFA {
		if dfa.Len() > 0 {
			if seenOne {
				fmt.Println()
			}
			fmt.Println("Decision " + strconv.Itoa(dfa.decision) + ":")
			fmt.Print(dfa.String(p.LiteralNames, p.SymbolicNames))
			seenOne = true
		}
	}
}

func (p *BaseParser) GetSourceName() string {
	return p.GrammarFileName
}

// SetTrace installs a trace listener for the parse.
//
// During a parse it is sometimes useful to listen in on the rule entry and exit
// events as well as token Matches. This is for quick and dirty debugging.
func (p *BaseParser) SetTrace(trace *TraceListener) {
	if trace == nil {
		p.RemoveParseListener(p.tracer)
		p.tracer = nil
	} else {
		if p.tracer != nil {
			p.RemoveParseListener(p.tracer)
		}
		p.tracer = NewTraceListener(p)
		p.AddParseListener(p.tracer)
	}
}
