// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// 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, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************

#include <getopt.h>

#include <cstdlib>

#include <filesystem>
#include <format>
#include <fstream>

#include "aevol.h"

// Helper functions
void print_help(std::filesystem::path prog_path);
auto interpret_cmd_line_args(int argc, char* argv[]) -> std::tuple<std::filesystem::path,
                                                                   std::filesystem::path,
                                                                   std::filesystem::path,
                                                                   bool>;

int main(int argc, char* argv[]) {
  // Print the hash and date of the commit used to compile this executable
  std::cout << std::format("Running aevol version {}\n", aevol::version_string);

  const auto [fasta_file_name, param_file_name, output_path, verbose] = interpret_cmd_line_args(argc, argv);

  // Open output file
  auto user_interface_output = std::make_unique<aevol::UserInterfaceOutput>(output_path, 1);

  // Initialize individual analyser
  auto individual_analyser = aevol::make_individual_analyser_from_param_file(param_file_name);
  const auto& phenotypic_target = individual_analyser->target();
  user_interface_output->write_environment_output(phenotypic_target);

  // Initialise fasta reader
  std::cout << "Reading sequence(s) from fasta file " << fasta_file_name << std::endl;
  auto fasta_reader = aevol::FastaReader(fasta_file_name);

  // For each sequence in fasta file
  while (not fasta_reader.eof()) {
    try {
      // Read individual from provided fasta file
      auto [seqid, indiv, modifiers] = fasta_reader.read_individual();

      // Extract generation from SeqID and set time accordingly
//      auto generation = fasta_reader.seqid_to_generation(seqid);
//      aevol::AeTime::set_time(generation);

      // Make and evaluate individual
      indiv->evaluate(aevol::exp_setup->w_max(), aevol::exp_setup->selection_pressure(), phenotypic_target);
      user_interface_output->write_indiv_output(*indiv, &seqid);
    } catch(std::exception& e) {
      if (not fasta_reader.eof()) {
        aevol::exit_with_usr_msg(e.what());
      }
    }
  }

  std::cout << std::format("UI outputs written to {}\n", output_path.string());

  return EXIT_SUCCESS;
}

void print_help(std::filesystem::path prog_path) {
  auto prog_name = prog_path.filename().string();

  std::cout << "******************************************************************************\n"
            << "*                                                                            *\n"
            << "*                        aevol - Artificial Evolution                        *\n"
            << "*                                                                            *\n"
            << "* Aevol is a simulation platform that allows one to let populations of       *\n"
            << "* digital organisms evolve in different conditions and study experimentally  *\n"
            << "* the mechanisms responsible for the structuration of the genome and the     *\n"
            << "* transcriptome.                                                             *\n"
            << "*                                                                            *\n"
            << "******************************************************************************\n"
            << "\n"
            << std::format("{}:\nGenerate ui output files from fasta and parameter file.\n", prog_name)
            << "\n"
            << "UI output files are json files that can be used e.g. by the aevol python package to generate "
            << "graphical views of an individual or the population.\n"
            << "The aevol python package can be installed with 'pip install aevol'.\n"
            << "\n"
            << std::format("Population level output cannot be generated using {}, ", prog_name)
            << "consider using the _from_checkpoint version.\n"
            << "\n"
            << std::format("Usage : {} -h or --help\n", prog_name)
            << std::format("   or : {} -V or --version\n", prog_name)
            << std::format("   or : {} [OPTIONS] FASTA_FILE PARAM_FILE\n", prog_name)
            << "\n"
            <<             "   FASTA_FILE:    sequence(s) of the individual(s) of interest\n"
            <<             "   PARAM_FILE:    parameters to be used for the experiment\n"
            << "\nOptions\n"
            << "  -h, --help\n\tprint this help, then exit\n"
            << "  -V, --version\n\tprint version number, then exit\n"
            << "  -v, --verbose\n\tbe verbose\n";
}

auto interpret_cmd_line_args(int argc, char* argv[]) -> std::tuple<std::filesystem::path,
                                                                   std::filesystem::path,
                                                                   std::filesystem::path,
                                                                   bool> {
  // Command-line option variables
  auto fasta_file_name = std::filesystem::path{};
  auto param_file_name = std::filesystem::path{};
  auto verbose = bool{false};

  // Define allowed options
  const char* options_list = "hVv";
  int option_index = 0;
  static struct option long_options_list[] = {
      {"help",          no_argument,       nullptr, 'h'},
      {"version",       no_argument,       nullptr, 'V'},
      {"verbose",       no_argument,       nullptr, 'v'},
      {0, 0, 0, 0}
  };

  // Get actual values of the CLI options
  int option;
  while ((option = getopt_long(argc, argv, options_list, long_options_list, &option_index)) != -1) {
    switch (option) {
      case 'h' : {
        print_help(argv[0]);
        exit(EXIT_SUCCESS);
      }
      case 'V' : {
        aevol::print_aevol_version();
        exit(EXIT_SUCCESS);
      }
      case 'v' : {
        verbose = true;
        break;
      }
      default : {
        // An error message is printed in getopt_long, we just need to exit
        exit(EXIT_FAILURE);
      }
    }
  }

  // Check number of positional arguments. There should be only exactly 2: the lineage file and the param file
  if (optind != argc - 2) {
    aevol::exit_with_usr_msg("please specify both a lineage file and a parameter file");
  }

  fasta_file_name = argv[optind];
  param_file_name = argv[optind + 1];

  auto output_path = "ui_output_dir";

  return std::make_tuple(fasta_file_name, param_file_name, output_path, verbose);
}
