// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "tgbaalgos/isdet.hh"
#include <set>
#include <deque>

namespace spot
{
  namespace
  {
    template<bool count>
    static
    unsigned
    count_nondet_states_aux(const const_tgba_ptr& aut)
    {
      unsigned res = 0;
      typedef std::deque<const state*> todo_list;

      state_set seen;
      todo_list todo;
      {
	const state* s = aut->get_init_state();
	seen.insert(s);
	todo.push_back(s);
      }
      while (!todo.empty())
	{
	  const state* src = todo.front();
	  todo.pop_front();

	  bool nondeterministic = false;
	  bdd available = bddtrue;
	  for (auto i: aut->succ(src))
	    {
	      // If we know the state is nondeterministic, just skip the
	      // test for the remaining transitions.  But don't break
	      // the loop, because we still have to record the
	      // destination states.
	      if (!nondeterministic)
		{
		  bdd label = i->current_condition();
		  if (!bdd_implies(label, available))
		    nondeterministic = true;
		  else
		    available -= label;
		}
	      const state* dst = i->current_state();
	      if (seen.insert(dst).second)
		todo.push_back(dst);
	      else
		dst->destroy();
	    }
	  res += nondeterministic;
	  if (!count && nondeterministic)
	    break;
	}
      for (state_set::const_iterator i = seen.begin(); i != seen.end();)
	{
	  const state* s = *i++;
	  s->destroy();
	}
      return res;
    }
  }

  unsigned
  count_nondet_states(const const_tgba_ptr& aut)
  {
    return count_nondet_states_aux<true>(aut);
  }

  bool
  is_deterministic(const const_tgba_ptr& aut)
  {
    if (aut->is_deterministic())
      return true;
    return !count_nondet_states_aux<false>(aut);
  }

  bool
  is_complete(const const_tgba_ptr& aut)
  {
    state_set seen;
    typedef std::deque<const state*> todo_list;
    todo_list todo;
    bool complete = true;
    {
      const state* s = aut->get_init_state();
      seen.insert(s);
      todo.push_back(s);
    }
    while (!todo.empty())
      {
	const state* src = todo.front();
	todo.pop_front();

	bdd available = bddtrue;
	for (auto i: aut->succ(src))
	  {
	    available -= i->current_condition();
	    const state* dst = i->current_state();
	    if (seen.insert(dst).second)
	      todo.push_back(dst);
	    else
	      dst->destroy();
	  }
	if (available != bddfalse)
	  {
	    complete = false;
	    break;
	  }
      }
    for (state_set::const_iterator i = seen.begin(); i != seen.end();)
      {
	const state* s = *i++;
	s->destroy();
      }
    return complete;
  }
}
