/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#pragma once

#include <functional>

namespace LeechCraft
{
namespace Util
{
	/** @brief A peir of two functions, typically a continuation and an
	 * error handler.
	 *
	 * Inspired by Haskell's Data.Either, the functions contained in
	 * EitherCont are called \em left and \em right respectively.
	 *
	 * The common convention is to store the "normal" continuation in the
	 * \em right function, while the \em left one stores the error handler.
	 *
	 * @tparam LeftSig The signature of the \em left function.
	 * @tparam RightSig The signature of the \em right function.
	 */
	template<typename LeftSig, typename RightSig>
	class EitherCont
	{
		using Left_f = std::function<LeftSig>;
		using Right_f = std::function<RightSig>;
		Left_f Left_;
		Right_f Right_;
	public:
		/** @brief Default-constructs the continuation with uninitialized
		 * functions.
		 */
		EitherCont () = default;

		/** @brief Sets the left and right functions to \em l and \em r.
		 *
		 * @tparam L The type of the left functor which should be used to
		 * initialize the left function (must be convertible to
		 * \em LeftSig).
		 * @tparam R The type of the right functor which should be used to
		 * initialize the left function (must be convertible to
		 * \em LeftSig).
		 */
		template<typename L, typename R>
		EitherCont (const L& l, const R& r)
		: Left_ { l }
		, Right_ { r }
		{
		}

		/** @brief Checks if both functions are initialized.
		 *
		 * Calling any function on an object for which this function
		 * returns false leads to undefined behavior.
		 *
		 * @return Whether both function are initialized.
		 */
		explicit operator bool () const
		{
			return Left_ && Right_;
		}

		/** @brief Invoke the left function and return its result.
		 *
		 * Invokes the left function forwarding \em args of type \em Args
		 * (which may be different from the types in \em LeftSig, but
		 * convertible to them), and returns its result.
		 *
		 * @param[in] args The arguments to forward to the left function.
		 * @return The result of invoking the left function with \em args.
		 * @tparam Args The types of arguments to pass to the left
		 * function.
		 *
		 * @sa Right()
		 */
		template<typename... Args>
		typename Left_f::result_type Left (Args&&... args) const
		{
			return Left_ (std::forward<Args> (args)...);
		}

		/** @brief Invoke the right function and return its result.
		 *
		 * Invokes the right function forwarding \em args of type \em Args
		 * (which may be different from the types in \em RightSig, but
		 * convertible to them), and returns its result.
		 *
		 * @param[in] args The arguments to forward to the right function.
		 * @return The result of invoking the right function with \em args.
		 * @tparam Args The types of arguments to pass to the right
		 * function.
		 *
		 * @sa Left()
		 */
		template<typename... Args>
		typename Right_f::result_type Right (Args&&... args) const
		{
			return Right_ (std::forward<Args> (args)...);
		}
	};
}
}
