/*
 * CyclicString.h
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Created on: Nov 23, 2013
 *      Author: Jan Travnicek
 */

#pragma once

#include <sstream>

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

#include <core/components.hpp>

#include <common/DefaultSymbolType.h>

#include <exception/CommonException.h>

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

namespace string {

class GeneralAlphabet;

/**
 * \brief
 * Cyclic string.

 * \details
 * Definition is similar to a linear string however cyclic.
 * S = (A, C),
 * A (Alphabet) = finite set of symbols,
 * C (Content) = linear representation of cyclic string in some rotation
 *
 * Note that two same cyclic strings compare not equal in different rotation. In order to either normalize rotation or compare without influence of particular rotation, use respective algoritm.
 *
 * \tparam SymbolType used for the terminal alphabet
 */
template < class SymbolType = DefaultSymbolType >
class CyclicString final : public core::Components < CyclicString < SymbolType >, ext::set < SymbolType >, component::Set, GeneralAlphabet > {
	/**
	 * Representation of the string content.
	 */
	ext::vector < SymbolType > m_Data;

public:
	/**
	 * \brief Creates a new instance of the string with an empty content.
	 */
	explicit CyclicString ( );

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

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

	/**
	 * \brief Creates a new instance of the string from the standard string. 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 CyclicString ( const std::string & str );

	/**
	 * \brief Creates a new instance of the string from c-string. 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 CyclicString ( const char * str );

	/**
	 * 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 ( ext::set < SymbolType > symbols ) {
		this->template accessComponent < GeneralAlphabet > ( ).add ( std::move ( symbols ) );
	}

	/**
	 * 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 CyclicString & other ) const {
		return std::tie ( m_Data, getAlphabet ( ) ) <=> std::tie ( other.m_Data, other.getAlphabet ( ) );
	}

	/**
	 * 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 CyclicString & other ) const {
		return std::tie ( m_Data, getAlphabet ( ) ) == std::tie ( other.m_Data, other.getAlphabet ( ) );
	}

	/**
	 * 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 CyclicString & instance ) {
		out << "(CyclicString";
		out << "content = " << instance.getContent ( );
		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 >
CyclicString < SymbolType >::CyclicString(ext::set<SymbolType> alphabet, ext::vector<SymbolType> str) : core::Components < CyclicString, ext::set < SymbolType >, component::Set, GeneralAlphabet > ( std::move ( alphabet ) ) {
	setContent(std::move(str));
}

template < class SymbolType >
CyclicString < SymbolType >::CyclicString() : CyclicString ( ext::set < SymbolType > ( ), ext::vector < SymbolType > ( ) ) {
}

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

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

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

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

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

//serves as both move and copy content setter
template < class SymbolType >
void CyclicString < 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.");

	m_Data = std::move(str);
}

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

template < class SymbolType >
CyclicString < 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::CyclicString < 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::CyclicString < 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::CyclicString < 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::CyclicString < 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::CyclicString < SymbolType > > {
	static string::CyclicString < > eval ( string::CyclicString < SymbolType > && value ) {
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getAlphabet ( ) );
		ext::vector < DefaultSymbolType > content = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( value ).getContent ( ) );
		return string::CyclicString < > ( std::move ( alphabet ), std::move ( content ) );
	}
};

} /* namespace core */

extern template class string::CyclicString < >;

