/*
 * This file is part of the GROMACS molecular simulation package.
 *
 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 * Copyright (c) 2001-2004, The GROMACS development team.
 * Copyright (c) 2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 * and including many others, as listed in the AUTHORS file in the
 * top-level source directory and at http://www.gromacs.org.
 *
 * GROMACS 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 2.1
 * of the License, or (at your option) any later version.
 *
 * GROMACS 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 GROMACS; if not, see
 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 *
 * If you want to redistribute modifications to GROMACS, please
 * consider that scientific software is very special. Version
 * control is crucial - bugs must be traceable. We will be happy to
 * consider code for inclusion in the official distribution, but
 * derived work must not be called official GROMACS. Details are found
 * in the README & COPYING files - if they are missing, get the
 * official version at http://www.gromacs.org.
 *
 * To help us fund GROMACS development, we humbly ask that you cite
 * the research papers on the package. Check out http://www.gromacs.org.
 */
/*! \libinternal \file
 *
 * \brief This file declares helper functionality for legacy option handling for mdrun
 *
 * \author Berk Hess <hess@kth.se>
 * \author David van der Spoel <david.vanderspoel@icm.uu.se>
 * \author Erik Lindahl <erik@kth.se>
 * \author Mark Abraham <mark.j.abraham@gmail.com>
 *
 * \ingroup module_mdrun
 * \inlibraryapi
 */
#ifndef GMX_MDRUN_LEGACYMDRUNOPTIONS_H
#define GMX_MDRUN_LEGACYMDRUNOPTIONS_H

#include "gromacs/commandline/filenm.h"
#include "gromacs/commandline/pargs.h"
#include "gromacs/domdec/domdec.h"
#include "gromacs/hardware/hw_info.h"
#include "gromacs/mdlib/mdrun.h"
#include "gromacs/mdrun/logging.h"

#include "replicaexchange.h"

/* PLUMED */
#include "../../../Plumed.h"
extern int    plumedswitch;
extern plumed plumedmain; 
/* END PLUMED */

/* PLUMED HREX */
extern int plumed_hrex;
/* END PLUMED HREX */

struct gmx_multisim_t;

namespace gmx
{

/*! \libinternal
 * \brief This class provides the same command-line option
 * functionality to both CLI and API sessions.
 *
 * This class should not exist, but is necessary now to introduce
 * support for the CLI and API without duplicating code. It should be
 * eliminated following the TODOs below.
 *
 * \todo Modules in mdrun should acquire proper option handling so
 * that all of these declarations and defaults are local to the
 * modules.
 *
 * \todo Contextual aspects, such as working directory, MPI
 * environment, and environment variable handling are more properly
 * the role of SimulationContext, and should be moved there */
class LegacyMdrunOptions
{
    public:
        //! Ongoing collection of mdrun options
        MdrunOptions                     mdrunOptions;
        //! Options for the domain decomposition.
        DomdecOptions                    domdecOptions;
        //! Parallelism-related user options.
        gmx_hw_opt_t                     hw_opt;
        //! Command-line override for the duration of a neighbor list with the Verlet scheme.
        int                              nstlist_cmdline = 0;
        //! Parameters for replica-exchange simulations.
        ReplicaExchangeParameters        replExParams;

