/*****************************************************************//**
 * \file   PricingSubProblem.h
 * \brief  The generic interface for pricing subproblem that inherent classes should override to fit with the branch and price algorithm.
 *
 * \author talmefta
 * \date   May 2022
 *********************************************************************/
#ifndef PRICINGSUBPROBLEM_H
#define PRICINGSUBPROBLEM_H

#include "Header.h"
#include "PricingSubProblemAlgorithm.h"
#include "BaseComponent.h"
#include "Constraint.h"

template<class Instance, class ColumnsType, class DualVariables, class ConstraintType>
class PricingSubProblem: public BaseComponent {

protected:
	Instance&																 instance;	/**< The studied instance.*/
	ColumnsType																  columns;  /**< Solution where to store the output of the pricing algorithm.*/
	ColumnsType*												 existing_rmp_columns;  /**< Solution where to store the output of the pricing algorithm.*/
	DualVariables*												   rmp_dual_variables;  /**< The dual variables value in the restricted master problem.*/
	PricingSubProblemAlgorithm<Instance, ColumnsType,
		DualVariables, ConstraintType>*										algorithm;  /**< The pricing method can be heuristic, metaheuristic, exact method...*/
	string																	  request;
	vector<Constraint<ConstraintType>*>*						branching_constraints;
	vector<Constraint<ConstraintType>*>*						cut_constraints;
	double																   value;
	double																   time_spent;
	string 																   name_algo;
	bool																	is_early_terminated;

public:
	/**
	 * Constructor with the reference to the studied instance and a pointer to the head of chain of responsibility algorithm.
	 *
	 * \param instance: Instance&
	 * \param algorithm: PricingSubProblemAlgorithm<PricingSubProblem<Instance, ColumnsType, DualVariables>>*
	 */
	PricingSubProblem(Instance& instance): instance(instance), rmp_dual_variables(nullptr), columns(), algorithm(algorithm), branching_constraints(nullptr),
		cut_constraints(nullptr), time_spent() {
	}
	/**
	 * Destructor.
	 *
	 */
	virtual ~PricingSubProblem() {
	}

	/**
	 * Get method of instance.
	 * 
	 * \return instance: Instance&
	 */
	Instance& get_instance() {
		return this->instance;
	}

	/**
	 * Get method of rmp_dual_variables.
	 *
	 * \return rmp_dual_variables: DualVariables*
	 */
	DualVariables* get_rmp_dual_variables() {
		return this->rmp_dual_variables;
	}

	/**
	 * Get method of columns.
	 *
	 * \return columns: ColumnsType&
	 */
	ColumnsType& get_columns() {
		return this->columns;
	}

	/**
	 * Get method of existing_rmp_columns.
	 *
	 * \return existing_rmp_columns: ColumnsType&
	 */
	ColumnsType* get_existing_rmp_columns() {
		return this->existing_rmp_columns;
	}

	/**
	 * Get method of existing_rmp_columns.
	 *
	 * \return existing_rmp_columns: ColumnsType&
	 */
	vector<Constraint<ConstraintType>*>* get_branching_constraints() {
		return this->branching_constraints;
	}

	/**
	 * Get method of existing_rmp_columns.
	 *
	 * \return existing_rmp_columns: ColumnsType&
	 */
	vector<Constraint<ConstraintType>*>* get_cut_constraints() {
		return this->cut_constraints;
	}

	/**
	 * return true if the pricing is solved exactly, false otherwise
	 *
	 */
	bool is_algorithm_exact() const {
		return (this->algorithm->get_name() == "EXACTGENERATOR");
	}

	const string& get_name_algorithm() const {
		return this->name_algo;
	}
	
	const double& get_time_spent() const {
		return this->time_spent;
	}

	const double& get_value() const {
		return this->value;
	}

	const bool& get_is_early_terminated() const {
		return this->is_early_terminated;
	}

	/**
	 * Set method of request.
	 * 
	 * \param new_request: const string&
	 */
	void set_request(const string& new_request) {
		this->request = new_request;
	}

	/**
	 * Set method of rmp_dual_variables.
	 * 
	 * \param dual_variables: DualVariables*
	 */
	void set_rmp_dual_variables(DualVariables* dual_variables) {
		this->rmp_dual_variables = dual_variables;
	}

