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

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

#include <sstream>
#include <algorithm>
#include <deque>

#include "RankedPattern.h"

namespace tree {

PrefixRankedPattern::PrefixRankedPattern(alphabet::RankedSymbol subtreeWildcard, std::set<alphabet::RankedSymbol> alphabet, std::vector<alphabet::RankedSymbol> data) : RankedPatternAlphabet(std::move(subtreeWildcard)) {
	this->alphabet = std::move(alphabet);
	setContent(std::move(data));
}

PrefixRankedPattern::PrefixRankedPattern(alphabet::RankedSymbol subtreeWildcard, std::vector<alphabet::RankedSymbol> data) : RankedPatternAlphabet(std::move(subtreeWildcard)) {
	arityChecksum(data);

	alphabet = std::set<alphabet::RankedSymbol>(data.begin(), data.end());
	m_Data = std::move(data);
}

PrefixRankedPattern::PrefixRankedPattern(const RankedPattern& tree) : RankedPatternAlphabet(tree.getSubtreeWildcard()) {
	alphabet = tree.getAlphabet();
	std::deque<const RankedNode*> queue {&tree.getRoot()};
	while(!queue.empty()) {
		const RankedNode* elem = queue.back();
		queue.pop_back();

		m_Data.push_back(elem->getSymbol());
		for(const RankedNode* child : elem->getChildren()) {
			queue.push_back(child);
		}
	}
}

TreeBase* PrefixRankedPattern::clone() const {
	return new PrefixRankedPattern(*this);
}

TreeBase* PrefixRankedPattern::plunder() && {
	return new PrefixRankedPattern(std::move(*this));
}

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

	if(this->subtreeWildcard == symbol)
		throw exception::AlibException("Input symbol \"" + (std::string) symbol + "\" is subtreeWildcard.");

	return alphabet.erase(symbol);
}

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

void PrefixRankedPattern::setContent(std::vector<alphabet::RankedSymbol> data) {
	arityChecksum(data);

	std::set<alphabet::RankedSymbol> minimalAlphabet(data.begin(), data.end());
	std::set<alphabet::RankedSymbol> 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.");

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

void PrefixRankedPattern::arityChecksum(const std::vector<alphabet::RankedSymbol>& data) {
	int arityChecksum = 1;
	for(const alphabet::RankedSymbol& symbol : data) {
		arityChecksum += symbol.getRank().getData();
		arityChecksum -= 1;
	}

	if(arityChecksum != 0) throw exception::AlibException("The string does not form a tree");
}

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

int PrefixRankedPattern::compare(const PrefixRankedPattern& other) const {
	auto first = std::tie(m_Data, alphabet);
	auto second = std::tie(other.m_Data, other.alphabet);

	std::compare<decltype(first)> comp;
	return comp(first, second);
}

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

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

} /* namespace tree */