/*
 * FirstChildNextSibling.h
 *
 *  Created on: 3. 4. 2020
 *      Author: Jan Travnicek
 */

#ifndef _FISRT_CHILD_NEXT_SIBLING_H_
#define _FISRT_CHILD_NEXT_SIBLING_H_

#include <common/createUnique.hpp>

#include <alphabet/EndSymbol.h>
#include <common/ranked_symbol.hpp>

#include <tree/ranked/RankedTree.h>
#include <tree/unranked/UnrankedTree.h>

namespace tree {

namespace transform {

class FirstChildNextSibling {
public:
	/**
	 * Transforms a unranked tree to ranked tree by first child next sibling approach.
	 * @return the transformed tree
	 */
	template < class SymbolType >
	static tree::RankedTree < SymbolType > transform ( const tree::UnrankedTree < SymbolType > & subject );

private:
	template < class SymbolType >
	static ext::tree < common::ranked_symbol < SymbolType > > transformInternal ( const ext::vector < ext::tree < SymbolType > > & tree, const SymbolType & terminalSymbol );
};

template < class SymbolType >
ext::tree < common::ranked_symbol < SymbolType > > FirstChildNextSibling::transformInternal ( const ext::vector < ext::tree < SymbolType > > & treeList, const SymbolType & terminalSymbol ) {
	ext::tree < common::ranked_symbol < SymbolType > > root ( common::ranked_symbol ( terminalSymbol, 0 ) );
	for ( const ext::tree < SymbolType > & node : ext::make_reverse ( treeList ) ) {
		ext::tree < common::ranked_symbol < SymbolType > > newRoot ( common::ranked_symbol ( node.getData ( ), 2 ) );
		newRoot.push_back ( transformInternal ( node.getChildren ( ), terminalSymbol ) );
		newRoot.push_back ( std::move ( root ) );
		root = std::move ( newRoot );
	}
	return root;
}

template < class SymbolType >
tree::RankedTree < SymbolType > FirstChildNextSibling::transform ( const tree::UnrankedTree < SymbolType > & subject ) {
	SymbolType terminalSymbol = common::createUnique ( alphabet::EndSymbol::instance < SymbolType > ( ), subject.getAlphabet ( ) );
	common::ranked_symbol < SymbolType > terminalNode ( terminalSymbol, 0 );

	ext::set < common::ranked_symbol < SymbolType > > alphabet;
	alphabet.insert ( terminalNode );
	for ( const SymbolType & symbol : subject.getAlphabet ( ) )
		alphabet.insert ( common::ranked_symbol < SymbolType > ( symbol, 2 ) );

	ext::tree < common::ranked_symbol < SymbolType > > root ( common::ranked_symbol < SymbolType > ( subject.getContent ( ).getData ( ), 2 ) );
	root.push_back ( transformInternal ( subject.getContent ( ).getChildren ( ), terminalSymbol ) );
	root.push_back ( ext::tree < common::ranked_symbol < SymbolType > > ( terminalNode ) );
	return tree::RankedTree < SymbolType > ( std::move ( alphabet ), std::move ( root ) );
}

} /* namespace transform */

} /* namespace tree */

#endif /* _FISRT_CHILD_NEXT_SIBLING_H_ */
