/*
 * This file is part of the GROMACS molecular simulation package.
 *
 * Copyright 2025- The GROMACS Authors
 * and the project initiators Erik Lindahl, Berk Hess and David van der Spoel.
 * Consult the AUTHORS/COPYING files and https://www.gromacs.org for details.
 *
 * 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
 * https://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 https://www.gromacs.org.
 *
 * To help us fund GROMACS development, we humbly ask that you cite
 * the research papers on the package. Check out https://www.gromacs.org.
 */

/*! \libinternal \file
 * \brief Declares backend-specific FMM options and enums for use in MDP handling.
 *
 * Used internally by FmmMdpOptions to delegate backend configuration.
 *
 * \author Muhammad Umair Sadiq <mumairsadiq1@gmail.com>
 */


#ifndef GMX_FMM_OPTIONS_H
#define GMX_FMM_OPTIONS_H

#include <string>

#include "gromacs/utility/classhelpers.h"
#include "gromacs/utility/enumerationhelpers.h"

class WarningHandler;

namespace gmx
{
class OptionSectionHandle;
class KeyValueTreeObjectBuilder;
class IKeyValueTreeTransformRules;

/**
 * @brief Enumeration of possible direct interaction providers.
 */
enum class FmmDirectProvider
{
    Gromacs, //!< Use GROMACS direct interactions
    Fmm,     //!< Use direct interactions from the FMM backend
    Count
};

/**
 * @brief Indicates which FMM backend is active based on MDP configuration.
 *
 * Only one FMM backend should be active at a time. This enum reflects the
 * active backend as determined by user-specified MDP options.
 */
enum class ActiveFmmBackend
{
    Inactive, //!< No FMM backend is active.
    ExaFmm,   //!< ExaFMM backend is active.
    FMSolvr,  //!< FMSolvr backend is active.
    Count
};

//! String names corresponding to ActiveFmmBackend enum values.
static const EnumerationArray<ActiveFmmBackend, const char*> c_activeFmmBackendNames = {
    { "inactive", "exafmm", "fmsolvr" }
};

/*! \brief Returns the string name for a given FMM backend.
 *
 * \param backend The FMM backend enum value.
 * \return The corresponding backend name as a std::string.
 */
std::string fmmBackendName(ActiveFmmBackend backend);

/*! \brief Returns the string name for a given FMM direct provider.
 *
 * \param directProvider The FmmDirectProvider enum value.
 * \return The corresponding direct provider name as a std::string.
 */
std::string fmmDirectProviderName(FmmDirectProvider directProvider);


//! MDP option name to enable one of the FMM backends (e.g., ExaFMM).
const std::string c_fmmActiveOptionName = "backend";

//! \brief Tree type options for ExaFMM.
enum class ExaFmmTreeType
{
    Uniform,
    Adaptive,
    Count
};

/*! \brief Returns the string name for a given ExaFmm Tree Type.
 *
 * \param treeType The ExaFmmTreeType enum value.
 * \return The corresponding tree type name as a std::string.
 */
std::string exaFmmTreeTypeName(ExaFmmTreeType treeType);

//! String names corresponding to ExaFmmTreeType enum values.
static const EnumerationArray<ExaFmmTreeType, const char*> c_exaFmmTreeTypeNames = {
    { "uniform", "adaptive" }
};

// ExaFMM MDP option names

//! MDP option name to configure the direct interaction range for ExaFMM (1 or 2)
const std::string c_fmmExaFmmDirectRangeOptionName = "exafmm-direct-range";
//! MDP option name to select the direct interaction provider for ExaFMM (GROMACS or FMM)
const std::string c_fmmExaFmmDirectProviderOptionName = "exafmm-direct-provider";
//! MDP option name to set the multipole expansion order for ExaFMM
const std::string c_fmmExaFmmOrderOptionName = "exafmm-order";
//! MDP option name to choose the tree type for ExaFMM (uniform or adaptive)
//! - Must be uniform when the direct provider is GROMACS
//! - If using an adaptive tree, maximum particles per cell must be specified
const std::string c_fmmExaFmmTreeTypeOptionName = "exafmm-tree-type";
//! MDP option name to set the tree depth for ExaFMM
//! - Required when using a uniform tree and the direct provider is FMM
//! - When the direct provider is GROMACS, the tree depth is determined
//!   based on the domain decomposition grid and should not be set by the user.
const std::string c_fmmExaFmmTreeDepthOptionName = "exafmm-tree-depth";
//! MDP option name to set the maximum number of particles per cell for ExaFMM
//! - Used only when an adaptive tree is used
//! - Must not be set when using a uniform tree
const std::string c_fmmExaFmmMaxParticlesPerCellOptionName = "exafmm-max-particles-per-cell";

//  FMSolvr MDP option names

//! MDP option name to set the multipole expansion order for FMSolvr
const std::string c_fmmFMSolvrOrderOptionName = "fmsolvr-order";
//! MDP option name to set the tree depth for FMSolvr (controls spatial subdivision granularity)
const std::string c_fmmFMSolvrTreeDepthOptionName = "fmsolvr-tree-depth";

/**
 * @brief Interface for FMM option sets used in MDP handling.
 *
 * This interface ensures that all FMM backend option types (e.g., ExaFMM, FMSolvr)
 * provide consistent implementations of the three MDP-related methods:
 * - initMdpOptionsFmm(): declares MDP options
 * - initMdpTransformFmm(): sets up option transformations
 * - buildMdpOutputFmm(): outputs values to the MDP file
 *
 * These methods follow the same structure as IMdpOptionProvider, but IFmmOptions does not
 * inherit from it because:
 * - Each FMM backend (e.g., ExaFMM, FMSolvr) is part of a larger option group,
 *   not a standalone MDP option provider.
 * - Only FmmMdpOptions is registered as the provider and delegates to the backends.
 *
 * This keeps the logic for each backend modular while centralizing integration.
 *
 */
struct IFmmOptions
{
    virtual void initMdpOptionsFmm(OptionSectionHandle& section)             = 0;
    virtual void initMdpTransformFmm(IKeyValueTreeTransformRules* rules)     = 0;
    virtual void buildMdpOutputFmm(KeyValueTreeObjectBuilder* builder) const = 0;
    virtual void validateMdpOptions(WarningHandler* wi) const                = 0;

