#ifndef PICEF_H
#define PICEF_H


#include "Header.h"
#include "Instance.h"
#include "Exchange.h"

/**
*	\class Picef.
*	\brief	Solve the KEP with Picef. Solve the KEP with weigthed uncertainty with McElfresh formulation. 
*/
class Picef {

public:

	const Instance& instance;
	const vector<Exchange>& all_cycles;
	string fileName;
	double time_limit;

	//Gurobi 
	GRBEnv* env;	/**< Gurobi environment.*/
	GRBModel* model;	/**< Gurobi model.*/

	//variables KEP
	vector<GRBVar> var_cycles;
	vector<vector<GRBVar>> var_chains_arcs;

	int ad_arcs_size;
	vector<pair<int, int>> arcs;
	vector<int> shortest_paths_from_ad;

	int status;
	int bab_nodes;
	int variable_number;
	int constraint_number;
	int transplant_number;
	double incumbent_value;
	double bound;
	double time;
	bool is_optimal;
	string csv_line;
	vector<Exchange> solution;

public:

	/**
	 * \brief Constructor with instance.
	 *
	 */
	Picef(const Instance& instance, const string& fileName, const vector<Exchange>& all_cycles, double time_limit);

	/**
	 * \brief Copy constructor.
	 *
	 */
	Picef(const Picef& p);

	/**
	 * \brief Destructor.
	 *
	 */
	~Picef();

	void solve(const double& cycle_generation_time);
	void add_variables();
	void add_objective();
	void add_constraints_kep();

	void export_model(const string& name);
	void solution_check();

	int get_next_in_chain(const Vertex& v, const int& length);

	void extract_solution_stats();
	void write_stats();

	/**
	*	\brief Getters
	*
	*/
	const int& get_status() const;
	const int& get_transplant_number() const;
	const int& get_variable_number() const;
	const int& get_constraint_number() const;
	const double& get_incumbent_value() const;
	const double& get_bound() const;
	const double& get_time() const;
	const bool& get_is_optimal() const;
	const string& get_csv_line() const;
	const vector<Exchange>& get_solution() const;

	string toString() const;

	/**
	*	\brief	Operator assignment
	*	\param new_subject : Subject
	*	\return Subject : The result of assigning all the attributs of new_Subject to the current customer.
	*/
	Picef& operator=(const Picef& p);
};

class mycallback_picef : public GRBCallback {
public:
	double& bound;
	double& value;
	int& bab_nodes;
	mycallback_picef(double& incumbent_value, double& bound, int& bab_nodes) : bound(bound), value(incumbent_value), bab_nodes(bab_nodes) {
	}
protected:
	void callback() {
		try {
			if (where == GRB_CB_MIPNODE) {
				if (getIntInfo(GRB_CB_MIPNODE_STATUS) == GRB_OPTIMAL) {
					this->bound = getDoubleInfo(GRB_CB_MIPNODE_OBJBND);
					this->value = getDoubleInfo(GRB_CB_MIPNODE_OBJBST);
					this->bab_nodes = getDoubleInfo(GRB_CB_MIPNODE_NODCNT);
				}
			}
		}
		catch (GRBException e) {

			cout << "[mycallback_picef::solve()] - Error code = " << e.getErrorCode() << endl;
			cout << e.getMessage() << endl;
		}
		catch (...) {
			cout << "[mycallback_picef::solve()] - Error during optimization" << endl;
		}
	}
};

#endif //PICEF_H