#include <catch2/catch.hpp>

#include <alib/variant>
#include <ext/iostream>

#include <global/GlobalData.h>

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

#include "string/LinearString.h"
#include "string/xml/LinearString.h"
#include "string/CyclicString.h"
#include "string/xml/CyclicString.h"

#include "factory/XmlDataFactory.hpp"

#include <primitive/xml/Character.h>
#include <container/xml/ObjectsSet.h>

TEST_CASE ( "String", "[unit][data][string]" ) {
	SECTION ( "LinearString basic usage" ) {
		ext::vector < DefaultSymbolType > expectedContent;
		size_t expectedSize;

		SECTION ( "empty string" ) {
			expectedContent = { };
			expectedSize = 0;
		}

		SECTION ( "string aabac" ) {
			expectedContent = { object::ObjectFactory < >::construct ( "a" ), object::ObjectFactory < >::construct ( "a" ), object::ObjectFactory < >::construct ( "b" ), object::ObjectFactory < >::construct ( "a" ), object::ObjectFactory < >::construct ( "c" ) };
			expectedSize = 5;
		}

		string::LinearString < DefaultSymbolType > string ( expectedContent );
		CHECK ( string.getContent ( ) == expectedContent );
		CHECK ( string.size ( ) == expectedSize );
		CHECK ( string.empty ( ) == ( string.size ( ) == 0 ) );
	}

	SECTION ( "Copy constructor" ) {
		string::LinearString < > string;

		string.accessComponent < string::GeneralAlphabet > ( ).set( { object::ObjectFactory < >::construct ( "1" ), object::ObjectFactory < >::construct ( "2" ), object::ObjectFactory < >::construct ( "3" ) } );
		string.setContent ( { object::ObjectFactory < >::construct ( "1" ), object::ObjectFactory < >::construct ( "2" ), object::ObjectFactory < >::construct ( "1" ) } );
		string::LinearString < > string2 ( string );

		CHECK ( string == string2 );

		string::LinearString < > string3 ( std::move ( string ) );

		CHECK ( string2 == string3 );
	}

	SECTION ( "Xml Parser" ) {

		string::LinearString < > string;

		string.accessComponent < string::GeneralAlphabet > ( ).set( { object::ObjectFactory < >::construct ( "1" ), object::ObjectFactory < >::construct ( "2" ), object::ObjectFactory < >::construct ( "3" ) } );
		string.setContent ( { object::ObjectFactory < >::construct ( "1" ), object::ObjectFactory < >::construct ( "2" ), object::ObjectFactory < >::construct ( "1" ) } );

		{
			ext::deque < sax::Token > tokens = factory::XmlDataFactory::toTokens ( string );
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );

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

			ext::deque < sax::Token > tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			string::LinearString < > string2 = factory::XmlDataFactory::fromTokens ( std::move( tokens2 ) );

			CHECK ( string == string2 );
		}
		{
			std::string tmp = factory::XmlDataFactory::toString ( string );
			ext::cout << tmp << std::endl;
			string::LinearString < > string2 = factory::XmlDataFactory::fromString ( tmp );

			CHECK ( string == string2 );
		}
	}

	SECTION ( "String in map" ) {
		ext::map < ext::variant < string::LinearString < >, int >, int > testMap;
		ext::variant < string::LinearString < >, int > epsVar {
			string::LinearString < > { }
		};

		CHECK ( string::LinearString < > ( ) == epsVar.get < string::LinearString < > > ( ) );
		CHECK ( epsVar.get < string::LinearString < > > ( ) == string::LinearString < > ( ) );

		std::pair < ext::variant < string::LinearString < >, int >, int > epsVarPair = std::make_pair ( epsVar, 10 );
		CHECK ( string::LinearString < > ( ) == epsVarPair.first.get < string::LinearString < > > ( ) );
		CHECK ( epsVarPair.first.get < string::LinearString < > > ( ) == string::LinearString < > ( ) );

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

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

			if ( entry.first.is < string::LinearString < > > ( ) )
				CHECK ( string::LinearString < > ( ) == entry.first.get < string::LinearString < > > ( ) );

			CHECK ( entry.first.get < string::LinearString < > > ( ) == string::LinearString < > ( ) );
		}
	}

	SECTION ( "Test XML Parser 2" ) {
		{
			common::GlobalData::optimizeXml = false;

			string::LinearString < char > s1 ( ext::vector < char > { 'a', 'b' } );
			string::LinearString < object::Object > s2 ( ext::vector < object::Object > { object::ObjectFactory < >::construct ( 'a' ), object::ObjectFactory < >::construct ( 'b' ) } );

			std::string s1xml = factory::XmlDataFactory::toString ( s1 );
			std::string s2xml = factory::XmlDataFactory::toString ( s2 );

			string::LinearString < char > s1c = factory::XmlDataFactory::fromString ( s1xml );
			string::LinearString < object::Object > s1o = factory::XmlDataFactory::fromString ( s1xml );

			string::LinearString < char > s2c = factory::XmlDataFactory::fromString ( s2xml );
			string::LinearString < object::Object > s2o = factory::XmlDataFactory::fromString ( s2xml );

			CHECK ( s1 == s1c );
			CHECK ( s1 == s2c );

			CHECK ( s2 == s1o );
			CHECK ( s2 == s2o );

			common::GlobalData::optimizeXml = true;
		}
		{
			common::GlobalData::optimizeXml = false;

			string::LinearString < ext::set < char > > s1 ( ext::vector < ext::set < char > > { ext::set < char > { 'a' }, ext::set < char > { 'b' } } );
			string::LinearString < object::Object > s2 ( ext::vector < object::Object > { object::ObjectFactory < >::construct ( ext::set < object::Object > { object::ObjectFactory < >::construct ( 'a' ) } ), object::ObjectFactory < >::construct ( ext::set < object::Object > { object::ObjectFactory < >::construct ( 'b' ) } ) } );

			std::string s1xml = factory::XmlDataFactory::toString ( s1 );
			std::string s2xml = factory::XmlDataFactory::toString ( s2 );

			string::LinearString < ext::set < char > > s1c = factory::XmlDataFactory::fromString ( s1xml );
			string::LinearString < object::Object > s1o = factory::XmlDataFactory::fromString ( s1xml );

			string::LinearString < ext::set < char > > s2c = factory::XmlDataFactory::fromString ( s2xml );
			string::LinearString < object::Object > s2o = factory::XmlDataFactory::fromString ( s2xml );

			CHECK ( s1 == s1c );
			CHECK ( s1 == s2c );

			CHECK ( s2 == s1o );
			CHECK ( s2 == s2o );

			common::GlobalData::optimizeXml = true;
		}
	}
}