#include <catch2/catch.hpp>
#include <alib/list>
#include <alib/pair>

#include "sax/SaxComposeInterface.h"
#include "sax/SaxParseInterface.h"

#include "regexp/unbounded/UnboundedRegExp.h"
#include <regexp/RegExp.h>

#include "regexp/glushkov/GlushkovFollow.h"
#include "regexp/glushkov/GlushkovIndexate.h"
#include "regexp/glushkov/GlushkovFirst.h"
#include "regexp/glushkov/GlushkovLast.h"

#include <factory/StringDataFactory.hpp>
#include <regexp/string/UnboundedRegExp.h>

TEST_CASE ( "RegExp glushkov test", "[unit][algo][regexp][glushkov]" ) {
	SECTION ( "First" ) {
		{
			std::string input = "#E* #0*";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > first = regexp::GlushkovFirst::first ( indexedRegExp );

			CHECK ( first.size ( ) == 0 );
		}
		{
			std::string input = "#E* a";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > first = regexp::GlushkovFirst::first ( indexedRegExp );

			CHECK ( first.size ( ) == 1 );
		}
	}

	SECTION ( "Last" ) {

		{
			std::string input = "a+a";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > last = regexp::GlushkovLast::last ( indexedRegExp );

			CHECK ( last.size ( ) == 2 );
		}
		{
			std::string input = "(a+a)b";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > last = regexp::GlushkovLast::last ( indexedRegExp );

			CHECK ( last.size ( ) == 1 );
		}
	}

	SECTION ( "Follow" ) {
		{
			std::string input = "(a+a)b";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			auto symbolsIter = indexedRegExp.getAlphabet ( ).begin ( );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow1 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow1.size ( ) == 1 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow2 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow2.size ( ) == 1 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow3 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow3.size ( ) == 0 );
		}
		{
			std::string input = "a+a* (b+a)* c";
			regexp::UnboundedRegExp < > regexp = factory::StringDataFactory::fromString ( input );
			regexp::UnboundedRegExp < ext::pair < DefaultSymbolType, unsigned > > indexedRegExp = regexp::GlushkovIndexate::index ( regexp );

			auto symbolsIter = indexedRegExp.getAlphabet ( ).begin ( );

			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow1 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow1.size ( ) == 0 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow2 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow2.size ( ) == 4 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow3 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow3.size ( ) == 3 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow4 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow4.size ( ) == 3 );

			symbolsIter++;
			ext::set < regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > > follow5 = regexp::GlushkovFollow::follow ( indexedRegExp, regexp::UnboundedRegExpSymbol < ext::pair < DefaultSymbolType, unsigned > > ( * symbolsIter ) );

			CHECK ( follow5.size ( ) == 0 );
		}
	}
}
