/*
 * ContainerRegistry.cpp
 *
 *  Created on: 19. 8. 2017
 *	  Author: Jan Travnicek
 */

#include <registry/ContainerRegistry.hpp>
#include <alib/algorithm>
#include <alib/typeinfo>

namespace abstraction {

ext::map < std::string, ext::list < ext::pair < std::string, std::unique_ptr < ContainerRegistry::Entry > > > > & ContainerRegistry::getEntries ( ) {
	static ext::map < std::string, ext::list < ext::pair < std::string, std::unique_ptr < Entry > > > > containerGroups;
	return containerGroups;
}

void ContainerRegistry::unregisterSet ( const std::string & param ) {
	std::string container = "Set";

	auto & group = getEntries ( ) [ container ];
	auto iter = find_if ( group.begin ( ), group.end ( ), [ & ] ( const ext::pair < std::string, std::unique_ptr < Entry > > & entry ) {
			return entry.first == param;
			} );
	if ( iter == group.end ( ) )
		throw std::invalid_argument ( "Callback for " + param + " in contaier " + container + " not registered." );
	group.erase ( iter );
}

void ContainerRegistry::registerSet ( std::string param, std::unique_ptr < Entry > entry ) {
	std::string container = "Set";

	auto & group = getEntries ( ) [ container ];
	auto iter = find_if ( group.begin ( ), group.end ( ), [ & ] ( const ext::pair < std::string, std::unique_ptr < Entry > > & elem ) {
			return elem.first == param;
			} );
	if ( iter != group.end ( ) )
		throw std::invalid_argument ( "Callback for " + param + " in contaier " + container + " already registered." );

	group.insert ( group.end ( ), ext::make_pair ( std::move ( param ), std::move ( entry ) ) );
}

bool ContainerRegistry::hasAbstraction ( const std::string & container ) {
	return getEntries ( ).contains ( container );
}

std::shared_ptr < abstraction::OperationAbstraction > ContainerRegistry::getAbstraction ( const std::string & container, const std::string & param ) {
	auto group = getEntries ( ).find ( container );
	if ( group == getEntries ( ).end ( ) )
		throw std::invalid_argument ( "Entry " + container + " not available" );

	auto is_same_type_lambda = [ & ] ( const ext::pair < std::string, std::unique_ptr < Entry > > & entry ) {
		return ext::is_same_type ( param, ext::erase_template_info ( entry.first ) );
	};

	auto iter = std::find_if ( group->second.begin ( ), group->second.end ( ), is_same_type_lambda );
	if ( iter == group->second.end ( ) )
		throw std::invalid_argument ( "Entry for " + container + " parametrized with " + param + " not available." );

	return iter->second->getAbstraction ( );
}

ext::set < std::string > ContainerRegistry::listOverloads ( const std::string & container ) {
	auto group = getEntries ( ).find ( container );
	if ( group == getEntries ( ).end ( ) )
		throw std::invalid_argument ( "Entry " + container + " not available" );

	ext::set < std::string > res;
	for ( const ext::pair < std::string, std::unique_ptr < Entry > > & entry : group->second )
		res.insert ( entry.first );

	return res;
}

ext::set < std::string > ContainerRegistry::list ( ) {
	ext::set < std::string > res;

	for ( const std::pair < const std::string, ext::list < ext::pair < std::string, std::unique_ptr < Entry > > > > & groups : getEntries ( ) )
		res.insert ( groups.first );

	return res;
}

} /* namespace abstraction */
