/*****************************************************************//**
 * \file   RestrictedMasterProblem.h
 * \brief  Our restricted master problem with overlapping group testing.
 * 
 * \author talmefta
 * \date   June 2022
 *********************************************************************/
#ifndef MASTERMODEL_H
#define MASTERMODEL_H

#include "RestrictedMasterProblemInterface.h"
#include "Instance.h"
#include "DualSolution.h"
#include "Exchange.h"
#include "Constraint.h"
#include "ArcBranchConstraint.h"
#include "VisitBranchConstraint.h"
#include "SubsetRowConstraint.h"

/**
 * \class RestrictedMasterProblem.
 * \brief  Our restricted master problem with overlapping group testing.
 */
class RestrictedMasterProblem: public RestrictedMasterProblemInterface<Instance, vector<double>, DualSolution, vector<Exchange>, GRBConstr> {

protected:

	int						number_columns;		/**< Number of groups we have (K).*/


	/**********************************************
	**					Variables
	***********************************************/
	GRBEnv* env;	/**< Gurobi environment.*/
	GRBModel* model;	/**< Gurobi model.*/
	GRBLinExpr objective;	/**< Gurobi expression of objective.*/

	vector<GRBVar> lambda;	/**< one variable per exchange.*/

	/**********************************************
	**					Constraints
	***********************************************/

	vector<GRBConstr> packingConstraints;
	GRBConstr upperBoundExchangeNumber;
	double upperBoundExchanges;

	/**********************************************
	**					Solution Related to the model
	***********************************************/
	vector<double> dualSolutionPi;
	vector<double> dualSolutionGamma;
	double dualSolutionBeta;
	vector<double> dualSolutionSigma;


public:
	/**
	 * Constructor with the instance.
	 *
	 * \param instance: Instance&
	 * \param initializer: RestrictedMasterProblemInitializer*
	 */
	RestrictedMasterProblem(Instance& instance);

	/**
	 * Destructor.
	 *
	 */
	~RestrictedMasterProblem();


	/**
	 * Get-method of y_k.
	 *
	 * \return y_k : const vector<GRBVar>&.
	 */
	const vector<GRBVar>& get_variables() const {
		return this->lambda;
	}

	const double& getUpperBoundExchanges() const {
		return this->upperBoundExchanges;
	}

	const int& get_number_columns() const {
		return this->number_columns;
	}


	/**
	 * construct the model with the adequate variables, constraints and objective function. Has to be override.
	 *
	 */
	 void construct() override;

	/**
	 * Solves the model with the adequate parameters. Has to be override.
	 *
	 */
	 void solve() override;
	 void call_restricted_master_heuristic_new_MIP(const double& timeLimit, const unordered_set<int>& warmStart, const bool& fix_variables = false) override;
	 string to_string_integer_solution() override;
	 double compute_reduced_cost(const Exchange& e);

	 void update_integer_solution() override;

	 void sanity_check_integer_solution() const override;

	 double compute_lagrangian_bound_RMP_obj_term() const override;

	 bool is_infeasible_dummy_variable() const override;

	/**
	 * Extracts the solution values. Has to be override.
	 *
	 */
	 void extract_solution_value() override;


	/**
	 * Extracts the dual solution values. Has to be override.
	 *
	 */
	 void extract_dual_solution_value() override;


	/**
	 * Writes the solution values on the screen or whatever wanted method. Has to be override.
	 *
	 */
	 void write_solution_value() override;
	 string write_solution_value_in_string() override;
	 void write_solution_value_into_text(const int& file_name);
	 void write_solution_info_into_text(const int& file_name);

	/**
	 * Extracts the dual solution values on the screen or whatever wanted method. Has to be override.
	 *
	 */
	 void write_dual_solution_value() override;

	/**
	 * Inserts new columns provided to nurture the restricted model.
	 *
	 * \param new_columns: const vector<Group>*
	 */
	 void insert_columns(const vector<Exchange>& new_columns) override;

	 /**
	 * Inserts new column provided to nurture the restricted model.
	 * \details It will nt add the column if it already exists in the columns pool!
	 *
	 * \param new_column: const Group&
	 */
	 void insert_column(const Exchange& column);

	 bool check_solution() const;
	 bool check_opt_sol_violation() const;

	 /**
	 * Checks if all variables are binary.
	 *
	 * \return true if it is correct, false otherwise.
	 */
	 bool are_all_variables_binary() const override;

	 /**
	 * Set warm start for restricted master heuristic.
	 *
	 */
	 void set_warm_start(const unordered_set<int>& warmStart);
	 int get_model_status() const;
	 double get_model_MIP_gap() const;

