// Copyright 2009, 2011 Hans Pirnay
// All Rights Reserved.
// This code is published under the Eclipse Public License.
//
// Date   : 2009-05-11

#include "SensAmplTNLP.hpp"
#include "IpIpoptApplication.hpp"
#include "SensApplication.hpp"
#include "IpPDSearchDirCalc.hpp"
#include "IpIpoptAlg.hpp"
#include "SensRegOp.hpp"


int main(int argv, char**argc)
{
  using namespace Ipopt;

  SmartPtr<IpoptApplication> app_ipopt = new IpoptApplication();
  app_ipopt->RethrowNonIpoptException(false);

  SmartPtr<SensApplication> app_sens = new SensApplication(app_ipopt->Jnlst(),
							   app_ipopt->Options(),
							   app_ipopt->RegOptions());

  // Register sIPOPT options
  RegisterOptions_sIPOPT(app_ipopt->RegOptions());
  app_ipopt->Options()->SetRegisteredOptions(app_ipopt->RegOptions());

  // Call Initialize the first time to create a journalist, but ignore
  // any options file
  ApplicationReturnStatus retval;
  retval = app_ipopt->Initialize("");
  if (retval != Solve_Succeeded) {
    //printf("ampl_ipopt.cpp: Error in first Initialize!!!!\n");
    exit(-100);
  }

  app_ipopt->Initialize();

  // prepare suffixes, or metadata ...
  SmartPtr<AmplSuffixHandler> suffix_handler = new AmplSuffixHandler();
  // Modified for warm-start from AMPL
  suffix_handler->AddAvailableSuffix("ipopt_zL_out", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
  suffix_handler->AddAvailableSuffix("ipopt_zU_out", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
  suffix_handler->AddAvailableSuffix("ipopt_zL_in", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
  suffix_handler->AddAvailableSuffix("ipopt_zU_in", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);

  suffix_handler->AddAvailableSuffix("parameter", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Index_Type);
  suffix_handler->AddAvailableSuffix("nominal_value", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
  suffix_handler->AddAvailableSuffix("perturbed_value", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);

  // Suffixes for sIPOPT
  suffix_handler->AddAvailableSuffix("sens_init_constr", AmplSuffixHandler::Constraint_Source, AmplSuffixHandler::Index_Type);

  int n_sens_steps = 0;
  app_ipopt->Options()->GetIntegerValue("n_sens_steps",n_sens_steps,"");
  std::string state;
  std::string state_value;
  std::string state_value_zL;
  std::string state_value_zU;
  std::string sol_state;
  std::string sol_state_zL;
  std::string sol_state_zU;
  for (int k=0; k<n_sens_steps+1; ++k) {
    state          = "sens_state_";
    state_value    = "sens_state_value_";
    sol_state      = "sens_sol_state_";
    state_value_zL = state_value;
    state_value_zU = state_value;
    sol_state_zL   = sol_state;
    sol_state_zU   = sol_state;
    append_Index(state,k);
    append_Index(state_value,k);
    append_Index(state_value_zL,k);
    append_Index(state_value_zU,k);
    append_Index(sol_state,k);
    append_Index(sol_state_zL,k);
    append_Index(sol_state_zU,k);
    sol_state_zL += "_z_L";
    sol_state_zU += "_z_U";
    state_value_zL += "_z_L";
    state_value_zU += "_z_U";
    suffix_handler->AddAvailableSuffix(state, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Index_Type);
    suffix_handler->AddAvailableSuffix(state_value, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(state_value_zL, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(state_value_zU, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(sol_state, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(sol_state, AmplSuffixHandler::Constraint_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(sol_state_zL, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
    suffix_handler->AddAvailableSuffix(sol_state_zU, AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Number_Type);
  }

  // for reduced hessian computation
  suffix_handler->AddAvailableSuffix("red_hessian", AmplSuffixHandler::Variable_Source, AmplSuffixHandler::Index_Type);

  // Create AmplOptionsList for sIPOPT AMPL options
  SmartPtr<AmplOptionsList> ampl_options_list = new AmplOptionsList();

  ampl_options_list->AddAmplOption("run_sens", "run_sens",
				   AmplOptionsList::String_Option,
				   "Set to yes if sens algorithm should be run.");
  ampl_options_list->AddAmplOption("compute_red_hessian", "compute_red_hessian",
				   AmplOptionsList::String_Option,
				   "Set to yes if reduced hessian should be computed.");
  ampl_options_list->AddAmplOption("sens_boundcheck", "sens_boundcheck",
				   AmplOptionsList::String_Option,
				   "Set to yes to enable the fix-relax QP adaption to a possible bound check. This feature is experimental.");
  ampl_options_list->AddAmplOption("n_sens_steps", "n_sens_steps",
				   AmplOptionsList::Integer_Option,
				   "Number of sensitivity steps");

  // create AmplSensTNLP from argc.
  SmartPtr<TNLP> sens_tnlp = new SensAmplTNLP(ConstPtr(app_ipopt->Jnlst()),
					      app_ipopt->Options(),
					      argc, suffix_handler, false,
					      ampl_options_list);

  app_sens->Initialize();

  const int n_loops = 1; // make larger for profiling
  for (Index i=0; i<n_loops; i++) {
    retval = app_ipopt->OptimizeTNLP(sens_tnlp);
  }

  /* give pointers to Ipopt algorithm objects to Sens Application */
  app_sens->SetIpoptAlgorithmObjects(app_ipopt, retval);

  app_sens->Run();


  return 0;
}
