/*
 * StringReaderRegistry.cpp
 *
 *  Created on: 21. 8. 2017
 *	  Author: Jan Travnicek
 */

#include <registry/StringReaderRegistry.hpp>
#include <exception/CommonException.h>

namespace abstraction {

ext::map < std::string, ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < StringReaderRegistry::Entry > > > > & StringReaderRegistry::getEntries ( ) {
	static ext::map < std::string, ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < Entry > > > > readers;
	return readers;
}

void StringReaderRegistry::unregisterStringReader ( const std::string & group, ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < StringReaderRegistry::Entry > > >::const_iterator iter ) {
	auto & entry = getEntries ( ) [ group ];
	if ( ! ext::range_contains_iterator ( entry.begin ( ), entry.end ( ), iter ) )
		throw std::invalid_argument ( "Entry not found in " + group + "." );

	entry.erase ( iter );
	if ( entry.empty ( ) )
		getEntries ( ).erase ( group );
}

ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < StringReaderRegistry::Entry > > >::const_iterator StringReaderRegistry::registerStringReader ( std::string group, std::function < bool ( std::istream & ) > first, std::unique_ptr < Entry > entry ) {
	auto & collection = getEntries ( ) [ std::move ( group ) ];
	return collection.insert ( collection.end ( ), std::make_pair ( std::move ( first ), std::move ( entry ) ) );
}

std::shared_ptr < abstraction::OperationAbstraction > StringReaderRegistry::getAbstraction ( const std::string & group, const std::string & str ) {
	std::stringstream ss ( str );
	while ( ext::isspace ( ss.peek ( ) ) )
		ss.get ( );

	auto lambda = [ & ] ( const std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < Entry > > & entry ) {
		return entry.first ( ss );
	};

	const auto & entryIterator = getEntries ( ).find ( group );
	if ( entryIterator == getEntries ( ).end ( ) )
		throw exception::CommonException ( "Entry " + group + " not available." );

	const auto & entries = entryIterator->second;

	int pos = ss.tellg();

	typename ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < Entry > > >::const_iterator callback = find_if ( entries.begin ( ), entries.end ( ), lambda );
	if ( callback == entries.end ( ) )
		throw exception::CommonException ( "No callback handling input found." );

	if ( pos != ss.tellg ( ) )
		throw exception::CommonException ( "First function of registered callback moved the stream." );

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

} /* namespace abstraction */
