/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                       */
/*    This file is part of the HiGHS linear optimization suite           */
/*                                                                       */
/*    Available as open-source under the MIT License                     */
/*                                                                       */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**@file ../app/RunHighs.cpp
 * @brief HiGHS main
 */
#include "Highs.h"
// #include "io/HighsIO.h"
#include "HighsRuntimeOptions.h"

// uncomment if we will be shutting down task executor from exe
// #include "parallel/HighsParallel.h"

int main(int argc, char** argv) {
  // Create the Highs instance.
  Highs highs;
  const HighsOptions& options = highs.getOptions();
  const HighsLogOptions& log_options = options.log_options;

  // Load user options.
  HighsCommandLineOptions cmd_options;
  HighsOptions loaded_options;

  // Set "HiGHS.log" as the default log_file for the app so that
  // log_file has this value if it isn't set in the file
  loaded_options.log_file = "HiGHS.log";
  // When loading the options file, any messages are reported using
  // the default HighsLogOptions

  // Replace command line options parsing library
  // cxxopts now Cpp17 with
  // CLI11 for Cpp11

  CLI::App app{""};
  argv = app.ensure_utf8(argv);

  setupCommandLineOptions(app, cmd_options);

  try {
    std::string usage_msg =
        "usage:\n      " + std::string(argv[0]) + " [options] [file]";
    app.usage(usage_msg);

    app.parse(argc, argv);
  } catch (const CLI::CallForHelp& e) {
    std::cout << app.help() << std::endl;
    return 0;
  } catch (const CLI::CallForAllHelp& e) {
    std::cout << app.help();
    return 0;
  } catch (const CLI::RequiredError& e) {
    std::cout << "Please specify filename in .mps|.lp|.ems format."
              << std::endl;
    return (int)HighsStatus::kError;
  } catch (const CLI::ExtrasError& e) {
    std::cout << e.what() << std::endl;
    std::cout << "Multiple files not supported." << std::endl;
    return (int)HighsStatus::kError;
  } catch (const CLI::ArgumentMismatch& e) {
    std::cout << e.what() << std::endl;
    std::cout << "Too many arguments provided. Please provide only one."
              << std::endl;
    return (int)HighsStatus::kError;
  } catch (const CLI::ParseError& e) {
    std::cout << e.what() << std::endl;
    // app.exit() should be called from main.
    return app.exit(e);
  }

  if (!loadOptions(app, log_options, cmd_options, loaded_options))
    return (int)HighsStatus::kError;

  // Open the app log file - unless output_flag is false, to avoid
  // creating an empty file. It does nothing if its name is "".
  if (loaded_options.output_flag) highs.openLogFile(loaded_options.log_file);

  // Pass the option settings to HiGHS. Only error-checking produces
  // output, but values are checked in loadOptions, so it's safe to
  // call this first so that printHighsVersionCopyright uses reporting
  // settings defined in any options file.
  highs.passOptions(loaded_options);
  //  highs.writeOptions("Options.md");
  highs.writeOptions("", true);

  // Load the model from model_file
  HighsStatus read_status = highs.readModel(cmd_options.model_file);
  if (read_status == HighsStatus::kError) {
    highsLogUser(log_options, HighsLogType::kInfo, "Error loading file\n");
    return (int)read_status;
  }

  if (cmd_options.cmd_read_basis_file != "") {
    HighsStatus basis_status = highs.readBasis(cmd_options.cmd_read_basis_file);
    if (basis_status == HighsStatus::kError) {
      highsLogUser(log_options, HighsLogType::kInfo,
                   "Error reading basis from file\n");
      return (int)basis_status;
    }
  }

  // Possible read a solution file
  if (cmd_options.read_solution_file != "") {
    HighsStatus read_solution_status =
        highs.readSolution(cmd_options.read_solution_file);
    if (read_solution_status == HighsStatus::kError) {
      highsLogUser(log_options, HighsLogType::kInfo,
                   "Error loading solution file\n");
      return (int)read_solution_status;
    }
  }
  if (options.write_presolved_model_to_file) {
    // Run presolve and write the presolved model to a file
    HighsStatus status = highs.presolve();
    if (status == HighsStatus::kError) return int(status);
    HighsPresolveStatus model_presolve_status = highs.getModelPresolveStatus();
    const bool ok_to_write =
        model_presolve_status == HighsPresolveStatus::kNotReduced ||
        model_presolve_status == HighsPresolveStatus::kReduced ||
        model_presolve_status == HighsPresolveStatus::kReducedToEmpty ||
        model_presolve_status == HighsPresolveStatus::kTimeout;
    if (!ok_to_write) {
      highsLogUser(log_options, HighsLogType::kInfo,
                   "No presolved model to write to file\n");
      return int(status);
    }
    status = highs.writePresolvedModel(options.write_presolved_model_file);
    return int(status);
  }
  // Solve the model
  HighsStatus run_status = highs.run();
  if (run_status == HighsStatus::kError) return int(run_status);

  // highs.writeInfo("Info.md");

  if (cmd_options.cmd_write_basis_file != "") {
    HighsStatus basis_status =
        highs.writeBasis(cmd_options.cmd_write_basis_file);
    if (basis_status == HighsStatus::kError) {
      highsLogUser(log_options, HighsLogType::kInfo,
                   "Error writing basis to file\n");

      return (int)basis_status;
    }
  }

  // Possibly write the solution to a file
  if (options.write_solution_to_file || options.solution_file != "")
    highs.writeSolution(options.solution_file, options.write_solution_style);

  // Possibly write the model to a file
  if (options.write_model_to_file) {
    HighsStatus write_model_status = highs.writeModel(options.write_model_file);
    if (write_model_status == HighsStatus::kError)
      return (int)write_model_status;  // todo: change to write model error
  }

  // Shut down task executor: optional and wip
  // HighsTaskExecutor::shutdown(true);

  return (int)run_status;
}