	/**
	 * Set method of rmp_dual_variables.
	 *
	 * \param dual_variables: DualVariables*
	 */
	void set_branching_constraints(vector<Constraint<ConstraintType>*>* new_branching_constraints) {
		this->branching_constraints = new_branching_constraints;
	}

	/**
	 * Set method of rmp_dual_variables.
	 *
	 * \param dual_variables: DualVariables*
	 */
	void set_cut_constraints(vector<Constraint<ConstraintType>*>* new_cut_constraints) {
		this->cut_constraints = new_cut_constraints;
	}

	/**
	 * Set method of existing_rmp_columns.
	 *
	 * \param rmp_columns: ColumnsType*
	 */
	void set_existing_rmp_columns(ColumnsType* rmp_columns) {
		this->existing_rmp_columns = rmp_columns;
	}

	/**
	 * Set method of algorithm.
	 *
	 * \param new_algorithm: PricingSubProblemAlgorithm<Instance, ColumnsType, DualVariables>*
	 */
	void set_algorithm(PricingSubProblemAlgorithm<Instance, ColumnsType, DualVariables, ConstraintType>* new_algorithm) {
		this->algorithm = new_algorithm;
	}

	/**
	 * Solves the pricing subproblem with the corresponding algorithm to the request.
	 * 
	 */
	void solve(const double time_limit) {
		clock_t start = clock();
		this->algorithm->set_start_time(start);
		this->columns = this->algorithm->solve(this->request, time_limit);
		clock_t end = clock();
		this->time_spent = (float)(end - start) / CLOCKS_PER_SEC;
		this->value = this->algorithm->get_value();
		this->is_early_terminated = this->algorithm->get_is_early_terminated();

		PricingSubProblemAlgorithm<Instance, ColumnsType,
			DualVariables, ConstraintType>* algo = this->algorithm;
		this->name_algo = "NONE"; //By default, the pricer is none
		while (algo != nullptr)
		{
			if (!algo->has_failed_to_find_favorably_priced_columns()) {
				this->name_algo = algo->get_name();
				break;
			}
			else {
				algo = algo->get_next_algorithm();
			}
		}
	}

	/**
	 * Notifies the mediator after solving the problem and communicates the colmuns.
	 * 
	 */
	void notify_mediator() const {
		this->mediator->notify((BaseComponent*)(this), "NEWCOLUMNS");
	}


	/**
	 * Checks if the algorithm found negatively or positively columns depending on the nature of the pricing subproblem minimization or maximization.
	 */
	bool at_least_one_favorably_priced_column_exists() const {
		return !this->algorithm->has_failed_to_find_favorably_priced_columns();
	}
};


#endif // !PRICINGSUBPROBLEM_H

//template<class Instance, class ColumnsType, class DualVariables>
//inline PricingSubProblem<Instance, ColumnsType, DualVariables>::PricingSubProblem(Instance& instance):
//	instance(instance), rmp_dual_variables(nullptr), columns(), algorithm(algorithm)
//{
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline PricingSubProblem<Instance, ColumnsType, DualVariables>::~PricingSubProblem()
//{
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline Instance& PricingSubProblem<Instance, ColumnsType, DualVariables>::get_instance()
//{
//	return instance;
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline void PricingSubProblem<Instance, ColumnsType, DualVariables>::set_request(const string& request)
//{
//	this->request = request;
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline DualVariables* PricingSubProblem<Instance, ColumnsType, DualVariables>::get_rmp_dual_variables()
//{
//	return rmp_dual_variables;
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline void PricingSubProblem<Instance, ColumnsType, DualVariables>::set_rmp_dual_vriables(DualVariables* dual_variables)
//{
//	rmp_dual_variables = dual_variables;
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline void PricingSubProblem<Instance, ColumnsType, DualVariables>::solve()
//{
//	columns = algorithm->solve(request);
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline void PricingSubProblem<Instance, ColumnsType, DualVariables>::notify_mediator() const
//{
//	mediator->notify(this, "NEWCOLUMNS", &columns);
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline bool PricingSubProblem<Instance, ColumnsType, DualVariables>::at_least_one_favorably_priced_column_exists() const
//{
//	return !algorithm->has_failed_to_find_favorably_priced_columns();
//}
//
//template<class Instance, class ColumnsType, class DualVariables>
//inline void PricingSubProblem<Instance, ColumnsType, DualVariables>::set_algorithm(PricingSubProblemAlgorithm<Instance, ColumnsType, DualVariables>* new_algorithm)
//{
//	algorithm = new_algorithm;
//}