        //! Filename options to fill from command-line argument values.
        std::vector<t_filenm> filenames =
        {{{ efTPR, nullptr,     nullptr,     ffREAD },
          { efTRN, "-o",        nullptr,     ffWRITE },
          { efCOMPRESSED, "-x", nullptr,     ffOPTWR },
          { efCPT, "-cpi",      nullptr,     ffOPTRD | ffALLOW_MISSING },
          { efCPT, "-cpo",      nullptr,     ffOPTWR },
          { efSTO, "-c",        "confout",   ffWRITE },
          { efEDR, "-e",        "ener",      ffWRITE },
          { efLOG, "-g",        "md",        ffWRITE },
          { efXVG, "-dhdl",     "dhdl",      ffOPTWR },
          { efXVG, "-field",    "field",     ffOPTWR },
          { efXVG, "-table",    "table",     ffOPTRD },
          { efXVG, "-tablep",   "tablep",    ffOPTRD },
          { efXVG, "-tableb",   "table",     ffOPTRDMULT },
          { efTRX, "-rerun",    "rerun",     ffOPTRD },
          { efXVG, "-tpi",      "tpi",       ffOPTWR },
          { efXVG, "-tpid",     "tpidist",   ffOPTWR },
          { efEDI, "-ei",       "sam",       ffOPTRD },
          { efXVG, "-eo",       "edsam",     ffOPTWR },
          { efXVG, "-devout",   "deviatie",  ffOPTWR },
          { efXVG, "-runav",    "runaver",   ffOPTWR },
          { efXVG, "-px",       "pullx",     ffOPTWR },
          { efXVG, "-pf",       "pullf",     ffOPTWR },
          { efXVG, "-ro",       "rotation",  ffOPTWR },
          { efLOG, "-ra",       "rotangles", ffOPTWR },
          { efLOG, "-rs",       "rotslabs",  ffOPTWR },
          { efLOG, "-rt",       "rottorque", ffOPTWR },
          { efMTX, "-mtx",      "nm",        ffOPTWR },
          { efRND, "-multidir", nullptr,     ffOPTRDMULT},
          { efXVG, "-awh",      "awhinit",   ffOPTRD },
          { efDAT, "-plumed",   "plumed",    ffOPTRD },   /* PLUMED */
          { efDAT, "-membed",   "membed",    ffOPTRD },
          { efTOP, "-mp",       "membed",    ffOPTRD },
          { efNDX, "-mn",       "membed",    ffOPTRD },
          { efXVG, "-if",       "imdforces", ffOPTWR },
          { efXVG, "-swap",     "swapions",  ffOPTWR }}};

        //! Print a warning if any force is larger than this (in kJ/mol nm).
        real                             pforce = -1;

        /*! \brief Output context for writing text files
         *
         * \todo Clarify initialization, ownership, and lifetime. */
        gmx_output_env_t                *oenv = nullptr;

        //! Handle to file used for logging.
        LogFilePtr logFileGuard = nullptr;

        /*! \brief Command line options, defaults, docs and storage for them to fill. */
        /*! \{ */
        rvec              realddxyz                                               = {0, 0, 0};
        const char       *ddrank_opt_choices[static_cast<int>(DdRankOrder::nr)+1] =
        { nullptr, "interleave", "pp_pme", "cartesian", nullptr };
        const char       *dddlb_opt_choices[static_cast<int>(DlbOption::nr)+1] =
        { nullptr, "auto", "no", "yes", nullptr };
        const char       *thread_aff_opt_choices[threadaffNR+1] =
        { nullptr, "auto", "on", "off", nullptr };
        const char       *nbpu_opt_choices[5] =
        { nullptr, "auto", "cpu", "gpu", nullptr };
        const char       *pme_opt_choices[5] =
        { nullptr, "auto", "cpu", "gpu", nullptr };
        const char       *pme_fft_opt_choices[5] =
        { nullptr, "auto", "cpu", "gpu", nullptr };
        const char       *bonded_opt_choices[5] =
        { nullptr, "auto", "cpu", "gpu", nullptr };
        gmx_bool          bTryToAppendFiles     = TRUE;
        const char       *gpuIdsAvailable       = "";
        const char       *userGpuTaskAssignment = "";

        ImdOptions       &imdOptions = mdrunOptions.imdOptions;

