#ifndef LIBWCCL_OPS_CONDITIONAL_H
#define LIBWCCL_OPS_CONDITIONAL_H

#include <boost/shared_ptr.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/count.hpp>
#include <libwccl/ops/predicate.h>
#include <libwccl/ops/formatters.h>
#include <libwccl/ops/constant.h>

namespace Wccl {

/**
 * Template class for conditional operators, returning value
 * depending on evaluation of some predicate.
 * This class is targeted towards if..then..else expression
 */
template<class T>
class Conditional : public Function<T> {
public:
	typedef boost::shared_ptr<Function<T> > ArgFunctionPtr;
	typedef boost::shared_ptr<Function<Bool> > BoolFunctionPtr;
	
	Conditional(
		const BoolFunctionPtr& cond_expr,
		const ArgFunctionPtr& iftrue_expr,
		const ArgFunctionPtr& iffalse_expr = Default())
		: cond_expr_(cond_expr), iftrue_expr_(iftrue_expr), iffalse_expr_(iffalse_expr)
	{
		BOOST_ASSERT(cond_expr_);
		BOOST_ASSERT(iftrue_expr_);
		BOOST_ASSERT(iffalse_expr_);
	}

	/**
	 * String representation of conditional operator in form of:
	 * "if cond_expr_string then iftrue_expr_string else iffalse_expr_string"
	 */
	virtual std::string to_string(const Corpus2::Tagset& tagset) const {
		std::string s(this->operator_name(tagset));
		s.append(" ");
		s.append(cond_expr_->to_string(tagset));
		s.append(" then ");
		s.append(iftrue_expr_->to_string(tagset));
		s.append(" else ");
		s.append(iffalse_expr_->to_string(tagset));
		return s;
	}

	/**
	 * String representation of conditional operator in form of:
	 * "if cond_expr_raw_s then iftrue_expr_raw_s else iffalse_expr_raw_s"
	 * This version does not require tagset, but may be inclomplete
	 * and/or contain internal info.
	 */
	virtual std::string to_raw_string() const {
		std::string s(this->raw_operator_name());
		s.append(" ");
		s.append(cond_expr_->to_raw_string());
		s.append(" then ");
		s.append(iftrue_expr_->to_raw_string());
		s.append(" else ");
		s.append(iffalse_expr_->to_raw_string());
		return s;
	}

	virtual const std::string raw_operator_name() const {
		return "if";
	}

protected:
	const BoolFunctionPtr cond_expr_;
	const ArgFunctionPtr iftrue_expr_;
	const ArgFunctionPtr iffalse_expr_;

	static const ArgFunctionPtr& Default() {
		static ArgFunctionPtr x(new Constant<T>(T()));
		return x;
	}

	typedef FunctionBase::BaseRetValPtr BaseRetValPtr;

	/**
	 * Evaluate the predicate. If it is true, evaluate and return value of
	 * iftrue_expression. If predicate is false, evalute and return value
	 * of iffalse_expression.
	 */
	virtual BaseRetValPtr apply_internal(const SentenceContext& context) const {
		if(this->cond_expr_->apply(context)->get_value()) {
			return iftrue_expr_->apply(context);
		}
		return iffalse_expr_->apply(context);
	}
};

/**
 * Template class for conditional operator targeted
 * towards the operator "? if_true_value ? predicate"
 * Difference between base Conditional<T> is that
 * the if_false_value is always default,
 * and string representation is different.
 */
template<class T>
class ConditionalOp : public Conditional<T> {
public:
	typedef typename Conditional<T>::ArgFunctionPtr ArgFunctionPtr;
	typedef boost::shared_ptr<Function<Bool> > BoolFunctionPtr;

	ConditionalOp(
		const BoolFunctionPtr& cond_expr,
		const ArgFunctionPtr& iftrue_expr)
		: Conditional<T>(cond_expr, iftrue_expr)
	{
	}

	/**
	 * String representation of conditional operator in form of:
	 * "? if_true_expr_string ? cond_expr_string"
	 */
	virtual std::string to_string(const Corpus2::Tagset& tagset) const {
		std::string s(this->operator_name(tagset));
		s.append(" ");
		s.append(this->iftrue_expr_->to_string(tagset));
		s.append(" ? ");
		s.append(this->cond_expr_->to_string(tagset));
		return s;
	}

	/**
	 * String representation of conditional operator in form of:
	 * "? if_true_expr_raw_string ? cond_expr_raw_string"
	 * This version does not require tagset, but may be inclomplete
	 * and/or contain internal info.
	 */
	virtual std::string to_raw_string() const {
		std::string s(this->raw_operator_name());
		s.append(" ");
		s.append(this->iftrue_expr_->to_raw_string());
		s.append(" ? ");
		s.append(this->cond_expr_->to_raw_string());
		return s;
	}

	virtual const std::string raw_operator_name() const {
		return "?";
	}
};


} /* end ns Wccl */

#endif // LIBWCCL_OPS_CONDITIONAL_H