/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*               This file is part of the program and library                */
/*    PaPILO --- Parallel Presolve for Integer and Linear Optimization       */
/*                                                                           */
/* Copyright (C) 2020-2026 Zuse Institute Berlin (ZIB)                       */
/*                                                                           */
/* Licensed under the Apache License, Version 2.0 (the "License");           */
/* you may not use this file except in compliance with the License.          */
/* You may obtain a copy of the License at                                   */
/*                                                                           */
/*     http://www.apache.org/licenses/LICENSE-2.0                            */
/*                                                                           */
/* Unless required by applicable law or agreed to in writing, software       */
/* distributed under the License is distributed on an "AS IS" BASIS,         */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  */
/* See the License for the specific language governing permissions and       */
/* limitations under the License.                                            */
/*                                                                           */
/* You should have received a copy of the Apache-2.0 license                 */
/* along with PaPILO; see the file LICENSE. If not visit scipopt.org.        */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "papilo/Config.hpp"
#include "papilo/core/Objective.hpp"
#include "papilo/misc/MultiPrecision.hpp"
#include "papilo/misc/OptionsParser.hpp"
#include "papilo/misc/VersionLogger.hpp"
#include "papilo/misc/Wrappers.hpp"

#include <boost/program_options.hpp>
#include <cassert>
#include <fstream>

#ifdef PAPILO_HAVE_SCIP

#include "papilo/interfaces/ScipInterface.hpp"
#include "scip/scipdefplugins.h"
static void
setupscip( SCIP* scip, void* usrdata )
{
   papilo::OptionsInfo* optInfo =
       reinterpret_cast<papilo::OptionsInfo*>( usrdata );

   SCIP_CALL_ABORT( SCIPincludeDefaultPlugins( scip ) );

   if( !optInfo->scip_settings_file.empty() )
   {
      SCIP_CALL_ABORT(
          SCIPreadParams( scip, optInfo->scip_settings_file.c_str() ) );
   }
}

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_mip_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::ScipFactory<REAL>::create( setupscip, &optionsInfo );
}

#elif defined PAPILO_HAVE_HIGHS

#include "papilo/interfaces/HighsInterface.hpp"

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_mip_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::HighsFactory<REAL>::create();
}

#elif defined PAPILO_HAVE_GUROBI

#include "papilo/interfaces/GurobiInterface.hpp"


template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_mip_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::GurobiFactory<REAL>::create( );
}
#else

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_mip_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return nullptr;
}

#endif

#if defined PAPILO_HAVE_ROUNDINGSAT

#include "papilo/interfaces/RoundingsatInterface.hpp"

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_sat_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::RoundingsatFactory<REAL>::create( );
}
#else

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_sat_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return nullptr;
}
#endif


#if defined( PAPILO_HAVE_SOPLEX )
#include "papilo/interfaces/SoplexInterface.hpp"

static void
setupsoplex( soplex::SoPlex& spx, void* usrdata )
{
   papilo::OptionsInfo* optInfo =
       reinterpret_cast<papilo::OptionsInfo*>( usrdata );

   if( !optInfo->soplex_settings_file.empty() )
      spx.loadSettingsFile( optInfo->soplex_settings_file.c_str() );
}

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_lp_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::SoplexFactory<REAL>::create( setupsoplex, &optionsInfo );
}
#elif defined PAPILO_HAVE_GLOP

#include "papilo/interfaces/GlopInterface.hpp"


template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_lp_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return papilo::GlopFactory<REAL>::create( );
}

#else

template <typename REAL>
static std::unique_ptr<papilo::SolverFactory<REAL>>
get_lp_solver_factory( papilo::OptionsInfo& optionsInfo )
{
   return nullptr;
}

#endif

int
main( int argc, char* argv[] )
{
   using namespace papilo;
//#ifdef PAPILO_HAVE_GLOP
//   google::InitGoogleLogging(argv[0]);
//#endif
   print_header();

   // get the options passed by the user
   OptionsInfo optionsInfo;
   try
   {
      optionsInfo = parseOptions( argc, argv );
   }
   catch( const boost::program_options::error& ex )
   {
      std::cerr << "Error while parsing the options.\n" << '\n';
      std::cerr << ex.what() << '\n';
      return 1;
   }

   if( !optionsInfo.is_complete )
      return 0;

   // run the command passed as argument
   switch( optionsInfo.command )
   {
   case Command::kNone:
      return 1;
   case Command::kPresolve:
   case Command::kSolve:
      switch( optionsInfo.arithmetic_type )
      {
      case ArithmeticType::kDouble:
         if( presolve_and_solve<double>(
                 optionsInfo, get_lp_solver_factory<double>( optionsInfo ),
                 get_mip_solver_factory<double>( optionsInfo ),
                 get_sat_solver_factory<double>( optionsInfo )) !=
             ResultStatus::kOk )
            return 1;
         break;
      case ArithmeticType::kQuad:
         if( presolve_and_solve<Quad>(
                 optionsInfo, get_lp_solver_factory<Quad>( optionsInfo ),
                 get_mip_solver_factory<Quad>( optionsInfo ),
                 get_sat_solver_factory<Quad>( optionsInfo )) !=
             ResultStatus::kOk )
            return 1;
         break;
      case ArithmeticType::kRational:
         if( presolve_and_solve<papilo::Rational>(
                 optionsInfo, get_lp_solver_factory<papilo::Rational>( optionsInfo ),
                 get_mip_solver_factory<papilo::Rational>( optionsInfo ),
                 get_sat_solver_factory<papilo::Rational>( optionsInfo )) !=
             ResultStatus::kOk )
            return 1;
      }
      break;
   case Command::kPostsolve:
      switch( optionsInfo.arithmetic_type )
      {
      case ArithmeticType::kDouble:
         postsolve<double>( optionsInfo );
         break;
      case ArithmeticType::kQuad:
         postsolve<Quad>( optionsInfo );
         break;
      case ArithmeticType::kRational:
         postsolve<papilo::Rational>( optionsInfo );
      }
   }

   return 0;
}
