/*
 * RandomAutomatonFactory.cpp
 *
 *  Created on: 6. 11. 2017
 *	  Author: Jan Travnicek
 */

#include "MinimizeGenerator.h"
#include "RandomAutomatonFactory2.h"
#include <registration/AlgoRegistration.hpp>
#include <automaton/simplify/UnreachableStatesRemover.h>
#include <automaton/simplify/UselessStatesRemover.h>
#include <automaton/simplify/Minimize.h>
#include <automaton/generate/RandomizeAutomaton.h>

#include <exception/CommonException.h>

#include <global/GlobalData.h>

namespace automaton {

namespace generate {

automaton::DFA < std::string, unsigned > MinimizeGenerator::generateMinimizeDFA ( size_t statesMinimal, size_t statesDuplicates, size_t statesUnreachable, size_t statesUseless, size_t alphabetSize, bool randomizedAlphabet, double density, size_t expectedSteps ) {
	size_t limit = 10000;

	std::stringstream ss;
	auto backup = common::Streams::log;
	common::Streams::log = ext::reference_wrapper < std::ostream > ( ss );

	auto verbose = common::GlobalData::verbose;
	common::GlobalData::verbose = true;

	while ( limit -- ) {
		ss.str ( "" );

		automaton::DFA < std::string, unsigned > automaton = RandomAutomatonFactory2::generateDFA ( statesMinimal, statesDuplicates, statesUnreachable, statesUseless, alphabetSize, randomizedAlphabet, density );
		automaton = automaton::generate::RandomizeAutomaton::randomize ( automaton );

		size_t states = automaton.getStates ( ).size ( );
		automaton::DFA < std::string, unsigned > unreachable = automaton::simplify::UnreachableStatesRemover::remove ( automaton );
		if ( unreachable.getStates ( ).size ( ) + statesUnreachable != states )
			continue;

		states = unreachable.getStates ( ).size ( );
		automaton::DFA < std::string, unsigned > useless = automaton::simplify::UselessStatesRemover::remove ( unreachable );
		if ( useless.getStates ( ).size ( ) + statesUseless != states )
			continue;

		size_t steps;
		states = useless.getStates ( ).size ( );
		automaton::DFA < std::string, unsigned > minimal = automaton::simplify::Minimize::minimize ( useless, steps );
		if ( ( expectedSteps != 0 && steps != expectedSteps ) || minimal.getStates ( ).size ( ) + statesDuplicates != states )
			continue;

		states = minimal.getStates ( ).size ( );
		if ( statesMinimal != states )
			continue;

		common::GlobalData::verbose = verbose;
		common::Streams::log = backup;

		if ( common::GlobalData::verbose )
			common::Streams::log << ss.str ( );

		return automaton;
	}

	throw exception::CommonException ( "Generating automaton for minimization failed." );
}

} /* namespace generate */

} /* namespace automaton */

namespace {

auto GenerateDFA = registration::AbstractRegister < automaton::generate::MinimizeGenerator, automaton::DFA < std::string, unsigned >, size_t, size_t, size_t, size_t, size_t, bool, double, size_t > ( automaton::generate::MinimizeGenerator::generateMinimizeDFA, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "statesMinimal", "statesDuplicates", "statesUnreachable", "statesUseless", "alphabetSize", "randomizedAlphabet", "density", "steps" );

} /* namespace */

