// 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"
)

const (
	lexerConfig  = iota // Indicates that this ATNConfig is for a lexer
	parserConfig        // Indicates that this ATNConfig is for a parser
)

// ATNConfig is a tuple: (ATN state, predicted alt, syntactic, semantic
// context). The syntactic context is a graph-structured stack node whose
// path(s) to the root is the rule invocation(s) chain used to arrive in the
// state. The semantic context is the tree of semantic predicates encountered
// before reaching an ATN state.
type ATNConfig struct {
	precedenceFilterSuppressed     bool
	state                          ATNState
	alt                            int
	context                        *PredictionContext
	semanticContext                SemanticContext
	reachesIntoOuterContext        int
	cType                          int // lexerConfig or parserConfig
	lexerActionExecutor            *LexerActionExecutor
	passedThroughNonGreedyDecision bool
}

// NewATNConfig6 creates a new ATNConfig instance given a state, alt and context only
func NewATNConfig6(state ATNState, alt int, context *PredictionContext) *ATNConfig {
	return NewATNConfig5(state, alt, context, SemanticContextNone)
}

// NewATNConfig5 creates a new ATNConfig instance given a state, alt, context and semantic context
func NewATNConfig5(state ATNState, alt int, context *PredictionContext, semanticContext SemanticContext) *ATNConfig {
	if semanticContext == nil {
		panic("semanticContext cannot be nil") // TODO: Necessary?
	}

	pac := &ATNConfig{}
	pac.state = state
	pac.alt = alt
	pac.context = context
	pac.semanticContext = semanticContext
	pac.cType = parserConfig
	return pac
}

// NewATNConfig4 creates a new ATNConfig instance given an existing config, and a state only
func NewATNConfig4(c *ATNConfig, state ATNState) *ATNConfig {
	return NewATNConfig(c, state, c.GetContext(), c.GetSemanticContext())
}

// NewATNConfig3 creates a new ATNConfig instance given an existing config, a state and a semantic context
func NewATNConfig3(c *ATNConfig, state ATNState, semanticContext SemanticContext) *ATNConfig {
	return NewATNConfig(c, state, c.GetContext(), semanticContext)
}

// NewATNConfig2 creates a new ATNConfig instance given an existing config, and a context only
func NewATNConfig2(c *ATNConfig, semanticContext SemanticContext) *ATNConfig {
	return NewATNConfig(c, c.GetState(), c.GetContext(), semanticContext)
}

// NewATNConfig1 creates a new ATNConfig instance given an existing config, a state, and a context only
func NewATNConfig1(c *ATNConfig, state ATNState, context *PredictionContext) *ATNConfig {
	return NewATNConfig(c, state, context, c.GetSemanticContext())
}

// NewATNConfig creates a new ATNConfig instance given an existing config, a state, a context and a semantic context, other 'constructors'
// are just wrappers around this one.
func NewATNConfig(c *ATNConfig, state ATNState, context *PredictionContext, semanticContext SemanticContext) *ATNConfig {
	if semanticContext == nil {
		panic("semanticContext cannot be nil") // TODO: Remove this - probably put here for some bug that is now fixed
	}
	b := &ATNConfig{}
	b.InitATNConfig(c, state, c.GetAlt(), context, semanticContext)
	b.cType = parserConfig
	return b
}

func (a *ATNConfig) InitATNConfig(c *ATNConfig, state ATNState, alt int, context *PredictionContext, semanticContext SemanticContext) {

	a.state = state
	a.alt = alt
	a.context = context
	a.semanticContext = semanticContext
	a.reachesIntoOuterContext = c.GetReachesIntoOuterContext()
	a.precedenceFilterSuppressed = c.getPrecedenceFilterSuppressed()
}

func (a *ATNConfig) getPrecedenceFilterSuppressed() bool {
	return a.precedenceFilterSuppressed
}

func (a *ATNConfig) setPrecedenceFilterSuppressed(v bool) {
	a.precedenceFilterSuppressed = v
}