    virtual ~IFmmOptions() = default;
    GMX_DEFAULT_CONSTRUCTORS(IFmmOptions);
};

struct ExaFmmOptions : IFmmOptions
{
    int               order               = 6;
    int               directRange         = 2;
    FmmDirectProvider directProvider      = FmmDirectProvider::Gromacs;
    ExaFmmTreeType    treeType            = ExaFmmTreeType::Uniform;
    int               treeDepth           = 0;
    int               maxParticlesPerCell = 0;

    void initMdpOptionsFmm(OptionSectionHandle& section) override;
    void initMdpTransformFmm(IKeyValueTreeTransformRules* rules) override;
    void buildMdpOutputFmm(KeyValueTreeObjectBuilder* builder) const override;
    void validateMdpOptions(WarningHandler* wi) const override;
};

struct FMSolvrOptions : IFmmOptions
{
    int               order              = 8;
    int               directRange        = 1;
    FmmDirectProvider directProvider     = FmmDirectProvider::Fmm;
    bool              dipoleCompensation = true;
    int               treeDepth          = 3;
    bool              sparse             = false;

    void initMdpOptionsFmm(OptionSectionHandle& section) override;
    void initMdpTransformFmm(IKeyValueTreeTransformRules* rules) override;
    void buildMdpOutputFmm(KeyValueTreeObjectBuilder* builder) const override;
    void validateMdpOptions(WarningHandler* wi) const override;
};

} // namespace gmx

#endif // GMX_FMM_OPTIONS_H
