/*
 * WildcardLinearString.h
 *
 *  Created on: 10. 4. 2018
 *      Author: Tomas Capek
 */

#pragma once

#include <sstream>

#include <alib/set>
#include <alib/vector>
#include <alib/algorithm>

#include <core/components.hpp>

#include <alphabet/WildcardSymbol.h>
#include <common/DefaultSymbolType.h>

#include <exception/CommonException.h>

#include <core/normalize.hpp>
#include <alphabet/common/SymbolNormalize.h>

#include "LinearString.h"

namespace string {

class GeneralAlphabet;
class WildcardSymbol;

/**
 * \brief
 * Linear wildcard string.

 * \details
 * S = (A, C, W),
 * A (Alphabet) = finite set of symbols,
 * C (Content) = representation of the string content
 * W (Wildcard symbol) = symbol representing an wildcard symbol
 *
 * \tparam SymbolType used for the terminal alphabet
 */
template < class SymbolType = DefaultSymbolType >
class WildcardLinearString final : public core::Components < WildcardLinearString < SymbolType >, ext::set < SymbolType >, component::Set, GeneralAlphabet, SymbolType, component::Value, WildcardSymbol > {
	/**
	 * Representation of the string content.
	 */
	ext::vector < SymbolType > m_Data;

public:

	/**
	 * \brief Creates a new instance of the string with a concrete alphabet, content and wildcard.
	 *
	 * \param alphabet the initial alphabet of the string
	 * \param str the initial content of the string
	 * \param wildcard the explicit wildcard symbol
	 */
	explicit WildcardLinearString ( ext::set < SymbolType > alphabet, ext::vector < SymbolType > str, SymbolType wildcard );

	/**
	 * \brief Creates a new instance of the string based on content and the wildcard, the alphabet is implicitly created from the content.
	 *
	 * \param str the initial content of the string
	 * \param wildcard the explicit wildcard symbol
	 */
	explicit WildcardLinearString ( ext::vector < SymbolType > str, SymbolType wildcard );

	/**
	 * \brief Creates a new instance of the string from the standard string and the wildcard. The alphabet is deduced from the content. The constructor expects SymbolType of the string is constructible from char.
	 *
	 * \param str the initial content of the string
	 * \param wildcard the explicit wildcard symbol
	 */
	explicit WildcardLinearString ( const std::string & str, char wildcard );

	/**
	 * \brief Creates a new instance of the string from the standard string. The wildcard is defaultly constructed. The alphabet is deduced from the content. The constructor expects SymbolType of the string is constructible from char.
	 *
	 * \param str the initial content of the string
	 */
	explicit WildcardLinearString ( const std::string & str );

	/**
	 * \brief Creates a new instance of the string from c-string and the wildcard. The alphabet is deduced from the content. The constructor expects SymbolType of the string is constructible from char.
	 *
	 * \param str the initial content of the string
	 * \param wildcard the explicit wildcard symbol
	 */
	explicit WildcardLinearString ( const char * str , char wildcard );

	/**
	 * \brief Creates a new instance of the string based on a linear string. It copies the alphabet and content. The wildcard is set from the the parametery.
	 *
	 * \param string the linear string to base the string on
	 * \param wildcard the explicit wildcard symbol
	 */
	explicit WildcardLinearString ( const LinearString < SymbolType > & string, SymbolType wildcard );

	/**
	 * \brief Creates a new instance of the string based on a linear string. It copies the alphabet and content. The wildcard is defaultly constructed.
	 *
	 * \param string the linear string to base the string on
	 */
	explicit WildcardLinearString ( const LinearString < SymbolType > & string );

