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

#include "CyclicString.h"
#include "../exception/AlibException.h"

#include <cassert>
#include <sstream>
#include <algorithm>
#include <typeinfo>

#include "LinearString.h"
#include "Epsilon.h"

namespace string {

CyclicString::CyclicString() {

}

CyclicString::CyclicString(const std::set<alphabet::Symbol>& alphabet, const std::vector<alphabet::Symbol>& data) {
	this->alphabet = alphabet;
	setContent(data);
}

CyclicString::CyclicString(std::set<alphabet::Symbol>&& alphabet, std::vector<alphabet::Symbol>&& data) {
	this->alphabet = std::move(alphabet);
	setContent(std::move(data));
}

CyclicString::CyclicString(const std::vector<alphabet::Symbol>& data) {
	alphabet = std::set<alphabet::Symbol>(data.begin(), data.end());
	setContent(std::move(data));
}

CyclicString::CyclicString(std::vector<alphabet::Symbol>&& data) {
	alphabet = std::set<alphabet::Symbol>(data.begin(), data.end());
	setContent(std::move(data));
}

StringBase* CyclicString::clone() const {
	return new CyclicString(*this);
}

StringBase* CyclicString::plunder() && {
	return new CyclicString(std::move(*this));
}

bool CyclicString::removeSymbolFromAlphabet(const alphabet::Symbol & symbol) {
	if(std::any_of(m_Data.begin(), m_Data.end(), [&](const alphabet::Symbol & s) { return s == symbol; } ) )
		throw exception::AlibException("Input symbol \"" + (std::string) symbol + "\" is used.");
	
	return alphabet.erase(symbol);
}

bool CyclicString::operator <(const StringBase& other) const {
	return other > *this;
}

bool CyclicString::operator ==(const StringBase& other) const {
	return other == *this;
}

bool CyclicString::operator >(const StringBase& other) const {
	return other < *this;
}

const std::vector<alphabet::Symbol>& CyclicString::getContent() const {
	return this->m_Data;
}

//serves as both move and copy content setter
void CyclicString::setContent(std::vector<alphabet::Symbol> data) {
	std::set<alphabet::Symbol> minimalAlphabet(data.begin(), data.end());
	std::set<alphabet::Symbol> unknownSymbols;
	std::set_difference(minimalAlphabet.begin(), minimalAlphabet.end(), alphabet.begin(), alphabet.end(), std::inserter(unknownSymbols, unknownSymbols.end()));

	if(unknownSymbols.size() > 0)
		throw exception::AlibException("Input symbols not in the alphabet.");

	m_Data = data;
	for(unsigned i = 1; i < data.size(); i++) {
		data.push_back(std::move(data[0]));
		data.erase(data.begin());

		if(m_Data > data) m_Data = data;
	}
}

bool CyclicString::isEmpty() const {
	return this->m_Data.size() == 0;
}

bool CyclicString::operator<(const LinearString& other) const {
	if(this->isEmpty() && other.isEmpty()) return alphabet < other.getAlphabet();
	return typeid(*this).before(typeid(other));
}

bool CyclicString::operator<(const CyclicString& other) const {
	return std::tie(m_Data, alphabet) < std::tie(other.m_Data, other.alphabet);
}

bool CyclicString::operator<(const Epsilon& other) const {
	if(this->isEmpty()) return alphabet < other.getAlphabet();
	return typeid(*this).before(typeid(other));
}

bool CyclicString::operator==(const LinearString& other) const {
	if(this->isEmpty() && other.isEmpty()) return alphabet == other.getAlphabet();
	return false;
}

bool CyclicString::operator==(const CyclicString& other) const {
	return m_Data == other.m_Data && alphabet == other.getAlphabet();
}

bool CyclicString::operator==(const Epsilon& other) const {
	if(this->isEmpty()) return alphabet == other.getAlphabet();
	return false;
}

void CyclicString::operator >>(std::ostream& out) const {
	if( this->isEmpty() ) {
		out << "(Epsilon)";
	} else {
		out << "(CyclicString ";
		for(const alphabet::Symbol& symbol : this->m_Data)
			out << symbol;
		out << ")";
	}
}

CyclicString::operator std::string () const {
	std::stringstream ss;
	if( this->isEmpty() ) {
		ss << "E";
	} else {
		ss << "<";
		for(const alphabet::Symbol& symbol : this->m_Data)
			ss << (std::string) symbol;
		ss << ">";
	}
	return std::move(ss).str();
}

} /* namespace string */