/*
 * LinearStringTerminatingSymbol.cpp
 *
 *  Created on: Nov 23, 2013
 *      Author: Jan Travnicek
 */

#include "LinearStringTerminatingSymbol.h"
#include <string/LinearString.h>
#include <exception/CommonException.h>

#include <sstream>
#include <alib/algorithm>

#include <sax/FromXMLParserHelper.h>
#include <string/xml/common/StringFromXmlParserCommon.h>
#include <string/xml/common/StringToXmlComposerCommon.h>
#include <object/Object.h>

#include <registration/ValuePrinterRegistration.hpp>
#include <registration/XmlRegistration.hpp>

namespace string {

LinearStringTerminatingSymbol::LinearStringTerminatingSymbol ( ext::set < DefaultSymbolType > alphabet, DefaultSymbolType terminatingSymbol, ext::vector < DefaultSymbolType > data ) : core::Components < LinearStringTerminatingSymbol, ext::set < DefaultSymbolType >, component::Set, GeneralAlphabet, DefaultSymbolType, component::Value, TerminatingSymbol > ( std::move ( alphabet ), std::move ( terminatingSymbol ) ) {
	setContent ( std::move ( data ) );
}

LinearStringTerminatingSymbol::LinearStringTerminatingSymbol ( DefaultSymbolType terminatingSymbol ) : LinearStringTerminatingSymbol ( ext::set < DefaultSymbolType > { terminatingSymbol }, terminatingSymbol, ext::vector < DefaultSymbolType > ( ) ) {
}

LinearStringTerminatingSymbol::LinearStringTerminatingSymbol ( DefaultSymbolType terminatingSymbol, ext::vector < DefaultSymbolType > data ) : LinearStringTerminatingSymbol ( ext::set < DefaultSymbolType > ( data.begin ( ), data.end ( ) ) + ext::set < DefaultSymbolType > { terminatingSymbol }, terminatingSymbol, data ) {
}

LinearStringTerminatingSymbol::LinearStringTerminatingSymbol ( DefaultSymbolType terminatingSymbol, const LinearString < > & str ) : LinearStringTerminatingSymbol ( str.getAlphabet( ), std::move ( terminatingSymbol ), str.getContent ( ) ) {
}

void LinearStringTerminatingSymbol::appendSymbol ( DefaultSymbolType symbol ) {
	if ( getAlphabet().count ( symbol ) == 0 )
		throw exception::CommonException ( "Input symbol \"" + ext::to_string ( symbol ) + "\" not in the alphabet." );

	m_Data.push_back ( std::move ( symbol ) );
}

const ext::vector < DefaultSymbolType > & LinearStringTerminatingSymbol::getContent ( ) const {
	return this->m_Data;
}

void LinearStringTerminatingSymbol::setContent ( ext::vector < DefaultSymbolType > data ) {
	ext::set < DefaultSymbolType > minimalAlphabet ( data.begin ( ), data.end ( ) );
	ext::set < DefaultSymbolType > unknownSymbols;
	std::set_difference ( minimalAlphabet.begin ( ), minimalAlphabet.end ( ), getAlphabet().begin ( ), getAlphabet().end ( ), std::inserter ( unknownSymbols, unknownSymbols.end ( ) ) );

	if ( !unknownSymbols.empty ( ) )
		throw exception::CommonException ( "Input symbols not in the alphabet." );

	this->m_Data = std::move ( data );
}

bool LinearStringTerminatingSymbol::isEmpty ( ) const {
	return this->m_Data.empty ( );
}

std::ostream & operator << ( std::ostream & out, const LinearStringTerminatingSymbol & instance ) {
	out << "(LinearStringTerminatingSymbol ";
	out << "content = " << instance.getContent ( );
	out << "alphabet = " << instance.getAlphabet ( );
	out << ")";
	return out;
}

LinearStringTerminatingSymbol::operator std::string ( ) const {
	std::stringstream ss;
	ss << "\"";

	for ( const DefaultSymbolType & symbol : this->m_Data )
		ss << symbol;

	ss << "\"";
	return std::move ( ss ).str ( );
}

} /* namespace string */

namespace core {

string::LinearStringTerminatingSymbol xmlApi < string::LinearStringTerminatingSymbol >::parse ( ext::deque < sax::Token >::iterator & input ) {
	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::START_ELEMENT, xmlTagName ( ) );
	ext::set < DefaultSymbolType > alphabet = string::StringFromXmlParserCommon::parseAlphabet < DefaultSymbolType > ( input );
	DefaultSymbolType terminatingSymbol = core::xmlApi < DefaultSymbolType >::parse ( input );
	ext::vector < DefaultSymbolType > content = string::StringFromXmlParserCommon::parseContent < DefaultSymbolType > ( input );
	sax::FromXMLParserHelper::popToken ( input, sax::Token::TokenType::END_ELEMENT, xmlTagName ( ) );

	return string::LinearStringTerminatingSymbol ( alphabet, terminatingSymbol, content );
}

bool xmlApi < string::LinearStringTerminatingSymbol >::first ( const ext::deque < sax::Token >::const_iterator & input ) {
	return sax::FromXMLParserHelper::isToken ( input, sax::Token::TokenType::START_ELEMENT, xmlTagName ( ) );
}

std::string xmlApi < string::LinearStringTerminatingSymbol >::xmlTagName ( ) {
	return "LinearStringTerminatingSymbol";
}

void xmlApi < string::LinearStringTerminatingSymbol >::compose ( ext::deque < sax::Token > & output, const string::LinearStringTerminatingSymbol & data ) {
	output.emplace_back ( xmlTagName ( ), sax::Token::TokenType::START_ELEMENT );
	string::StringToXmlComposerCommon::composeAlphabet ( output, data.getAlphabet() );
	core::xmlApi < DefaultSymbolType >::compose ( output, data.getTerminatingSymbol ( ) );
	string::StringToXmlComposerCommon::composeContent ( output, data.getContent ( ) );
	output.emplace_back ( xmlTagName ( ), sax::Token::TokenType::END_ELEMENT );
}

} /* namespace core */

namespace {

auto valuePrinter = registration::ValuePrinterRegister < string::LinearStringTerminatingSymbol > ( );

auto xmlWrite = registration::XmlWriterRegister < string::LinearStringTerminatingSymbol > ( );
auto xmlRead = registration::XmlReaderRegister < string::LinearStringTerminatingSymbol > ( );

auto xmlGroup1 = registration::XmlRegisterTypeInGroup < object::Object, string::LinearStringTerminatingSymbol > ( );

} /* namespace */
