// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
  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 2 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/>.

  Consult the COPYING file in the top-level source directory of this
  module for the precise wording of the license and the list of
  copyright holders.
*/
/*!
 * \file
 *
 * \copydoc Opm::EclWriter
 */
#ifndef OPM_ECL_GENERIC_WRITER_HPP
#define OPM_ECL_GENERIC_WRITER_HPP

#include <opm/models/parallel/tasklets.hpp>

#include <opm/simulators/flow/CollectDataOnIORank.hpp>
#include <opm/simulators/flow/Transmissibility.hpp>
#include <opm/simulators/timestepping/SimulatorReport.hpp>

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

namespace Opm {

class EclipseIO;
class EclipseState;
class InterRegFlowMap;
class Inplace;
struct NNCdata;
class Schedule;
class SummaryConfig;
class SummaryState;
class UDQState;

} // namespace Opm

namespace Opm { namespace Action {
class State;
}} // namespace Opm::Action

namespace Opm {

template <class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
class EclGenericWriter
{
    using CartesianIndexMapper = Dune::CartesianIndexMapper<Grid>;
    using EquilCartesianIndexMapper = Dune::CartesianIndexMapper<EquilGrid>;
    using CollectDataOnIORankType = CollectDataOnIORank<Grid,EquilGrid,GridView>;
    using TransmissibilityType = Transmissibility<Grid,GridView,ElementMapper,CartesianIndexMapper,Scalar>;

public:
    EclGenericWriter(const Schedule& schedule,
                     const EclipseState& eclState,
                     const SummaryConfig& summaryConfig,
                     const Grid& grid,
                     const EquilGrid* equilGrid,
                     const GridView& gridView,
                     const Dune::CartesianIndexMapper<Grid>& cartMapper,
                     const Dune::CartesianIndexMapper<EquilGrid>* equilCartMapper,
                     bool enableAsyncOutput,
                     bool enableEsmry);

    const EclipseIO& eclIO() const;

    void writeInit();

    void setTransmissibilities(const TransmissibilityType* globalTrans)
    {
        globalTrans_ = globalTrans;
    }

    void setSubStepReport(const SimulatorReportSingle& report)
    {
        sub_step_report_ = report;
    }
    void setSimulationReport(const SimulatorReport& report)
    {
        simulation_report_ = report;
    }

    const std::vector<NNCdata>& getOutputNnc() const
    {
        return outputNnc_;
    }

    const CollectDataOnIORankType& collectOnIORank() const
    {
        return collectOnIORank_;
    }

    void extractOutputTransAndNNC(const std::function<unsigned int(unsigned int)>& map);

protected:
    const TransmissibilityType& globalTrans() const;
    unsigned int gridEquilIdxToGridIdx(unsigned int elemIndex) const;

    void doWriteOutput(const int                          reportStepNum,
                       const std::optional<int>           timeStepNum,
                       const bool                         isSubStep,
                       data::Solution&&                   localCellData,
                       data::Wells&&                      localWellData,
                       data::GroupAndNetworkValues&&      localGroupAndNetworkData,
                       data::Aquifers&&                   localAquiferData,
                       WellTestState&&                    localWTestState,
                       const Action::State&               actionState,
                       const UDQState&                    udqState,
                       const SummaryState&                summaryState,
                       const std::vector<Scalar>&         thresholdPressure,
                       Scalar                             curTime,
                       Scalar                             nextStepSize,
                       bool                               doublePrecision,
                       bool                               isFlowsn,
                       std::array<FlowsData<double>,3>&&  flowsn,
                       bool                               isFloresn,
                       std::array<FlowsData<double>, 3>&& floresn);

    void evalSummary(int                                                  reportStepNum,
                     Scalar                                               curTime,
                     const data::Wells&                                   localWellData,
                     const data::WellBlockAveragePressures&               localWBPData,
                     const data::GroupAndNetworkValues&                   localGroupAndNetworkData,
                     const std::map<int,data::AquiferData>&               localAquiferData,
                     const std::map<std::pair<std::string, int>, double>& blockData,
                     const std::map<std::string, double>&                 miscSummaryData,
                     const std::map<std::string, std::vector<double>>&    regionData,
                     const Inplace&                                       inplace,
                     const std::optional<Inplace>&                        initialInPlace,
                     const InterRegFlowMap&                               interRegFlows,
                     SummaryState&                                        summaryState,
                     UDQState&                                            udqState);

    CollectDataOnIORankType collectOnIORank_;
    const Grid& grid_;
    const GridView& gridView_;
    const Schedule& schedule_;
    const EclipseState& eclState_;
    std::unique_ptr<EclipseIO> eclIO_;
    std::unique_ptr<TaskletRunner> taskletRunner_;
    Scalar restartTimeStepSize_;
    const TransmissibilityType* globalTrans_ = nullptr;
    const Dune::CartesianIndexMapper<Grid>& cartMapper_;
    const Dune::CartesianIndexMapper<EquilGrid>* equilCartMapper_;
    const EquilGrid* equilGrid_;
    SimulatorReportSingle sub_step_report_;
    SimulatorReport simulation_report_;
    mutable std::vector<NNCdata> outputNnc_;
    mutable std::unique_ptr<data::Solution> outputTrans_;

private:
    void computeTrans_(const std::unordered_map<int,int>& cartesianToActive, const std::function<unsigned int(unsigned int)>& map) const;
    std::vector<NNCdata> exportNncStructure_(const std::unordered_map<int,int>& cartesianToActive, const std::function<unsigned int(unsigned int)>& map) const;
};

} // namespace Opm

#endif // OPM_ECL_GENERIC_WRITER_HPP
