// ---------------------------------------------------------------------------------
//  choose.h:
//      Choose function main file.
//
//  2010 Written by Luis García.
//
//  History:
//    10.02.16 - First release
// ---------------------------------------------------------------------------------

#if !defined CHOOSE_H
#define CHOOSE_H

#include <functional>
#include <stdexcept>
#include <string>

namespace newcastle {

	typedef struct {} * whatever_t;

	const whatever_t whatever = 0;

	struct undefined_value
		: std::logic_error
	{
		inline explicit undefined_value(
			const char * _Message
			)
			: std::logic_error(_Message)
			{
			}

		inline explicit undefined_value(
			const std::string & _Message
			)
			: std::logic_error(_Message)
			{
			}
	};

	template <
		typename _IndexTy, typename _ResultTy
	>
	class _Choose_helper {

		template <
			typename _IndexTy, typename _ResultTy
		>
		friend _Choose_helper<_IndexTy, const _ResultTy> choose(
			const _IndexTy & _Index
			);

		template <
			typename _IndexTy, typename _ResultTy
		>
		friend _Choose_helper<_IndexTy, _ResultTy> choose_reference(
			const _IndexTy & _Index
			);

		_Choose_helper(
			const _Choose_helper & _Right
			);

		_Choose_helper operator = (
			const _Choose_helper & _Right
			);

		const _IndexTy & _index;

		_ResultTy * _result;

	public:

		inline explicit _Choose_helper(
			const _IndexTy & _Index
			)
			: _index(_Index), _result(nullptr)
			{
			}

		inline _Choose_helper & operator () (
			const _IndexTy & _Index, _ResultTy & _Value
			) {
				if (!_result && _index == _Index)
					_result = &_Value;

				return * this;
			}

		inline _Choose_helper & operator () (
			whatever_t, _ResultTy & _Value
			) {
				if (!_result)
					_result = &_Value;

				return * this;
			}

		inline _Choose_helper & operator () (
			const std::function<bool (const _IndexTy &)> & _Pred, _ResultTy & _Value
			) {
				if (!_result && _Pred(_index))
					_result = &_Value;

				return * this;
			}

		inline _ResultTy & operator () (
			) {
				if (!_result)
					throw undefined_value("no result for a choose_reference() call.");

				return * _result;
			}
	};

	template <
		typename _ResultTy, typename _IndexTy
	>
	inline _Choose_helper<_IndexTy, const _ResultTy> choose(
		const _IndexTy & _Index
		) {
			return _Choose_helper<_IndexTy, const _ResultTy>(_Index);
		}

	template <
		typename _ResultTy, typename _IndexTy
	>
	inline _Choose_helper<_IndexTy, _ResultTy> choose_reference(
		const _IndexTy & _Index
		) {
			return _Choose_helper<_IndexTy, _ResultTy>(_Index);
		}

}

#endif // !CHOOSE_H

// ---------------------------------------------------------------------------------
//  End of file
// ---------------------------------------------------------------------------------
