// Copyright (C) 2010 Deltares
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

/**
 * @file
 * @brief xxx
 * @author Dirk Schwanenberg
 * @version 1.0
 * @date 2010, 2011
 */


#ifndef RTCTOOLSIPOPT_H
#define RTCTOOLSIPOPT_H

#define HAVE_CONFIG_H

#include "IpIpoptApplication.hpp"
#include "IpTNLP.hpp"
#include "rtcToolsRuntime.h"

using namespace Ipopt;

class rtcToolsIPOPT : public TNLP, public rtcToolsOptimizer
{
private:
	int _n;
	int _m;
	int _nnz;
	rtcToolsRuntime * tool;
	string workDir;
	bool needsInitialization;
	SmartPtr<Ipopt::IpoptApplication> app;

	clock_t t_eval_f;
	clock_t t_eval_grad_f;
	clock_t t_eval_g;
	clock_t t_eval_jac_g;

public:
	/** default constructor */
	rtcToolsIPOPT(rtcToolsRuntime * tool, string workDir);
	rtcToolsIPOPT(rtcToolsRuntime * tool, rtcRuntimeConfigSettings::IPOPT par, string workDir);

	/** default destructor */
	virtual ~rtcToolsIPOPT() {};

	/**@name Overloaded from TNLP */
	//@{
	/** Method to return some info about the nlp */
	virtual bool get_nlp_info(Index& n, Index& m, Index& nnz_jac_g,
                              Index& nnz_h_lag, IndexStyleEnum& index_style);

	/** Method to return the bounds for my problem */
	virtual bool get_bounds_info(Index n, Number* x_l, Number* x_u,
                                 Index m, Number* g_l, Number* g_u);

	/** overload this method to return any meta data for
     *  the variables and the constraints */
	virtual bool get_var_con_metadata(Index n,
		                  StringMetaDataMapType& var_string_md,
                          IntegerMetaDataMapType& var_integer_md,
                          NumericMetaDataMapType& var_numeric_md,
                          Index m,
                          StringMetaDataMapType& con_string_md,
                          IntegerMetaDataMapType& con_integer_md,
                          NumericMetaDataMapType& con_numeric_md);

	/** Method to return the starting point for the algorithm */
	virtual bool get_starting_point(Index n, bool init_x, Number* x,
                                    bool init_z, Number* z_L, Number* z_U,
                                    Index m, bool init_lambda,
                                    Number* lambda);

	/** Method to return the objective value */
	virtual bool eval_f(Index n, const Number* x, bool new_x, Number& obj_value);

	/** Method to return the gradient of the objective */
	virtual bool eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f);

	/** Method to return the constraint residuals */
	virtual bool eval_g(Index n, const Number* x, bool new_x, Index m, Number* g);

	/** Method to return:
	 *   1) The structure of the jacobian (if "values" is NULL)
	 *   2) The values of the jacobian (if "values" is not NULL)
	 */
	virtual bool eval_jac_g(Index n, const Number* x, bool new_x,
                            Index m, Index nele_jac, Index* iRow, Index *jCol,
                            Number* values);

	/** Method to return:
	 *   1) The structure of the hessian of the lagrangian (if "values" is NULL)
	 *   2) The values of the hessian of the lagrangian (if "values" is not NULL)
	 */
	virtual bool eval_h(Index n, const Number* x, bool new_x,
                        Number obj_factor, Index m, const Number* lambda,
                        bool new_lambda, Index nele_hess, Index* iRow,
                        Index* jCol, Number* values);

	bool get_scaling_parameters(Number& obj_scaling,
								bool& use_x_scaling, Index n,
								Number* x_scaling,
								bool& use_g_scaling, Index m,
								Number* g_scaling);

	virtual bool intermediate_callback(AlgorithmMode mode,
                           Index iter, Number obj_value,
                           Number inf_pr, Number inf_du,
                           Number mu, Number d_norm,
                           Number regularization_size,
                           Number alpha_du, Number alpha_pr,
                           Index ls_trials,
                           const IpoptData* ip_data,
                           IpoptCalculatedQuantities* ip_cq);

	/** This method is called when the algorithm is complete so the TNLP can store/write the solution */
	virtual void finalize_solution(SolverReturn status,
                                   Index n, const Number* x, const Number* z_L, const Number* z_U,
                                   Index m, const Number* g, const Number* lambda,
                                   Number obj_value,
								   const IpoptData* ip_data,
								   IpoptCalculatedQuantities* ip_cq);
 
	bool parCustom;
	rtcRuntimeConfigSettings::IPOPT par;

	double* x_0;

	vector<AlgorithmMode> hist_mode;
	vector<Index> hist_iter;
	vector<Number> hist_f;
	vector<Number> hist_constr_viol;
	vector<Number> hist_dual_inf;
	vector<Number> hist_mu;

    // implement rtcToolsOptimizer function Optimize
    int optimize();
	void initialize();
	void write(string filename);
};

#endif // RTCTOOLSIPOPT