// GetState returns the ATN state associated with this configuration
func (a *ATNConfig) GetState() ATNState {
	return a.state
}

// GetAlt returns the alternative associated with this configuration
func (a *ATNConfig) GetAlt() int {
	return a.alt
}

// SetContext sets the rule invocation stack associated with this configuration
func (a *ATNConfig) SetContext(v *PredictionContext) {
	a.context = v
}

// GetContext returns the rule invocation stack associated with this configuration
func (a *ATNConfig) GetContext() *PredictionContext {
	return a.context
}

// GetSemanticContext returns the semantic context associated with this configuration
func (a *ATNConfig) GetSemanticContext() SemanticContext {
	return a.semanticContext
}

// GetReachesIntoOuterContext returns the count of references to an outer context from this configuration
func (a *ATNConfig) GetReachesIntoOuterContext() int {
	return a.reachesIntoOuterContext
}

// SetReachesIntoOuterContext sets the count of references to an outer context from this configuration
func (a *ATNConfig) SetReachesIntoOuterContext(v int) {
	a.reachesIntoOuterContext = v
}

// Equals is the default comparison function for an ATNConfig when no specialist implementation is required
// for a collection.
//
// An ATN configuration is equal to another if both have the same state, they
// predict the same alternative, and syntactic/semantic contexts are the same.
func (a *ATNConfig) Equals(o Collectable[*ATNConfig]) bool {
	switch a.cType {
	case lexerConfig:
		return a.LEquals(o)
	case parserConfig:
		return a.PEquals(o)
	default:
		panic("Invalid ATNConfig type")
	}
}

// PEquals is the default comparison function for a Parser ATNConfig when no specialist implementation is required
// for a collection.
//
// An ATN configuration is equal to another if both have the same state, they
// predict the same alternative, and syntactic/semantic contexts are the same.
func (a *ATNConfig) PEquals(o Collectable[*ATNConfig]) bool {
	var other, ok = o.(*ATNConfig)

	if !ok {
		return false
	}
	if a == other {
		return true
	} else if other == nil {
		return false
	}

	var equal bool

	if a.context == nil {
		equal = other.context == nil
	} else {
		equal = a.context.Equals(other.context)
	}

	var (
		nums = a.state.GetStateNumber() == other.state.GetStateNumber()
		alts = a.alt == other.alt
		cons = a.semanticContext.Equals(other.semanticContext)
		sups = a.precedenceFilterSuppressed == other.precedenceFilterSuppressed
	)

	return nums && alts && cons && sups && equal
}

// Hash is the default hash function for a parser ATNConfig, when no specialist hash function
// is required for a collection
func (a *ATNConfig) Hash() int {
	switch a.cType {
	case lexerConfig:
		return a.LHash()
	case parserConfig:
		return a.PHash()
	default:
		panic("Invalid ATNConfig type")
	}
}

// PHash is the default hash function for a parser ATNConfig, when no specialist hash function
// is required for a collection
func (a *ATNConfig) PHash() int {
	var c int
	if a.context != nil {
		c = a.context.Hash()
	}

	h := murmurInit(7)
	h = murmurUpdate(h, a.state.GetStateNumber())
	h = murmurUpdate(h, a.alt)
	h = murmurUpdate(h, c)
	h = murmurUpdate(h, a.semanticContext.Hash())
	return murmurFinish(h, 4)
}

// String returns a string representation of the ATNConfig, usually used for debugging purposes
func (a *ATNConfig) String() string {
	var s1, s2, s3 string

	if a.context != nil {
		s1 = ",[" + fmt.Sprint(a.context) + "]"
	}

	if a.semanticContext != SemanticContextNone {
		s2 = "," + fmt.Sprint(a.semanticContext)
	}

	if a.reachesIntoOuterContext > 0 {
		s3 = ",up=" + fmt.Sprint(a.reachesIntoOuterContext)
	}

	return fmt.Sprintf("(%v,%v%v%v%v)", a.state, a.alt, s1, s2, s3)
}

