#include <list>
#include <variant>
#include "StringTest.h"

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

#include "string/String.h"
#include "string/LinearString.h"
#include "string/CyclicString.h"
#include "string/Epsilon.h"

#include "factory/XmlDataFactory.hpp"

#include "alphabet/Symbol.h"
#include "alphabet/LabeledSymbol.h"
#include "alphabet/BlankSymbol.h"

#define CPPUNIT_IMPLY( x, y )    CPPUNIT_ASSERT ( !( x ) || ( y ) )

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION ( StringTest, "string" );
CPPUNIT_TEST_SUITE_REGISTRATION ( StringTest );

void StringTest::setUp ( ) {
}

void StringTest::tearDown ( ) {
}

void StringTest::testCopyConstruct ( ) {
	string::LinearString linearString;

	linearString.setAlphabet ( { alphabet::symbolFrom ( "1" ), alphabet::symbolFrom ( "2" ), alphabet::symbolFrom ( "3" ) } );
	linearString.setContent ( { alphabet::symbolFrom ( "1" ), alphabet::symbolFrom ( "2" ), alphabet::symbolFrom ( "1" ) } );
	string::String string ( linearString );
	string::String string2 ( string );

	CPPUNIT_ASSERT ( string == string2 );

	string::String string3 ( std::move ( string ) );

	CPPUNIT_ASSERT ( string2 == string3 );
}

void StringTest::testXMLParser ( ) {

	string::LinearString linearString;

	linearString.setAlphabet ( { alphabet::symbolFrom ( "1" ), alphabet::symbolFrom ( "2" ), alphabet::symbolFrom ( "3" ) } );
	linearString.setContent ( { alphabet::symbolFrom ( "1" ), alphabet::symbolFrom ( "2" ), alphabet::symbolFrom ( "1" ) } );

	string::String string ( linearString );
	{
		std::deque < sax::Token > tokens = alib::XmlDataFactory::toTokens ( string );
		std::string tmp;
		sax::SaxComposeInterface::printMemory ( tmp, tokens );

		std::cout << tmp << std::endl;

		std::deque < sax::Token > tokens2;
		sax::SaxParseInterface::parseMemory ( tmp, tokens2 );
		string::String string2 = alib::XmlDataFactory::fromTokens < string::String > ( std::move( tokens2 ) );

		CPPUNIT_ASSERT ( string == string2 );
	}
	{
		std::string tmp = alib::XmlDataFactory::toString ( string );
		std::cout << tmp << std::endl;
		string::String string2 = alib::XmlDataFactory::fromString < string::String > ( tmp );

		CPPUNIT_ASSERT ( string == string2 );
	}
}

void StringTest::testStringInMap ( ) {
	std::map < std::variant < string::Epsilon, int >, int > testMap;
	std::variant < string::Epsilon, int > epsVar {
		string::Epsilon { }
	};

	CPPUNIT_ASSERT ( string::Epsilon::EPSILON == epsVar.get < string::Epsilon > ( ) );
	CPPUNIT_ASSERT ( epsVar.get < string::Epsilon > ( ) == string::Epsilon::EPSILON );

	std::pair < std::variant < string::Epsilon, int >, int > epsVarPair = std::make_pair ( epsVar, 10 );
	CPPUNIT_ASSERT ( string::Epsilon::EPSILON == epsVarPair.first.get < string::Epsilon > ( ) );
	CPPUNIT_ASSERT ( epsVarPair.first.get < string::Epsilon > ( ) == string::Epsilon::EPSILON );

	testMap.insert ( std::make_pair ( std::variant < string::Epsilon, int > { string::Epsilon { }
									  }, 10 ) );
	CPPUNIT_ASSERT ( testMap.find ( std::variant < string::Epsilon, int > { string::Epsilon { }
									} ) != testMap.end ( ) );

	for ( const auto & entry : testMap ) {
		CPPUNIT_ASSERT ( entry.first.is < string::Epsilon > ( ) );

		if ( entry.first.is < string::Epsilon > ( ) )
			CPPUNIT_ASSERT ( string::Epsilon::EPSILON == entry.first.get < string::Epsilon > ( ) );

		CPPUNIT_ASSERT ( entry.first.get < string::Epsilon > ( ) == string::Epsilon::EPSILON );
	}
}