	 /**
	 * Checks if the problem after solving is feasible.
	 *
	 * \return true if it is feasible, false otherwise.
	 */
	 bool is_infeasible() const override;
	 bool is_feasible_MIP() const override;

	 /**
	 * Initialize the restricted master problem with a set of feasible columns depending on the strategy of initialization.
	 *
	 */
	 void initialize() override;

	 /**
	  * Creates, defines and adds to the model the variables lambda being continuous.
	 *
	 */
	 void addLambdaVariables();


	 /**
	  * Constructs the covering constraints that imposes each subject to be in two different groups.
	  */
	 void addPackingConstraints();
	 void addUpperBoundExchangeNumber();

	 /**
	  * Constructs the objective function that is the linear combination of expected false negatives and expected false positives..
	  */
	 void objective_function();

	 /**
	  * Adds new branching constraint to the model.
	  *
	  * \param new_branching_constraint: Constraint<GRBConstr>&
	  */
	 void add_branching_constraint(Constraint<GRBConstr>* new_branching_constraint, const int& pos);

	 /**
	 * Adds new branching constraint to the model.
	 *
	 * \param new_branching_constraints: vector<Constraint<GRBConstr>>&
	 */
	 void add_branching_constraints(vector<Constraint<GRBConstr>*>& new_branching_constraints);

	 /**
	  * Adds new cut constraint to the model.
	  *
	  * \param new_cut_constraint: Constraint<GRBConstr>*
	  */
	 void add_cut_constraint(Constraint<GRBConstr>* new_cut_constraint, const int& pos, const bool& isAddedInList);

	 /**
	 * Adds new cut constraints to the model.
	 *
	 * \param new_cut_constraints: vector<Constraint<GRBConstr>*>&
	 */
	 void add_cut_constraints(vector<Constraint<GRBConstr>*>& new_cut_constraints);
	 
	 /**
	 * Adds new ineffective cut constraint to the list of ineffective cut connstraints.
	 *
	 * \param new_ineffective_cut_constraint: Constraint<GRBConstr>*
	 */
	 void add_ineffective_cut_constraint(Constraint<GRBConstr>* new_ineffective_cut_constraint);

	 /**
	* Removes new ineffective cut constraint from the model.
	*
	* \param new_ineffective_cut_constraint: Constraint<GRBConstr>*
	*/
	 void remove_from_model_ineffective_cut_constraint(Constraint<GRBConstr>* new_ineffective_cut_constraint);

	 /**
	   * Removes new ineffective cut constraint from the model.
	   *
	   * \param new_ineffective_cut_constraint: Constraint<GRBConstr>*
	*/
	 void remove_from_cut_constraints_ineffective_cut_constraint(Constraint<GRBConstr>* new_ineffective_cut_constraint);


	 /**
	  * \brief Removes branching constraint from the list of branching constraints.
	  *
	  * \param branching_constraint: Constraint<GRBConstr>&
	  */
	 void remove_branching_constraints();

	 /**
	  * \brief Removes cut constraint from the list of cut constraints.
	  *
	  * \param cut_constraint: Constraint<GRBConstr>&
	  */
	 void remove_cut_constraints();
	 /**
	  * \brief Removes cut constraint from the model, but not from the list. This method is used only in the restricted primal heuristic
	  *
	  * \param cut_constraint: Constraint<GRBConstr>&
	  */
	 void remove_cut_constraints_from_model();
	 void add_cut_constraints_in_model();

	 /**
	  * \brief Frees memory allocated by env and model.
	  *
	  */
	 void free_memory();

	 /**
	  * Exports model to lp file.
	  * 
	  */
	 void export_model();
	 void export_model(const int& id);

	 void change_variable_type_to_binary() override;

	 void change_variable_type_to_continuous() override;

	 //OBSERVER design pattern methods
	 void attach(RMPNodeObserver* observer) override;

	 void detach(RMPNodeObserver* observer) override;

	 //void notify_nodes_arc_branching_constraints(const vector<pair<int, pair< int, int>>>& new_column_indexes_and_involved_items) override;
	
	 void notify_nodes_clique_inequalities(const vector<int>& new_column_indexes) override;
	
	 void notify_nodes_for_ineffective_cuts() override;

	 void notify_BaP_for_new_incumbent() override;

	 void update_ineffective_cuts();

	 void attach(RMPCutInformationObserver* observer) override;

	 void detach(RMPCutInformationObserver* observer) override;

	 void attach(RMPBaPObserver* observer) override;

	 void detach_BaP_observer() override;

	 void notify_cut_information_observer() override;

	 bool detect_conflicting_branching_rules() const override;

	 void fake_solution();

};

#endif //MASTERMODEL_H