/*
 * GrammarDiff.h
 *
 *  Created on: Apr 1, 2013
 *      Author: honza
 */

#pragma once

#include <compare/DiffAux.h>
#include <compare/GrammarCompare.h>

#include <alib/set>
#include <alib/map>
#include <alib/list>
#include <alib/utility>
#include <alib/vector>
#include <alib/typeinfo>
#include <alib/iostream>
#include <ostream>
#include <alib/algorithm>

#include "grammar/Regular/LeftLG.h"
#include "grammar/Regular/LeftRG.h"
#include "grammar/Regular/RightLG.h"
#include "grammar/Regular/RightRG.h"
#include "grammar/ContextFree/LG.h"
#include "grammar/ContextFree/CFG.h"
#include "grammar/ContextFree/EpsilonFreeCFG.h"
#include "grammar/ContextFree/CNF.h"
#include "grammar/ContextFree/GNF.h"
#include "grammar/ContextSensitive/CSG.h"
#include "grammar/ContextSensitive/NonContractingGrammar.h"
#include "grammar/Unrestricted/ContextPreservingUnrestrictedGrammar.h"
#include "grammar/Unrestricted/UnrestrictedGrammar.h"

namespace compare {

class GrammarDiff {
private:
	template < class SymbolType >
	static void printDiff(const grammar::LeftLG < SymbolType > & a, const grammar::LeftLG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::LeftRG < SymbolType > & a, const grammar::LeftRG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::RightLG < SymbolType > & a, const grammar::RightLG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::RightRG < SymbolType > & a, const grammar::RightRG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::LG < SymbolType > & a, const grammar::LG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::CFG < SymbolType > & a, const grammar::CFG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::EpsilonFreeCFG < SymbolType > & a, const grammar::EpsilonFreeCFG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::CNF < SymbolType > & a, const grammar::CNF < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::GNF < SymbolType > & a, const grammar::GNF < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::CSG < SymbolType > & a, const grammar::CSG < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::NonContractingGrammar < SymbolType > & a, const grammar::NonContractingGrammar < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::ContextPreservingUnrestrictedGrammar < SymbolType > & a, const grammar::ContextPreservingUnrestrictedGrammar < SymbolType > & b, std::ostream & out );

	template < class SymbolType >
	static void printDiff(const grammar::UnrestrictedGrammar < SymbolType > & a, const grammar::UnrestrictedGrammar < SymbolType > & b, std::ostream & out );
public:
	template<class T>
	static void diff(const T & a, const T & b, std::ostream & out );

	template < class T >
	static std::string diff ( const T & a, const T & b );
};

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::LeftLG < SymbolType > & a, const grammar::LeftLG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::LeftRG < SymbolType > & a, const grammar::LeftRG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::RightLG < SymbolType > & a, const grammar::RightLG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::RightRG < SymbolType > & a, const grammar::RightRG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::LG < SymbolType > & a, const grammar::LG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::CFG < SymbolType > & a, const grammar::CFG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::EpsilonFreeCFG < SymbolType > & a, const grammar::EpsilonFreeCFG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::CNF < SymbolType > & a, const grammar::CNF < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::GNF < SymbolType > & a, const grammar::GNF < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::CSG < SymbolType > & a, const grammar::CSG < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::NonContractingGrammar < SymbolType > & a, const grammar::NonContractingGrammar < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::ContextPreservingUnrestrictedGrammar < SymbolType > & a, const grammar::ContextPreservingUnrestrictedGrammar < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class SymbolType >
void GrammarDiff::printDiff(const grammar::UnrestrictedGrammar < SymbolType > & a, const grammar::UnrestrictedGrammar < SymbolType > & b, std::ostream & out ) {
	out << "GrammarsComparer" << std::endl;

	if(a.getNonterminalAlphabet() != b.getNonterminalAlphabet()) {
		out << "Nonterminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getNonterminalAlphabet(), b.getNonterminalAlphabet());
	}

	if(a.getRules() != b.getRules()){
		out << "Rules" << std::endl;

		DiffAux::mapDiff ( out, a.getRules(), b.getRules());
	}

	if(a.getInitialSymbol() != b.getInitialSymbol()){
		out << "Initial symbol" << std::endl;

		out << "< " << a.getInitialSymbol() << std::endl;
		out << "---" << std::endl;
		out << "> " << b.getInitialSymbol() << std::endl;
	}

	if(a.getTerminalAlphabet() != b.getTerminalAlphabet()) {
		out << "Terminal alphabet" << std::endl;

		DiffAux::setDiff ( out, a.getTerminalAlphabet(), b.getTerminalAlphabet());
	}
}

template < class T >
void GrammarDiff::diff(const T & a, const T & b, std::ostream & out ) {
	if(!GrammarCompare::compare(a, b)) {
	  GrammarDiff::printDiff ( a, b, out );
	}
}

template < class T >
std::string GrammarDiff::diff ( const T & a, const T & b ) {
	std::stringstream ss;
	diff ( a, b, ss );
	return ss.str ( );
}

} /* namespace compare */