        t_pargs           pa[49] = {

            { "-dd",      FALSE, etRVEC, {&realddxyz},
              "Domain decomposition grid, 0 is optimize" },
            { "-ddorder", FALSE, etENUM, {ddrank_opt_choices},
              "DD rank order" },
            { "-npme",    FALSE, etINT, {&domdecOptions.numPmeRanks},
              "Number of separate ranks to be used for PME, -1 is guess" },
            { "-nt",      FALSE, etINT, {&hw_opt.nthreads_tot},
              "Total number of threads to start (0 is guess)" },
            { "-ntmpi",   FALSE, etINT, {&hw_opt.nthreads_tmpi},
              "Number of thread-MPI ranks to start (0 is guess)" },
            { "-ntomp",   FALSE, etINT, {&hw_opt.nthreads_omp},
              "Number of OpenMP threads per MPI rank to start (0 is guess)" },
            { "-ntomp_pme", FALSE, etINT, {&hw_opt.nthreads_omp_pme},
              "Number of OpenMP threads per MPI rank to start (0 is -ntomp)" },
            { "-pin",     FALSE, etENUM, {thread_aff_opt_choices},
              "Whether mdrun should try to set thread affinities" },
            { "-pinoffset", FALSE, etINT, {&hw_opt.core_pinning_offset},
              "The lowest logical core number to which mdrun should pin the first thread" },
            { "-pinstride", FALSE, etINT, {&hw_opt.core_pinning_stride},
              "Pinning distance in logical cores for threads, use 0 to minimize the number of threads per physical core" },
            { "-gpu_id",  FALSE, etSTR, {&gpuIdsAvailable},
              "List of unique GPU device IDs available to use" },
            { "-gputasks",  FALSE, etSTR, {&userGpuTaskAssignment},
              "List of GPU device IDs, mapping each PP task on each node to a device" },
            { "-ddcheck", FALSE, etBOOL, {&domdecOptions.checkBondedInteractions},
              "Check for all bonded interactions with DD" },
            { "-ddbondcomm", FALSE, etBOOL, {&domdecOptions.useBondedCommunication},
              "HIDDENUse special bonded atom communication when [TT]-rdd[tt] > cut-off" },
            { "-rdd",     FALSE, etREAL, {&domdecOptions.minimumCommunicationRange},
              "The maximum distance for bonded interactions with DD (nm), 0 is determine from initial coordinates" },
            { "-rcon",    FALSE, etREAL, {&domdecOptions.constraintCommunicationRange},
              "Maximum distance for P-LINCS (nm), 0 is estimate" },
            { "-dlb",     FALSE, etENUM, {dddlb_opt_choices},
              "Dynamic load balancing (with DD)" },
            { "-dds",     FALSE, etREAL, {&domdecOptions.dlbScaling},
              "Fraction in (0,1) by whose reciprocal the initial DD cell size will be increased in order to "
              "provide a margin in which dynamic load balancing can act while preserving the minimum cell size." },
            { "-ddcsx",   FALSE, etSTR, {&domdecOptions.cellSizeX},
              "HIDDENA string containing a vector of the relative sizes in the x "
              "direction of the corresponding DD cells. Only effective with static "
              "load balancing." },
            { "-ddcsy",   FALSE, etSTR, {&domdecOptions.cellSizeY},
              "HIDDENA string containing a vector of the relative sizes in the y "
              "direction of the corresponding DD cells. Only effective with static "
              "load balancing." },
            { "-ddcsz",   FALSE, etSTR, {&domdecOptions.cellSizeZ},
              "HIDDENA string containing a vector of the relative sizes in the z "
              "direction of the corresponding DD cells. Only effective with static "
              "load balancing." },
            { "-gcom",    FALSE, etINT, {&mdrunOptions.globalCommunicationInterval},
              "Global communication frequency" },
            { "-nb",      FALSE, etENUM, {nbpu_opt_choices},
              "Calculate non-bonded interactions on" },
            { "-nstlist", FALSE, etINT, {&nstlist_cmdline},
              "Set nstlist when using a Verlet buffer tolerance (0 is guess)" },
            { "-tunepme", FALSE, etBOOL, {&mdrunOptions.tunePme},
              "Optimize PME load between PP/PME ranks or GPU/CPU (only with the Verlet cut-off scheme)" },
            { "-pme",     FALSE, etENUM, {pme_opt_choices},
              "Perform PME calculations on" },
            { "-pmefft", FALSE, etENUM, {pme_fft_opt_choices},
              "Perform PME FFT calculations on" },
            { "-bonded",     FALSE, etENUM, {bonded_opt_choices},
              "Perform bonded calculations on" },
            { "-v",       FALSE, etBOOL, {&mdrunOptions.verbose},
              "Be loud and noisy" },
            { "-pforce",  FALSE, etREAL, {&pforce},
              "Print all forces larger than this (kJ/mol nm)" },
            { "-reprod",  FALSE, etBOOL, {&mdrunOptions.reproducible},
              "Try to avoid optimizations that affect binary reproducibility" },
            { "-cpt",     FALSE, etREAL, {&mdrunOptions.checkpointOptions.period},
              "Checkpoint interval (minutes)" },
            { "-cpnum",   FALSE, etBOOL, {&mdrunOptions.checkpointOptions.keepAndNumberCheckpointFiles},
              "Keep and number checkpoint files" },
            { "-append",  FALSE, etBOOL, {&bTryToAppendFiles},
              "Append to previous output files when continuing from checkpoint instead of adding the simulation part number to all file names" },
            { "-nsteps",  FALSE, etINT64, {&mdrunOptions.numStepsCommandline},
              "Run this number of steps (-1 means infinite, -2 means use mdp option, smaller is invalid)" },
            { "-maxh",   FALSE, etREAL, {&mdrunOptions.maximumHoursToRun},
              "Terminate after 0.99 times this time (hours)" },
            { "-replex",  FALSE, etINT, {&replExParams.exchangeInterval},
              "Attempt replica exchange periodically with this period (steps)" },
            { "-nex",  FALSE, etINT, {&replExParams.numExchanges},
              "Number of random exchanges to carry out each exchange interval (N^3 is one suggestion).  -nex zero or not specified gives neighbor replica exchange." },
            { "-reseed",  FALSE, etINT, {&replExParams.randomSeed},
              "Seed for replica exchange, -1 is generate a seed" },
            { "-hrex",  FALSE, etBOOL, {&plumed_hrex}, /* PLUMED HREX */
              "Enable hamiltonian replica exchange" },
            { "-imdport",    FALSE, etINT, {&imdOptions.port},
              "HIDDENIMD listening port" },
            { "-imdwait",  FALSE, etBOOL, {&imdOptions.wait},
              "HIDDENPause the simulation while no IMD client is connected" },
            { "-imdterm",  FALSE, etBOOL, {&imdOptions.terminatable},
              "HIDDENAllow termination of the simulation from IMD client" },
            { "-imdpull",  FALSE, etBOOL, {&imdOptions.pull},
              "HIDDENAllow pulling in the simulation from IMD client" },
            { "-rerunvsite", FALSE, etBOOL, {&mdrunOptions.rerunConstructVsites},
              "HIDDENRecalculate virtual site coordinates with [TT]-rerun[tt]" },
            { "-confout", FALSE, etBOOL, {&mdrunOptions.writeConfout},
              "HIDDENWrite the last configuration with [TT]-c[tt] and force checkpointing at the last step" },
            { "-stepout", FALSE, etINT, {&mdrunOptions.verboseStepPrintInterval},
              "HIDDENFrequency of writing the remaining wall clock time for the run" },
            { "-resetstep", FALSE, etINT, {&mdrunOptions.timingOptions.resetStep},
              "HIDDENReset cycle counters after these many time steps" },
            { "-resethway", FALSE, etBOOL, {&mdrunOptions.timingOptions.resetHalfway},
              "HIDDENReset the cycle counters after half the number of steps or halfway [TT]-maxh[tt]" }
        };
        /*! \} */

        //! Handle to communication object.
        t_commrec        *cr = nullptr;
        //! Multi-simulation object.
        gmx_multisim_t   *ms = nullptr;

        //! Parses the command-line input and prepares to start mdrun.
        int updateFromCommandLine(int argc, char **argv, ArrayRef<const char *> desc);

        ~LegacyMdrunOptions();
};

} // end namespace gmx

#endif
