// Copyright (C) 2026 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef SIMULATEPRICEANDCONTROLUSINGCONTROLSTORAGEAUTODIFF_H
#define SIMULATEPRICEANDCONTROLUSINGCONTROLSTORAGEAUTODIFF_H
#include <functional>
#include <fstream>
#include <tuple>
#include <Eigen/Dense>
#include <geners/BinaryFileArchive.hh>
#include <StOpt/core/grids/FullGrid.h>
#include <StOpt/dp/SimulateStepRegressionControlAutoDiff.h>
#include "test/c++/tools/utils/Tunnel.h"
#include "test/c++/tools/simulators/MeanRevertingNDSimulator.h"


/* \file simulatePriceControlUsingControlStorageAutoDiff.h
 * Simulate the optimal control using the Control values calculated in  optimization. AutoDiff version
 *
 * \author Xavier warin
 */


/// \brief Simulate the storage management using the Control values calculated by Dynamic Programming
///        Residual stock in storage has no value
/// \param p_tunnel    the tunnel for the storage
/// \param p_optimize  the optimizer
/// \param p_pointStock initial position in storage
template< class Optimizer, typename  T>
T   simulatePriceControlUsingControlStorageAutoDiff(const Tunnel &p_tunnel,
        const std::shared_ptr<Optimizer> &p_optimizer,
        const Eigen::ArrayXd &p_pointStock,
        const std::string    &p_fileForControl)
{
    // from the optimizer get back the simulator
    std::shared_ptr< MeanRevertingNDSimulatorT<T>> simulator =  std::static_pointer_cast<MeanRevertingNDSimulatorT<T>>(p_optimizer->getSimulator());
    int nbStep = simulator->getNbStep();
    std::vector< StOpt::StateWithStocks<T>> states;
    states.reserve(simulator->getNbSimul());
    {
        Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>  initPart =  simulator->getParticlesAD().array();
        for (int is = 0; is < simulator->getNbSimul(); ++is)
            states.push_back(StOpt::StateWithStocks<T>(0, p_pointStock.template cast<T>(), initPart.col(is)));
    }
    Eigen::Array<T, Eigen::Dynamic, 1> stockNext = Eigen::Array<T, Eigen::Dynamic, 1>::Constant(simulator->getNbSimul(), T(p_pointStock(0)));

    // test if one file generated
    gs::BinaryFileArchive ar(p_fileForControl.c_str(), "r");
    // name for continuation object in archive
    std::string nameAr = "Continuation";
    // gain function trajectories
    Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>  gainAndContFunction = Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>::Zero(p_optimizer->getSimuFuncSize(), simulator->getNbSimul());
    std::shared_ptr< std::fstream> fileForResultsPosition;
    std::shared_ptr< std::fstream> fileForResultsPrice ;

    // use one Control file
    // bool bOneFile = true;
    for (int istep = 0; istep < nbStep; ++istep)
    {

        // grid on next step
        // std::shared_ptr<StOpt::FullGrid> gridNextCurr=  p_tunnel.getGrid(istep);
        std::shared_ptr<StOpt::FullGrid> gridNextStep =  p_tunnel.getGrid(istep + 1);
        // optimal control
        StOpt::SimulateStepRegressionControlAutoDiff<Optimizer, T>(ar, istep, nameAr,  gridNextStep,  p_optimizer).oneStep(states, gainAndContFunction);

        // step forward
        // new stochastic state for next step
        Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic> particules =  simulator->stepForwardAndGetParticlesAD();
        // for next step : new stochastic state
        for (int is = 0; is < simulator->getNbSimul(); ++is)
            states[is].setStochasticRealization(particules.col(is));

    }
    return gainAndContFunction.row(0).mean();
}
#endif