	/**
	 * Getter of the alphabet.
	 *
	 * \returns the alphabet of the string
	 */
	const ext::set < SymbolType > & getAlphabet ( ) const & {
		return this->template accessComponent < GeneralAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the alphabet.
	 *
	 * \returns the alphabet of the string
	 */
	ext::set < SymbolType > && getAlphabet ( ) && {
		return std::move ( this->template accessComponent < GeneralAlphabet > ( ).get ( ) );
	}

	/**
	 * Adder of an alphabet symbols.
	 *
	 * \param symbols the new symbols to be added to the alphabet
	 */
	void extendAlphabet ( const ext::set < SymbolType > & symbols ) {
		this->template accessComponent < GeneralAlphabet > ( ).add( symbols );
	}

	/**
	 * Getter of the wildcard symbol
	 *
	 * \returns the wildcard symbol of the string
	 */
	const SymbolType & getWildcardSymbol ( ) const & {
		return this->template accessComponent < WildcardSymbol > ( ).get ( );
	}

	/**
	 * Getter of the wildcard symbol
	 *
	 * \returns the wildcard symbol of the string
	 */
	SymbolType && getWildcardSymbol ( ) && {
		return std::move ( this->template accessComponent < WildcardSymbol > ( ).get ( ) );
	}

	/**
	 * Setter of the wildcard symbol
	 *
	 * \param wildcard new wildcard symbol of the string
	 *
	 * \returns true if the wildcard symbol was indeed changed
	 */
	bool setWildcardSymbol ( SymbolType wildcard ) {
		return this-> template accessComponent < WildcardSymbol > ( ).set ( std::move ( wildcard ) );
	}

	/**
	 * Appender of a symbol to the string.
	 *
	 * \param symbol the symbol to be added to the end of the string
	 */
	void appendSymbol ( SymbolType symbol );

	/**
	 * Getter of the string content.
	 *
	 * \return List of symbols forming string.
	 */
	const ext::vector < SymbolType > & getContent ( ) const &;

	/**
	 * Getter of the string content.
	 *
	 * \return List of symbols forming string.
	 */
	ext::vector < SymbolType > && getContent ( ) &&;

	/**
	 * Setter of the string content.
	 *
	 * \throws CommonException when new string contains symbols not present in the alphabet
	 *
	 * \param new List of symbols forming string.
	 */
	void setContent ( ext::vector < SymbolType > str );

	/**
	 * Test function to determine whether the cyclic string is empty
	 *
	 * \return true if string is an empty word (vector length is 0)
	 */
	bool isEmpty ( ) const;

	/**
	 * The three way comparison implementation
	 *
	 * \param other the other instance
	 *
	 * \returns the ordering between this object and the @p other.
	 */
	auto operator <=> ( const WildcardLinearString & other ) const {
		return std::tie ( m_Data, getAlphabet ( ), getWildcardSymbol ( ) ) <=> std::tie ( other.m_Data, other.getAlphabet ( ), other.getWildcardSymbol ( ) );
	}

	/**
	 * The equality comparison implementation.
	 *
	 * \param other the other object to compare with.
	 *
	 * \returns true if this and other objects are equal, false othervise
	 */
	bool operator == ( const WildcardLinearString & other ) const {
		return std::tie ( m_Data, getAlphabet ( ), getWildcardSymbol ( ) ) == std::tie ( other.m_Data, other.getAlphabet ( ), other.getWildcardSymbol ( ) );
	}

	/**
	 * Print this object as raw representation to ostream.
	 *
	 * \param out ostream where to print
	 * \param instance object to print
	 *
	 * \returns modified output stream
	 */
	friend std::ostream & operator << ( std::ostream & out, const WildcardLinearString & instance ) {
		out << "(WildcardLinearString";
		out << "content = " << instance.getContent ( );
		out << "wildcard = " << instance.getWildcardSymbol ( );
		out << "alphabet = " << instance.getAlphabet ( );
		out << ")";
		return out;
	}

	/**
	 * Casts this instance to as compact as possible string representation.
	 *
	 * \returns string representation of the object
	 */
	explicit operator std::string ( ) const;

};

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(ext::set<SymbolType> alphabet, ext::vector<SymbolType> str, SymbolType wildcard) : core::Components < WildcardLinearString, ext::set < SymbolType >, component::Set, GeneralAlphabet, SymbolType, component::Value, WildcardSymbol > ( std::move ( alphabet ), std::move(wildcard) ) {
	setContent(std::move(str));
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(ext::vector<SymbolType> str, SymbolType wildcard) : WildcardLinearString ( ext::set < SymbolType > ( str.begin(), str.end() ) + ext::set < SymbolType > { wildcard }, str, wildcard ) {
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(const std::string & str, char wildcard) : WildcardLinearString ( ext::vector < SymbolType > ( str.begin ( ), str.end ( ) ), SymbolType ( wildcard ) ) {
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(const std::string & str) : WildcardLinearString ( str, alphabet::WildcardSymbol::instance < char > ( ) ) {
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(const char* str, char wildcard) : WildcardLinearString ( ( std::string ) str, wildcard ) {
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(const LinearString < SymbolType > & string, SymbolType wildcard) : WildcardLinearString ( string.getAlphabet( ) + ext::set < SymbolType > { wildcard }, string.getContent ( ), wildcard ) {
}

template < class SymbolType >
WildcardLinearString < SymbolType >::WildcardLinearString(const LinearString < SymbolType > & string) : WildcardLinearString ( string, alphabet::WildcardSymbol::instance < SymbolType > ( ) ) {
}

template < class SymbolType >
void WildcardLinearString < SymbolType >::appendSymbol ( SymbolType 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 ) );
}

template < class SymbolType >
const ext::vector < SymbolType > & WildcardLinearString < SymbolType >::getContent ( ) const & {
	return this->m_Data;
}

template < class SymbolType >
ext::vector < SymbolType > && WildcardLinearString < SymbolType >::getContent ( ) && {
	return std::move ( this->m_Data );
}

template < class SymbolType >
void WildcardLinearString < SymbolType >::setContent ( ext::vector < SymbolType > str ) {
	ext::set < SymbolType > minimalAlphabet ( str.begin ( ), str.end ( ) );
	ext::set < SymbolType > 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 ( str );
}

template < class SymbolType >
bool WildcardLinearString < SymbolType >::isEmpty ( ) const {
	return this->m_Data.empty ( );
}

template < class SymbolType >
WildcardLinearString < SymbolType >::operator std::string ( ) const {
	std::stringstream ss;
	ss << "\"";

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

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

} /* namespace string */

namespace core {

/**
 * Helper class specifying constraints for the string's internal alphabet component.
 *
 * \tparam SymbolType used for the alphabet of the string.
 */
template < class SymbolType >
class SetConstraint< string::WildcardLinearString < SymbolType >, SymbolType, string::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used in the string.
	 *
	 * \param string the tested string
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const string::WildcardLinearString < SymbolType > & str, const SymbolType & symbol ) {
		const ext::vector<SymbolType>& content = str.getContent ( );
		return std::find(content.begin(), content.end(), symbol) != content.end();
	}

	/**
	 * Returns true as all symbols are possibly available to be in an alphabet.
	 *
	 * \param string the tested string
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const string::WildcardLinearString < SymbolType > &, const SymbolType & ) {
		return true;
	}

	/**
	 * All symbols are valid as symbols of an alphabet.
	 *
	 * \param string the tested string
	 * \param symbol the tested symbol
	 */
	static void valid ( const string::WildcardLinearString < SymbolType > &, const SymbolType & ) {
	}
};

/**
 * Helper class specifying constraints for the string's internal wildcard element.
 *
 * \tparam SymbolType used for the alphabet of the string.
 */
template<class SymbolType >
class ElementConstraint< string::WildcardLinearString < SymbolType >, SymbolType, string::WildcardSymbol > {
public:
	/**
	 * Determines whether the symbol is available in the string's alphabet.
	 *
	 * \param string the tested string
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is already in the alphabet of the string
	 */
	static bool available ( const string::WildcardLinearString < SymbolType > & string, const SymbolType & symbol ) {
		return string.getAlphabet ( ).count ( symbol );
	}

	/**
	 * All symbols are valid as an initial sildcard symbol of the string.
	 *
	 * \param string the tested string
	 * \param symbol the tested symbol
	 */
	static void valid ( const string::WildcardLinearString < SymbolType > &, const SymbolType & ) {
	}

};

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the string with default template parameters or unmodified instance if the template parameters were already default ones
 */
template < class SymbolType >
struct normalize < string::WildcardLinearString < SymbolType > > {
	static string::WildcardLinearString < > eval ( string::WildcardLinearString < SymbolType > && value ) {
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlphabet ( ) );
		ext::vector < DefaultSymbolType > content = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( value ).getContent ( ) );
		DefaultSymbolType wildcard = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( value ).getWildcardSymbol ( ) );
		return string::WildcardLinearString < > ( std::move ( alphabet ), std::move ( content ), std::move ( wildcard ) );
	}
};

} /* namespace core */

extern template class string::WildcardLinearString < >;

