/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef CPLEX_HELPER_HPP_
#define CPLEX_HELPER_HPP_

#include <vector>
#include <set>
#include "single_row_cut_generator.hpp"
using std::set;
using std::vector;

struct CplexRow {
	int nz;
	double pi_zero;
	double *pi;
	int *indices;
	int depth;
	int head;
	double dynamism;

	bool operator<(const CplexRow &other) const;
	double get_violation(double *x);
	void print(double *x);
};

/**
 * This class provides useful methods for dealing with CPLEX.
 */
class CplexHelper {
private:
	const CPXENVptr env;
	const CPXLPptr lp;
	bool *is_integer;
	int n_cuts;

public:
	/**
	 * Constructs a helper associated with the provided environment and problem.
	 *
	 * @param env Pointer to the ILOG CPLEX environment.
	 * @param lp Pointer to a CPLEX LP problem object.
	 */
	CplexHelper(CPXENVptr env, CPXLPptr lp);

	~CplexHelper();

	/**
	 * Adds the specified constraints to the model.
	 *
	 * @param cuts Set of constraints to add.
	 */
	void add_cut(Constraint *cut);

	/**
	 * For each fractional row of the current tableau, adds as many single row
	 * cuts as possible. The cuts are generated by the provided generator class.
	 *
	 * @tparam Generator Class used to generate the cuts.
	 * @param max_rows The maximum number of rows to consider.
	 * @returns The number of cuts added.
	 */
	template<class Generator>
	int add_single_row_cuts(int max_rows);

	/**
	 * Gets a single row from the current tableau.
	 *
	 * @param index Index of the row to fetch.
	 * @returns The selected tableau row.
	 */
	Row* get_tableau_row(int index);

	void read_basis();
	void read_columns();

	/**
	 * Solves the current problem, and prints some solution information.
	 *
	 * @returns The solution status, as returned by CPXgetstat.
	 */
    void solve(bool save_stats = false);

	void dump_constraint(const Constraint &c, const char *msg = "");

	void print_basis();

	void flush_cuts();

	void eta_print();
	void eta_reset();

	CplexRow constraint_to_cplex_row(const Constraint &cut);

	Constraint cplex_row_to_constraint(const CplexRow &cplex_row);

	void print_solution(double *x);

	void find_good_rows(int max_rows);

	int n_rows;
	int n_cols;

	double *ub;
	double *lb;
	int *cstat;

	CplexRow *cplex_rows;

	int eta_count;
	int eta_total;
	time_t eta_start;

	set<CplexRow> cut_buffer;

	double *first_solution;
	double *current_solution;
	double *optimal_solution;

	long total_cuts;

	int current_round;

	int n_good_rows;
	int *good_rows;

	double *reduced_costs;
	double cost_cutoff;
};

#include "cplex_helper.tpp"

#endif /* CPLEX_HELPER_HPP_ */
