/*
 * ContainerRegistry.hpp
 *
 *  Created on: 11. 7. 2017
 *	  Author: Jan Travnicek
 */

#ifndef _CONTAINER_REGISTRY_HPP_
#define _CONTAINER_REGISTRY_HPP_

#include <alib/memory>
#include <alib/list>
#include <alib/string>
#include <alib/set>
#include <alib/map>
#include <alib/typeinfo>

#include <exception>
#include <abstraction/OperationAbstraction.hpp>

namespace abstraction {

class ContainerRegistry {
public:
	class Entry {
	public:
		virtual std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( ) const = 0;

		virtual ~Entry ( ) = default;
	};

private:
	template < class Params >
	class SetEntryImpl : public Entry {
	public:
		std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( ) const override;
	};

	static ext::map < std::string, ext::list < ext::pair < std::string, std::unique_ptr < Entry > > > > & getEntries ( );

public:
	static void unregisterSet ( const std::string & param );

	template < class ParamTypes >
	static void unregisterSet ( ) {
		std::string param = ext::to_string < typename std::decay < ParamTypes >::type > ( );
		unregisterSet ( param );
	}

	static void registerSet ( std::string param, std::unique_ptr < Entry > entry );

	template < class ParamTypes >
	static void registerSet ( std::string param ) {
		registerSet ( std::move ( param ), std::make_unique < SetEntryImpl < ParamTypes > > ( ) );
	}

	template < class ParamTypes >
	static void registerSet ( ) {
		std::string param = ext::to_string < typename std::decay < ParamTypes >::type > ( );
		registerSet < ParamTypes > ( std::move ( param ) );
	}

	static bool hasAbstraction ( const std::string & container );

	static std::shared_ptr < abstraction::OperationAbstraction > getAbstraction ( const std::string & container, const std::string & param );

	static ext::set < std::string > listOverloads ( const std::string & container );

	static ext::set < std::string > list ( );
};

} /* namespace abstraction */

#include <abstraction/SetAbstraction.hpp>

namespace abstraction {

template < class Param >
std::shared_ptr < abstraction::OperationAbstraction > ContainerRegistry::SetEntryImpl < Param >::getAbstraction ( ) const {
	return std::make_shared < abstraction::SetAbstraction < Param > > ( );
}

} /* namespace abstraction */

#endif /* _CONTAINER_REGISTRY_HPP_ */
