//                                               -*- C++ -*-
/**
 *  @brief BonminProblem implements the interface between OT and Bonmin for
 * optimization problems. It is derived from Bonmin::TMINLP to ensure
 * compatibility with Bonmin algorithms.
 *
 *  Copyright 2005-2026 Airbus-EDF-IMACS-ONERA-Phimeca
 *
 *  This library is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This library 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#ifndef OPENTURNS_BONMINPROBLEM_HXX
#define OPENTURNS_BONMINPROBLEM_HXX

#include "openturns/OTprivate.hxx"
#include "openturns/OptimizationAlgorithmImplementation.hxx"

#define HAVE_CSTDDEF // for 3.11 in debian
#include <BonTMINLP.hpp>
#include <IpTNLP.hpp>

#include <chrono>

BEGIN_NAMESPACE_OPENTURNS

typedef ::Bonmin::TMINLP::VariableType * VariableTypeTable;
typedef ::Ipopt::TNLP::LinearityType * LinearityTypeTable;


class BonminProblem
  : public ::Bonmin::TMINLP
{
public:

  /** Constructor with parameters */
  BonminProblem( const OptimizationProblem & optimProblem,
                 const Point & startingPoint,
                 const UnsignedInteger maximumCallsNumber,
                 const Scalar maximumTimeDuration,
                 const std::chrono::steady_clock::time_point & t0);

  /** Retrieving objective function input.output history */
  Sample getInputHistory() const;
  Sample getOutputHistory() const;

  /** Overloading functions from Bonmin::TMINLP
   See BonTMINLP.hpp for description */

  // n: number of variables
  // m: number of constraints

  bool get_nlp_info(int & n,
                    int & m,
                    int & nnz_jac_g, // Number of non-zero components in the Jacobian of g
                    int & nnz_h_lag, // Number of non-zero components in Hessian of Lagrangean
                    ::Ipopt::TNLP::IndexStyleEnum & index_style) override;

  bool get_variables_types( int n,
                            VariableTypeTable var_types) override;

  bool get_variables_linearity( int n,
                                LinearityTypeTable var_types) override;

  bool get_constraints_linearity( int m,
                                  LinearityTypeTable const_types) override;

  bool get_bounds_info( int n,
                        double* x_l,  // Lower bounds
                        double* x_u,  // Upper bounds
                        int m,
                        double* g_l,  // Lower bounds
                        double* g_u) override; // Upper bounds

  bool get_starting_point(int n,
                          bool init_x,
                          double* x,
                          bool init_z,
                          double* z_L,
                          double* z_U,
                          int m,
                          bool init_lambda,
                          double* lambda) override;

  bool eval_f(int n,
              const double* x,
              bool new_x,
              double& obj_value) override;

  bool eval_grad_f( int n,
                    const double* x,
                    bool new_x,
                    double* grad_f) override;

  bool eval_g(int n,
              const double* x,
              bool new_x,
              int m,
              double* g) override;

  bool eval_jac_g(int n,
                  const double* x,
                  bool new_x,
                  int m,
                  int nele_jac,
                  int* iRow,
                  int *jCol,
                  double* values) override;

  bool eval_h(int n,
              const double* x,
              bool new_x,
              double obj_factor,
              int m,
              const double* lambda,
              bool new_lambda,
              int nele_hess,
              int* iRow,
              int* jCol,
              double* values) override;

  bool eval_gi(int n,
               const double* x,
               bool new_x,
               int i,
               double& gi) override;

  bool eval_grad_gi(int n,
                    const double* x,
                    bool new_x,
                    int i,
                    int& nele_grad_gi,
                    int* jCol,
                    double* values) override;

  void finalize_solution( ::Bonmin::TMINLP::SolverReturn status,
                          ::Ipopt::Index n,
                          const ::Ipopt::Number* x,
                          ::Ipopt::Number obj_value) override;

  const ::Bonmin::TMINLP::BranchingInfo * branchingInfo() const override
  {
    return NULL;
  };

  const ::Bonmin::TMINLP::SosInfo * sosConstraints() const override
  {
    return NULL;
  };

  ::Bonmin::TMINLP::SolverReturn getStatus() const
  {
    return status_;
  }

  Bool getTimeOut() const
  {
    return timeOut_;
  }

  virtual void setProgressCallback(OptimizationAlgorithmImplementation::ProgressCallback callBack, void * state = nullptr)
  {
    progressCallback_ = std::pair<OptimizationAlgorithmImplementation::ProgressCallback, void *>(callBack, state);
  }

  virtual void setStopCallback(OptimizationAlgorithmImplementation::StopCallback callBack, void * state = nullptr)
  {
    stopCallback_ = std::pair<OptimizationAlgorithmImplementation::StopCallback, void *>(callBack, state);
  }


private:
  // Clip point wrt problem bounds
  void clip(Point & xPoint) const;

  const OptimizationProblem optimProblem_;
  const Point startingPoint_;
  Sample evaluationInputHistory_;
  Sample evaluationOutputHistory_;
  ::Bonmin::TMINLP::SolverReturn status_;
  // Callbacks
  UnsignedInteger maximumCallsNumber_ = 0;
  Scalar maximumTimeDuration_ = 0.0;
  Bool timeOut_ = false;
  std::chrono::steady_clock::time_point t0_;
  std::pair< OptimizationAlgorithmImplementation::ProgressCallback, void *> progressCallback_;
  std::pair< OptimizationAlgorithmImplementation::StopCallback, void *> stopCallback_;
};

END_NAMESPACE_OPENTURNS

#endif
