/*
  Copyright 2023 SINTEF Digital

  This file is part of the Open Porous Media project (OPM).

  OPM 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 3 of the License, or
  (at your option) any later version.

  OPM 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 OPM.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef OPM_RST_CONV_HEADER_INCLUDED
#define OPM_RST_CONV_HEADER_INCLUDED

#include <array>
#include <functional>
#include <vector>

#include <opm/simulators/utils/ParallelCommunication.hpp>

namespace Opm {

namespace data { class Solution; }
class RSTConfig;

//! \brief Class computing RPTRST CONV output.
class RSTConv
{
public:
    using LocalToGlobalCellFunc = std::function<int(const int)>;
    //! \brief Constructor.
    //! \param globalCell Mapping from local to global cell indices
    //! \param comm Parallel communicator
    RSTConv(LocalToGlobalCellFunc globalCell,
            Parallel::Communication comm)
        : globalCell_(std::move(globalCell))
        , comm_(comm)
    {}

    //! \brief Init state at beginning of step.
    //! \param numCells Global number of active cells in the model
    //! \param rst_config RPTRST configuration
    //! \param compIdx Component index for phases {OIL, GAS, WAT, POLYMER, BRINE, SOLVENT}, negative if inactive
    void init(const std::size_t numCells,
              const RSTConfig& rst_config,
              const std::array<int,6>& compIdx);

    //! \brief Inserts the CONV output into the restart output container.
    void outputRestart(data::Solution& sol);

    //! \brief Adds the CONV output for given residual vector.
    template<class ResidualVector>
    void update(const ResidualVector& resid);

    //! \brief Adds the CONV_NEW output.
    void updateNewton(const std::vector<int>& convNewt);

    //! \brief Obtain a const-ref to the accumulated data (only used for unit testing).
    const std::vector<std::vector<int>>& getData() const
    { return cnv_X_; }

    //! \brief Obtain a const-ref to the CONV_NEW (only used for unit testing).
    const std::vector<int>& getConvNew() const
    { return conv_new_; }

    //! \brief Check if CONV is required.
    bool hasConv() const;

    //! \brief CONV_NEW is restarted at iteration 0.
    void prepareConv();

private:
    //! \brief Gathers and accumulates results to the CONV arrays.
    //! \param lIdx Vector of local indices (N first is used)
    //! \param resid Residual vector
    //! \param comp Component to consider
    //! \details Handles parallel reduction
    template<class ResidualVector>
    void gatherAndAccumulate(const std::vector<int>& lIdx,
                             const ResidualVector& resid, int comp);

    LocalToGlobalCellFunc globalCell_; //!< Global cell indices
    Parallel::Communication comm_; //!< Communicator
    std::vector<std::vector<int>> cnv_X_{}; //!< Counts of worst cells for RPTRST CONV
    std::vector<int> conv_new_{}; //!< Number of Newtons by each cell
    std::array<int,6> compIdx_{}; //!< Component indices
    int N_ = 0; //!< Number of cells to consider
};

} // namespace Opm

#endif // OPM_RST_CONV_HEADER_INCLUDED
