/*****************************************************************//**
 * \file   Constraint.h
 * \brief  A generic constraint interface to store the specific constraint wheither it is a branching constraint or a gut constraint.
 * 
 * \author talmefta
 * \date   June 2022
 *********************************************************************/
#ifndef CONSTRAINT_H
#define CONSTRAINT_H

#include "Header.h"

template<class Type>
class Constraint {

protected:

	string					 name;
	double					  rhs;
	string				_operator;
	Type*		  real_constraint;
	int		  ineffective_counter;
	int			 become_effective;
	int		   become_ineffective;
	int						birth;
	bool isVI;

public:
	/**
	 * Constructor by default.
	 * 
	 */
	Constraint() : name(), rhs(), _operator(), real_constraint(nullptr), ineffective_counter(), become_effective(), become_ineffective(), birth() {
		this->isVI = false;
	}


	/**
	 * Constructor with the left hand side, the right hand side and the operator of the constraint.
	 * 
	 * \param variables_indexes: vector<int>&
	 * \param rhs: double
	 * \param _operator: const string&
	 */
	Constraint(double rhs, const string& _operator, const string& name = "") :
		rhs(rhs), _operator(_operator), real_constraint(nullptr), name(name), ineffective_counter(), become_effective(), become_ineffective(), 
		birth() {
		this->isVI = false;
	}

	/**
	 * Destructor.
	 * 
	 */
	virtual ~Constraint() {
	}

	/**
	 * Get method of rhs.
	 *
	 * \return rhs: const double&
	 */
	const double get_rhs() const {
		return this->rhs;
	}

	const bool get_isVI() const {
		return this->isVI;
	}

	/**
	 * Get method of _operator.
	 *
	 * \return _operator: const string&
	 */
	const string& get_operator() const {
		return this->_operator;
	}

	/**
	 * Get method of real_constraint.
	 *
	 * \return real_constraint: const Type*
	 */
	Type* get_real_constraint() {
		return this->real_constraint;
	}

	/**
	 * Get method of name.
	 *
	 * \return name: const string&
	 */
	const string& get_name() const {
		return this->name;
	}

	/**
	 * Get method of ineffective_counter.
	 *
	 * \return ineffective_counter: const int&
	 */
	const int& get_ineffective_counter() const {
		return this->ineffective_counter;
	}
	
	/**
	 * Get method of birth.
	 *
	 * \return birth: const int&
	 */
	const int& get_birth() const {
		return this->birth;
	}

	/**
	 * Get method of become_effective.
	 *
	 * \return become_effective: const int&
	 */
	const int& get_become_effective() const {
		return this->become_effective;
	}

	/**
	 * Get method of become_ineffective.
	 *
	 * \return become_ineffective: const int&
	 */
	const int& get_become_ineffective() const {
		return this->become_ineffective;
	}

	/**
	 * Set method of real_constraint.
	 * 
	 * \param new_real_constraint: Type*
	 */
	void set_real_constraint(Type* new_real_constraint) {
		this->real_constraint = new_real_constraint;
	}

	/**
	 * Set method of ineffective_counter.
	 *
	 * \param new_ineffective_counter: const int&
	 */
	void set_ineffective_counter(const int& new_ineffective_counter) {
		this->ineffective_counter = new_ineffective_counter;
	}

	/**
	 * Set method of birth.
	 *
	 * \param new_birth: const int&
	 */
	void set_birth(const int& new_birth) {
		this->birth = new_birth;
	}

	/**
	 * Set method of become_effective.
	 *
	 * \param new_value: const int&
	 */
	void set_become_effective(const int& new_value) {
		this->become_effective = new_value;
	}


	/**
	 * Set method of become_ineffective.
	 *
	 * \param new_value: const int&
	 */
	void set_become_ineffective(const int& new_value) {
		this->become_ineffective = new_value;
	}

	/**
	 * Equality operator.
	 * 
	 * \param constraint: const Constraint&
	 * \return true if equality, false otherwise
	 */
	bool operator==(const Constraint<Type>& constraint) const {
		return this->name == constraint.name && fabs(this->rhs - constraint.rhs) < EPSILON &&
			this->_operator == constraint._operator /*&& real_constraint == constraint.real_constraint*/;
	}

	/**
	 * Equality operator.
	 *
	 * \param constraint: const Constraint&
	 * \return true if equality, false otherwise
	 */
	bool operator!=(const Constraint<Type>& constraint) const {
		return !(*this == constraint);
	}

	string to_string() const {
		stringstream mystring;
		/*for (int index = 0; index < variables_indexes.size(); index++)
			mystring << variables_indexes[index] << ((index == variables_indexes.size() - 1) ? " " : " + ");
		mystring << _operator << " " << rhs;*/
		mystring << ", Birth = " << this->birth << " becomeEff = " << this->become_effective << " becomeIneff = " << this->become_ineffective
			<< " InefCounter = " << this->ineffective_counter;
		return mystring.str();
	}
	/**
	*  \brief	 Get a string for debug output.
	*  \param    out : ostream
	*  \param    constraint : const Constraint<Type>& 
	*  \return   The stream with the message ID added : ostream
	*/
	friend ostream& operator<<(ostream& out, const Constraint<Type>& constraint) {
		out << constraint.to_string();
		return out;
	}

	Constraint<Type>& operator=(const Constraint<Type>& new_cut) {
		this->name				 = new_cut.name;
		this->rhs					 = new_cut.rhs;
		this->_operator			 = new_cut._operator;
		this->real_constraint		 = new_cut.real_constraint;
		this->ineffective_counter	 = new_cut.ineffective_counter;
		this->become_effective	 = new_cut.become_effective;
		this->become_ineffective	 = new_cut.become_ineffective;
	}
};
#endif //CONSTRAINT_H
