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

#include "dfa/DFASerializer.h"
#include "dfa/LexerDFASerializer.h"
#include "support/CPPUtils.h"
#include "atn/StarLoopEntryState.h"
#include "atn/ATNConfigSet.h"

#include "dfa/DFA.h"

using namespace antlr4;
using namespace antlr4::dfa;
using namespace antlrcpp;

DFA::DFA(atn::DecisionState *atnStartState) : DFA(atnStartState, 0) {
}

DFA::DFA(atn::DecisionState *atnStartState, size_t decision)
  : atnStartState(atnStartState), s0(nullptr), decision(decision) {

  _precedenceDfa = false;
  if (is<atn::StarLoopEntryState *>(atnStartState)) {
    if (static_cast<atn::StarLoopEntryState *>(atnStartState)->isPrecedenceDecision) {
      _precedenceDfa = true;
      s0 = new DFAState(std::unique_ptr<atn::ATNConfigSet>(new atn::ATNConfigSet()));
      s0->isAcceptState = false;
      s0->requiresFullContext = false;
    }
  }
}

DFA::DFA(DFA &&other) : atnStartState(other.atnStartState), decision(other.decision) {
  // Source states are implicitly cleared by the move.
  states = std::move(other.states);

  other.atnStartState = nullptr;
  other.decision = 0;
  s0 = other.s0;
  other.s0 = nullptr;
  _precedenceDfa = other._precedenceDfa;
  other._precedenceDfa = false;
}

DFA::~DFA() {
  bool s0InList = (s0 == nullptr);
  for (auto *state : states) {
    if (state == s0)
      s0InList = true;
    delete state;
  }

  if (!s0InList)
    delete s0;
}

bool DFA::isPrecedenceDfa() const {
  return _precedenceDfa;
}

DFAState* DFA::getPrecedenceStartState(int precedence) const {
  assert(_precedenceDfa); // Only precedence DFAs may contain a precedence start state.

  auto iterator = s0->edges.find(precedence);
  if (iterator == s0->edges.end())
    return nullptr;

  return iterator->second;
}

void DFA::setPrecedenceStartState(int precedence, DFAState *startState, SingleWriteMultipleReadLock &lock) {
  if (!isPrecedenceDfa()) {
    throw IllegalStateException("Only precedence DFAs may contain a precedence start state.");
  }

  if (precedence < 0) {
    return;
  }

  {
    lock.writeLock();
    s0->edges[precedence] = startState;
    lock.writeUnlock();
  }
}

std::vector<DFAState *> DFA::getStates() const {
  std::vector<DFAState *> result;
  for (auto *state : states)
    result.push_back(state);

  std::sort(result.begin(), result.end(), [](DFAState *o1, DFAState *o2) -> bool {
    return o1->stateNumber < o2->stateNumber;
  });

  return result;
}

std::string DFA::toString(const std::vector<std::string> &tokenNames) {
  if (s0 == nullptr) {
    return "";
  }
  DFASerializer serializer(this, tokenNames);

  return serializer.toString();
}

std::string DFA::toString(const Vocabulary &vocabulary) const {
  if (s0 == nullptr) {
    return "";
  }

  DFASerializer serializer(this, vocabulary);
  return serializer.toString();
}

std::string DFA::toLexerString() {
  if (s0 == nullptr) {
    return "";
  }
  LexerDFASerializer serializer(this);

  return serializer.toString();
}

