#include <catch2/catch.hpp>
#include <alib/vector>

#include "testing/TimeoutAqlTest.hpp"
#include "testing/TestFiles.hpp"

const unsigned RAND_STATES   = 15;
const unsigned RAND_ALPHABET = 5;
const double   RAND_DENSITY  = 2.5;
const size_t   ITERATIONS = 50;

std::string qGenNFA ( ) {
	std::ostringstream oss;
	oss << "execute automaton::generate::RandomAutomatonFactory ";
	oss << "(size_t)" << rand ( ) % RAND_STATES + 1 << " ";
	oss << "(size_t)" << rand ( ) % RAND_ALPHABET + 1 << " ";
	oss << "(bool)true ";
	oss << "(double)\"" << RAND_DENSITY << "\"";
	return oss.str ( );
}

TEST_CASE ( "FA-RG-RE conversions test", "[integration]" ) {
	auto pipeline = GENERATE ( as < std::string > ( ),
			"automaton::convert::ToGrammarLeftRG  - | grammar::convert::ToGrammarRightRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToGrammarRightRG - | grammar::convert::ToGrammarLeftRG  - | grammar::convert::ToAutomaton -",

			"automaton::convert::ToRegExpAlgebraic        - | regexp::convert::ToAutomatonDerivation -",
			"automaton::convert::ToRegExpAlgebraic        - | regexp::convert::ToAutomatonGlushkov   -",
			"automaton::convert::ToRegExpAlgebraic        - | regexp::convert::ToAutomatonThompson   -",
			"automaton::convert::ToRegExpStateElimination - | regexp::convert::ToAutomatonDerivation -",
			"automaton::convert::ToRegExpStateElimination - | regexp::convert::ToAutomatonGlushkov   -",
			"automaton::convert::ToRegExpStateElimination - | regexp::convert::ToAutomatonThompson   -",
			"automaton::convert::ToRegExpKleene           - | regexp::convert::ToAutomatonDerivation -",
			"automaton::convert::ToRegExpKleene           - | regexp::convert::ToAutomatonGlushkov   -",
			"automaton::convert::ToRegExpKleene           - | regexp::convert::ToAutomatonThompson   -",

			"automaton::convert::ToRegExpAlgebraic        - | regexp::convert::ToGrammarRightRGDerivation - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToRegExpAlgebraic        - | regexp::convert::ToGrammarRightRGGlushkov   - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToRegExpStateElimination - | regexp::convert::ToGrammarRightRGDerivation - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToRegExpStateElimination - | regexp::convert::ToGrammarRightRGGlushkov   - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToRegExpKleene           - | regexp::convert::ToGrammarRightRGDerivation - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",
			"automaton::convert::ToRegExpKleene           - | regexp::convert::ToGrammarRightRGGlushkov   - | grammar::convert::ToGrammarLeftRG - | grammar::convert::ToAutomaton -",

			"automaton::convert::ToGrammarLeftRG  - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonDerivation -",
			"automaton::convert::ToGrammarRightRG - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonDerivation -",
			"automaton::convert::ToGrammarLeftRG  - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonThompson   -",
			"automaton::convert::ToGrammarRightRG - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonThompson   -",
			"automaton::convert::ToGrammarLeftRG  - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonGlushkov   -",
			"automaton::convert::ToGrammarRightRG - | grammar::convert::ToRegExp - | regexp::convert::ToAutomatonGlushkov   (FormalRegExp) -" );

	static const std::string qMinimize ( "automaton::simplify::efficient::EpsilonRemoverIncoming - | automaton::determinize::Determinize - | "
			"automaton::simplify::Trim - | automaton::simplify::Minimize - | automaton::simplify::Normalize -" );


	SECTION ( "Files tests" ) {
		for ( const std::string & inputFile : TestFiles::Get ( "/automaton/aconversion.test.*.xml$" ) ) {
			ext::vector < std::string > qs = {
				"execute < " + inputFile + " > $gen",
				"quit compare::AutomatonCompare <( $gen | " + qMinimize + " )" + " <( $gen | " + pipeline + " | " + qMinimize + ")"
			};
			TimeoutAqlTest ( 10s, qs );
		}
	}

	SECTION ( "Random tests" ) {
		for ( size_t i = 0; i < ITERATIONS; i++ ) {
			ext::vector < std::string > qs = {
				qGenNFA ( ) + " > $gen",
				"quit compare::AutomatonCompare <( $gen | " + qMinimize + " )" + " <( $gen | " + pipeline + " | " + qMinimize + ")"
			};
			TimeoutAqlTest ( 10s, qs );
		}
	}
}
