#
# 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.
from QBCodeSpecs.antlr4.atn.ATNState import StarLoopEntryState

from QBCodeSpecs.antlr4.atn.ATNConfigSet import ATNConfigSet
from QBCodeSpecs.antlr4.dfa.DFAState import DFAState
from QBCodeSpecs.antlr4.error.Errors import IllegalStateException


class DFA(object):

    def __init__(self, atnStartState, decision=0):
        # From which ATN state did we create this DFA?
        self.atnStartState = atnStartState
        self.decision = decision
        # A set of all DFA states. Use {@link Map} so we can get old state back
        #  ({@link Set} only allows you to see if it's there).
        self._states = dict()
        self.s0 = None
        # {@code true} if this DFA is for a precedence decision; otherwise,
        # {@code false}. This is the backing field for {@link #isPrecedenceDfa},
        # {@link #setPrecedenceDfa}.
        self.precedenceDfa = False

        if isinstance(atnStartState, StarLoopEntryState):
            if atnStartState.isPrecedenceDecision:
                self.precedenceDfa = True
                precedenceState = DFAState(configs=ATNConfigSet())
                precedenceState.edges = []
                precedenceState.isAcceptState = False
                precedenceState.requiresFullContext = False
                self.s0 = precedenceState


    # Get the start state for a specific precedence value.
    #
    # @param precedence The current precedence.
    # @return The start state corresponding to the specified precedence, or
    # {@code null} if no start state exists for the specified precedence.
    #
    # @throws IllegalStateException if this is not a precedence DFA.
    # @see #isPrecedenceDfa()

    def getPrecedenceStartState(self, precedence):
        if not self.precedenceDfa:
            raise IllegalStateException("Only precedence DFAs may contain a precedence start state.")

        # s0.edges is never null for a precedence DFA
        if precedence < 0 or precedence >= len(self.s0.edges):
            return None
        return self.s0.edges[precedence]

    # Set the start state for a specific precedence value.
    #
    # @param precedence The current precedence.
    # @param startState The start state corresponding to the specified
    # precedence.
    #
    # @throws IllegalStateException if this is not a precedence DFA.
    # @see #isPrecedenceDfa()
    #
    def setPrecedenceStartState(self, precedence, startState):
        if not self.precedenceDfa:
            raise IllegalStateException("Only precedence DFAs may contain a precedence start state.")

        if precedence < 0:
            return

        # synchronization on s0 here is ok. when the DFA is turned into a
        # precedence DFA, s0 will be initialized once and not updated again
        # s0.edges is never null for a precedence DFA
        if precedence >= len(self.s0.edges):
            ext = [None] * (precedence + 1 - len(self.s0.edges))
            self.s0.edges.extend(ext)
        self.s0.edges[precedence] = startState
    #
    # Sets whether this is a precedence DFA. If the specified value differs
    # from the current DFA configuration, the following actions are taken;
    # otherwise no changes are made to the current DFA.
    #
    # <ul>
    # <li>The {@link #states} map is cleared</li>
    # <li>If {@code precedenceDfa} is {@code false}, the initial state
    # {@link #s0} is set to {@code null}; otherwise, it is initialized to a new
    # {@link DFAState} with an empty outgoing {@link DFAState#edges} array to
    # store the start states for individual precedence values.</li>
    # <li>The {@link #precedenceDfa} field is updated</li>
    # </ul>
    #
    # @param precedenceDfa {@code true} if this is a precedence DFA; otherwise,
    # {@code false}

    def setPrecedenceDfa(self, precedenceDfa):
        if self.precedenceDfa != precedenceDfa:
            self._states = dict()
            if precedenceDfa:
                precedenceState = DFAState(configs=ATNConfigSet())
                precedenceState.edges = []
                precedenceState.isAcceptState = False
                precedenceState.requiresFullContext = False
                self.s0 = precedenceState
            else:
                self.s0 = None
            self.precedenceDfa = precedenceDfa

    @property
    def states(self):
        return self._states

    # Return a list of all states in this DFA, ordered by state number.
    def sortedStates(self):
        return sorted(self._states.keys(), key=lambda state: state.stateNumber)

    def __str__(self):
        return unicode(self)

    def __unicode__(self):
        return self.toString(None)

    def toString(self, literalNames=None, symbolicNames=None):
        if self.s0 is None:
            return ""
        from QBCodeSpecs.antlr4.dfa.DFASerializer import DFASerializer
        serializer = DFASerializer(self, literalNames, symbolicNames)
        return unicode(serializer)

    def toLexerString(self):
        if self.s0 is None:
            return ""
        from QBCodeSpecs.antlr4.dfa.DFASerializer import LexerDFASerializer
        serializer = LexerDFASerializer(self)
        return unicode(serializer)

