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

// PredPrediction maps a predicate to a predicted alternative.
type PredPrediction struct {
	alt  int
	pred SemanticContext
}

func NewPredPrediction(pred SemanticContext, alt int) *PredPrediction {
	return &PredPrediction{alt: alt, pred: pred}
}

func (p *PredPrediction) String() string {
	return "(" + fmt.Sprint(p.pred) + ", " + fmt.Sprint(p.alt) + ")"
}

// DFAState represents a set of possible [ATN] configurations. As Aho, Sethi,
// Ullman p. 117 says: "The DFA uses its state to keep track of all possible
// states the ATN can be in after reading each input symbol. That is to say,
// after reading input a1, a2,..an, the DFA is in a state that represents the
// subset T of the states of the ATN that are reachable from the ATN's start
// state along some path labeled a1a2..an."
//
// In conventional NFA-to-DFA conversion, therefore, the subset T would be a bitset representing the set of
// states the [ATN] could be in. We need to track the alt predicted by each state
// as well, however. More importantly, we need to maintain a stack of states,
// tracking the closure operations as they jump from rule to rule, emulating
// rule invocations (method calls). I have to add a stack to simulate the proper
// lookahead sequences for the underlying LL grammar from which the ATN was
// derived.
//
// I use a set of [ATNConfig] objects, not simple states. An [ATNConfig] is both a
// state (ala normal conversion) and a [RuleContext] describing the chain of rules
// (if any) followed to arrive at that state.
//
// A [DFAState] may have multiple references to a particular state, but with
// different [ATN] contexts (with same or different alts) meaning that state was
// reached via a different set of rule invocations.
type DFAState struct {
	stateNumber int
	configs     *ATNConfigSet

	// edges elements point to the target of the symbol. Shift up by 1 so (-1)
	// Token.EOF maps to the first element.
	edges []*DFAState

	isAcceptState bool

	// prediction is the 'ttype' we match or alt we predict if the state is 'accept'.
	// Set to ATN.INVALID_ALT_NUMBER when predicates != nil or
	// requiresFullContext.
	prediction int

	lexerActionExecutor *LexerActionExecutor

	// requiresFullContext indicates it was created during an SLL prediction that
	// discovered a conflict between the configurations in the state. Future
	// ParserATNSimulator.execATN invocations immediately jump doing
	// full context prediction if true.
	requiresFullContext bool

	// predicates is the predicates associated with the ATN configurations of the
	// DFA state during SLL parsing. When we have predicates, requiresFullContext
	// is false, since full context prediction evaluates predicates on-the-fly. If
	// d is
	// not nil, then prediction is ATN.INVALID_ALT_NUMBER.
	//
	// We only use these for non-requiresFullContext but conflicting states. That
	// means we know from the context (it's $ or we don't dip into outer context)
	// that it's an ambiguity not a conflict.
	//
	// This list is computed by
	// ParserATNSimulator.predicateDFAState.
	predicates []*PredPrediction
}

func NewDFAState(stateNumber int, configs *ATNConfigSet) *DFAState {
	if configs == nil {
		configs = NewATNConfigSet(false)
	}

	return &DFAState{configs: configs, stateNumber: stateNumber}
}

// GetAltSet gets the set of all alts mentioned by all ATN configurations in d.
func (d *DFAState) GetAltSet() []int {
	var alts []int

	if d.configs != nil {
		for _, c := range d.configs.configs {
			alts = append(alts, c.GetAlt())
		}
	}

	if len(alts) == 0 {
		return nil
	}

	return alts
}

func (d *DFAState) getEdges() []*DFAState {
	return d.edges
}

func (d *DFAState) numEdges() int {
	return len(d.edges)
}

func (d *DFAState) getIthEdge(i int) *DFAState {
	return d.edges[i]
}

func (d *DFAState) setEdges(newEdges []*DFAState) {
	d.edges = newEdges
}

func (d *DFAState) setIthEdge(i int, edge *DFAState) {
	d.edges[i] = edge
}

func (d *DFAState) setPrediction(v int) {
	d.prediction = v
}

func (d *DFAState) String() string {
	var s string
	if d.isAcceptState {
		if d.predicates != nil {
			s = "=>" + fmt.Sprint(d.predicates)
		} else {
			s = "=>" + fmt.Sprint(d.prediction)
		}
	}

	return fmt.Sprintf("%d:%s%s", d.stateNumber, fmt.Sprint(d.configs), s)
}

func (d *DFAState) Hash() int {
	h := murmurInit(7)
	h = murmurUpdate(h, d.configs.Hash())
	return murmurFinish(h, 1)
}

// Equals returns whether d equals other. Two DFAStates are equal if their ATN
// configuration sets are the same. This method is used to see if a state
// already exists.
//
// Because the number of alternatives and number of ATN configurations are
// finite, there is a finite number of DFA states that can be processed. This is
// necessary to show that the algorithm terminates.
//
// Cannot test the DFA state numbers here because in
// ParserATNSimulator.addDFAState we need to know if any other state exists that
// has d exact set of ATN configurations. The stateNumber is irrelevant.
func (d *DFAState) Equals(o Collectable[*DFAState]) bool {
	if d == o {
		return true
	}

	return d.configs.Equals(o.(*DFAState).configs)
}
