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

#ifndef _ALGORITHM_REGISTRY_INFO_HPP_
#define _ALGORITHM_REGISTRY_INFO_HPP_

#include <alib/vector>
#include <alib/set>
#include <alib/pair>
#include <alib/typeinfo>
#include <alib/tuple>

#include <common/ParamQualifiers.hpp>
#include <common/AlgorithmCategories.hpp>

namespace abstraction {

class AlgorithmBaseInfo {
	AlgorithmCategories::AlgorithmCategory m_category;

	ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > m_params;

protected:
	template < class ... ParamTypes >
	static ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > convertTypes ( ) {
		ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params { convertType < ParamTypes > ( ) ... };

		return params;
	}

	template < class ReturnType >
	static ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > convertType ( ) {
		return ext::make_pair ( ext::to_string < typename std::decay < ReturnType >::type > ( ), abstraction::ParamQualifiers::paramQualifiers < ReturnType > ( ) );
	}

public:
	AlgorithmBaseInfo ( AlgorithmCategories::AlgorithmCategory category, ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params ) : m_category ( category ), m_params ( std::move ( params ) ) {
	}

	AlgorithmCategories::AlgorithmCategory getCategory ( ) const {
		return m_category;
	}

	const ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > & getParams ( ) const {
		return m_params;
	}

	template < class ObjectType, class ... ParamTypes >
	static AlgorithmBaseInfo methodEntryInfo ( ) {
		AlgorithmCategories::AlgorithmCategory category = AlgorithmCategories::AlgorithmCategory::DEFAULT;

		ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params = convertTypes < ParamTypes ... > ( );
		params.insert ( params.begin ( ), convertType < ObjectType & > ( ) );

		return AlgorithmBaseInfo ( category, std::move ( params ) );
	}

	template < class ... ParamTypes >
	static AlgorithmBaseInfo algorithmEntryInfo ( AlgorithmCategories::AlgorithmCategory category ) {
		ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params = convertTypes < ParamTypes ... > ( );

		return AlgorithmBaseInfo ( category, std::move ( params ) );
	}

	template < class ... ParamTypes >
	static AlgorithmBaseInfo wrapperEntryInfo ( ) {
		AlgorithmCategories::AlgorithmCategory category = AlgorithmCategories::AlgorithmCategory::DEFAULT;

		ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > > params = convertTypes < ParamTypes ... > ( );

		return AlgorithmBaseInfo ( category, std::move ( params ) );
	}
};

class AlgorithmFullInfo : public AlgorithmBaseInfo {
	ext::vector < std::string > m_paramNames;

	ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > m_result;

public:
	AlgorithmFullInfo ( AlgorithmBaseInfo baseInfo, ext::vector < std::string > paramNames, ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > result ) : AlgorithmBaseInfo ( std::move ( baseInfo ) ), m_paramNames ( std::move ( paramNames ) ), m_result ( std::move ( result ) ) {
	}

	const ext::vector < std::string > & getParamNames ( ) const {
		return m_paramNames;
	}

	const ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > & getResult ( ) const {
		return m_result;
	}

	ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > getNormalizedResult ( ) const;

	template < class ObjectType, class ReturnType, class ... ParamTypes >
	static AlgorithmFullInfo methodEntryInfo ( std::array < std::string, sizeof ... ( ParamTypes ) > paramNames ) {
		ext::vector < std::string > parameterNames;
		parameterNames.insert ( parameterNames.end ( ), "object" );
		parameterNames.insert ( parameterNames.end ( ), paramNames.begin ( ), paramNames.end ( ) );

		ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > result = convertType < ReturnType > ( );

		return AlgorithmFullInfo ( AlgorithmBaseInfo::methodEntryInfo < ObjectType, ParamTypes ... > ( ), std::move ( parameterNames ), std::move ( result ) );
	}

	template < class ReturnType, class ... ParamTypes >
	static AlgorithmFullInfo algorithmEntryInfo ( AlgorithmCategories::AlgorithmCategory category, std::array < std::string, sizeof ... ( ParamTypes ) > paramNames ) {
		ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > result = convertType < ReturnType > ( );

		ext::vector < std::string > parameterNames ( paramNames.begin ( ), paramNames.end ( ) );

		return AlgorithmFullInfo ( AlgorithmBaseInfo::algorithmEntryInfo < ParamTypes ... > ( category ), std::move ( parameterNames ), std::move ( result ) );
	}

	template < class ReturnType, class ... ParamTypes >
	static AlgorithmFullInfo wrapperEntryInfo ( std::array < std::string, sizeof ... ( ParamTypes ) > paramNames ) {
		ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > > result = convertType < ReturnType > ( );

		ext::vector < std::string > parameterNames ( paramNames.begin ( ), paramNames.end ( ) );

		return AlgorithmFullInfo ( AlgorithmBaseInfo::wrapperEntryInfo < ParamTypes ... > ( ), std::move ( parameterNames ), std::move ( result ) );
	}

};

} /* namespace abstraction */

#endif /* _ALGORITHM_REGISTRY_INFO_HPP_ */
