#include "stringApi.hpp"

namespace core {

ext::map < std::string, std::unique_ptr < stringApi < object::Object >::GroupWriter > > & stringApi < object::Object >::composeFunctions ( ) {
	static ext::map < std::string, std::unique_ptr < GroupWriter > > res;

	return res;
}

ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < stringApi < object::Object >::GroupReader > > > & stringApi < object::Object >::parseFunctions ( ) {
	static ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < GroupReader > > > res;

	return res;
}

void stringApi < object::Object >::unregisterStringReader ( ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < GroupReader > > >::const_iterator iter ) {
	auto & entry = parseFunctions ( );
	if ( ! ext::range_contains_iterator ( entry.begin ( ), entry.end ( ), iter ) )
		throw std::invalid_argument ( "Entry not found." );

	entry.erase ( iter );
}

ext::list < std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < stringApi < object::Object >::GroupReader > > >::const_iterator stringApi < object::Object >::registerStringReader ( std::function < bool ( std::istream & ) > first, std::unique_ptr < GroupReader > entry ) {
	auto & entries = parseFunctions ( );
	return entries.insert ( entries.end ( ), std::make_pair ( std::move ( first ), std::move ( entry ) ) );
}

void stringApi < object::Object >::unregisterStringWriter ( const std::string & type, const std::string & typeName ) {
	if ( ! composeFunctions ( ).erase ( type ) ) {
		std::string groupName = ext::to_string < object::Object > ( );

		throw::exception::CommonException ( "Parse callback of " + typeName + " not registered in group " + groupName + "." );
	}
}

void stringApi < object::Object >::registerStringWriter ( std::string type, const std::string & typeName, std::unique_ptr < GroupWriter > entry ) {
	bool res = composeFunctions ( ).insert ( std::make_pair ( std::move ( type ), std::move ( entry ) ) ).second;
	if ( ! res ) {
		std::string groupName = ext::to_string < object::Object > ( );

		throw::exception::CommonException ( "Parse callback of " + typeName + " already registered in group " + groupName + "." );
	}
}

object::Object stringApi < object::Object >::parse ( std::istream & input ) {
	while ( ext::isspace ( input.peek ( ) ) )
		input.get ( );

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

	int pos = input.tellg();

	auto callback = find_if ( parseFunctions ( ).begin ( ), parseFunctions ( ).end ( ), lambda );
	if ( callback == parseFunctions ( ).end ( ) )
		throw exception::CommonException ( "Parse callback not registered." );

	if ( pos != input.tellg ( ) )
		throw exception::CommonException ( "First function of registered callback moved the stream (before = " + ext::to_string ( pos ) + ", after = " + ext::to_string ( input.tellg ( ) ) + ")." );

	object::Object res = callback->second->parse ( input );

	input.clear();
	while ( input >> ext::string ( "'" ) )
		++ res;
	input.clear();

	return res;
}

bool stringApi < object::Object >::first ( std::istream & input ) {
	auto lambda = [ & ] ( const std::pair < std::function < bool ( std::istream & ) >, std::unique_ptr < GroupReader > > & entry ) {
		return entry.first ( input );
	};

	return std::any_of ( parseFunctions ( ).begin ( ), parseFunctions ( ).end ( ), lambda );
}

void stringApi < object::Object >::compose ( std::ostream & output, const object::Object & data ) {
	const auto & content = data.getData ( );
	std::string type = ext::to_string ( ext::type_index ( typeid ( content ) ) );
	auto callback = composeFunctions ( ).find ( type );

	if ( callback == composeFunctions ( ).end ( ) ) throw exception::CommonException ( "Compose callback for " + type + " tag not registered." );

	callback->second->compose ( output, data );
	for ( unsigned i = 0; i < data.getId ( ); ++ i )
		output << "'";
}

} /* namespace core */