func NewLexerATNConfig6(state ATNState, alt int, context *PredictionContext) *ATNConfig {
	lac := &ATNConfig{}
	lac.state = state
	lac.alt = alt
	lac.context = context
	lac.semanticContext = SemanticContextNone
	lac.cType = lexerConfig
	return lac
}

func NewLexerATNConfig4(c *ATNConfig, state ATNState) *ATNConfig {
	lac := &ATNConfig{}
	lac.lexerActionExecutor = c.lexerActionExecutor
	lac.passedThroughNonGreedyDecision = checkNonGreedyDecision(c, state)
	lac.InitATNConfig(c, state, c.GetAlt(), c.GetContext(), c.GetSemanticContext())
	lac.cType = lexerConfig
	return lac
}

func NewLexerATNConfig3(c *ATNConfig, state ATNState, lexerActionExecutor *LexerActionExecutor) *ATNConfig {
	lac := &ATNConfig{}
	lac.lexerActionExecutor = lexerActionExecutor
	lac.passedThroughNonGreedyDecision = checkNonGreedyDecision(c, state)
	lac.InitATNConfig(c, state, c.GetAlt(), c.GetContext(), c.GetSemanticContext())
	lac.cType = lexerConfig
	return lac
}

func NewLexerATNConfig2(c *ATNConfig, state ATNState, context *PredictionContext) *ATNConfig {
	lac := &ATNConfig{}
	lac.lexerActionExecutor = c.lexerActionExecutor
	lac.passedThroughNonGreedyDecision = checkNonGreedyDecision(c, state)
	lac.InitATNConfig(c, state, c.GetAlt(), context, c.GetSemanticContext())
	lac.cType = lexerConfig
	return lac
}

//goland:noinspection GoUnusedExportedFunction
func NewLexerATNConfig1(state ATNState, alt int, context *PredictionContext) *ATNConfig {
	lac := &ATNConfig{}
	lac.state = state
	lac.alt = alt
	lac.context = context
	lac.semanticContext = SemanticContextNone
	lac.cType = lexerConfig
	return lac
}

// LHash is the default hash function for Lexer ATNConfig objects, it can be used directly or via
// the default comparator [ObjEqComparator].
func (a *ATNConfig) LHash() int {
	var f int
	if a.passedThroughNonGreedyDecision {
		f = 1
	} else {
		f = 0
	}
	h := murmurInit(7)
	h = murmurUpdate(h, a.state.GetStateNumber())
	h = murmurUpdate(h, a.alt)
	h = murmurUpdate(h, a.context.Hash())
	h = murmurUpdate(h, a.semanticContext.Hash())
	h = murmurUpdate(h, f)
	h = murmurUpdate(h, a.lexerActionExecutor.Hash())
	h = murmurFinish(h, 6)
	return h
}

// LEquals is the default comparison function for Lexer ATNConfig objects, it can be used directly or via
// the default comparator [ObjEqComparator].
func (a *ATNConfig) LEquals(other Collectable[*ATNConfig]) bool {
	var otherT, ok = other.(*ATNConfig)
	if !ok {
		return false
	} else if a == otherT {
		return true
	} else if a.passedThroughNonGreedyDecision != otherT.passedThroughNonGreedyDecision {
		return false
	}

	switch {
	case a.lexerActionExecutor == nil && otherT.lexerActionExecutor == nil:
		return true
	case a.lexerActionExecutor != nil && otherT.lexerActionExecutor != nil:
		if !a.lexerActionExecutor.Equals(otherT.lexerActionExecutor) {
			return false
		}
	default:
		return false // One but not both, are nil
	}

	return a.PEquals(otherT)
}

func checkNonGreedyDecision(source *ATNConfig, target ATNState) bool {
	var ds, ok = target.(DecisionState)

	return source.passedThroughNonGreedyDecision || (ok && ds.getNonGreedy())
}
