// Copyright (C) 2025 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef SIMULATEREGREGRESSIONCUTGRIDADAPT2D_H
#define SIMULATEREGREGRESSIONCUTGRIDADAPT2D_H
#include <Eigen/Dense>
#include <functional>
#include <memory>
#ifdef USE_MPI
#include <boost/mpi.hpp>
#endif
#include "geners/BinaryFileArchive.hh"
#include "StOpt/core/utils/StateWithStocks.h"
#include "StOpt/core/grids/GridAdapt2D.h"
#include "StOpt/regression/BaseRegression.h"
#include "StOpt/dp/SimulateStepRegressionCutGridAdapt.h"
#include "StOpt/dp/OptimizerDPCutGridAdaptBase.h"
#include "StOpt/dp/SimulatorDPBase.h"


/** \file SimulateRegressionCutGridAdapt2D.h
 *  \brief Defines a simple program showing how to use simulations when optimizaton achived with transition problems solved with cuts.
 *         A 2D grid with adaptation is used. Cuts ae done on each mesh so no concavty is required
 *  \author Xavier Warin
 */

/// \brief Simulate the optimal strategy , Bellman cuts used to allow LP resolution of transition problems
/// \param p_optimize               optimizer defining the optimization between two time steps
/// \param p_pointStock             initial point stock
/// \param p_fileToDump             name of the file used to dump continuation values in optimization
/// \param p_world                  MPI communicator
double SimulateRegressionCutGridAdapt2D(const std::shared_ptr<StOpt::OptimizerDPCutGridAdaptBase > &p_optimize,
                                        const Eigen::ArrayXd &p_pointStock,
                                        const std::string   &p_fileToDump
#ifdef USE_MPI
                                        , const boost::mpi::communicator &p_world
#endif
                                       )
{
    // from the optimizer get back the simulator
    std::shared_ptr< StOpt::SimulatorDPBase> simulator = p_optimize->getSimulator();
    int nbStep = simulator->getNbStep();
    std::vector< StOpt::StateWithStocks> states;
    int initRegime = 0; // no regime
    states.reserve(simulator->getNbSimul());
    for (int is = 0; is < simulator->getNbSimul(); ++is)
        states.push_back(StOpt::StateWithStocks(initRegime, p_pointStock, Eigen::ArrayXd::Zero(simulator->getDimension())));
    gs::BinaryFileArchive ar(p_fileToDump.c_str(), "r");
    // name for continuation object in archive
    std::string nameAr = "Continuation";
    // cost function
    Eigen::ArrayXXd costFunction = Eigen::ArrayXXd::Zero(p_optimize->getSimuFuncSize(), simulator->getNbSimul());
    // iterate on time steps
    for (int istep = 0; istep < nbStep; ++istep)
    {
        StOpt::SimulateStepRegressionCutGridAdapt(ar, nbStep - 1 - istep, nameAr, p_optimize
#ifdef USE_MPI
                , p_world
#endif
                                                 ).oneStep(states, costFunction);
        // new stochastic state
        Eigen::ArrayXXd particles =  simulator->stepForwardAndGetParticles();
        for (int is = 0; is < simulator->getNbSimul(); ++is)
            states[is].setStochasticRealization(particles.col(is));
    }
    // average gain/cost
    return costFunction.mean();
}
#endif /* SIMULATEREGRESSIONCUTGRIDCUTADPAT2D_H */
