// Copyright (C) 2026 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef SIMULATESTEPREGRESSIONCONTROLAUTODIFF_H
#define SIMULATESTEPREGRESSIONCONTROLAUTODIFF_H
#include <memory>
#include <vector>
#include <string>
#include <boost/lexical_cast.hpp>
#include "geners/BinaryFileArchive.hh"
#include "geners/Reference.hh"
#include "StOpt/dp/SimulateStepBase.h"
#include "StOpt/core/utils/StateWithStocks.h"
#include "StOpt/core/grids/FullGrid.h"
#include "StOpt/dp/OptimizerBaseInterp.h"
#include "StOpt/regression/GridAndRegressedValue.h"
#include "StOpt/regression/GridAndRegressedValueAutoDiff.h"
#include "StOpt/regression/GridAndRegressedValueGeners.h"

/** \file SimulateStepRegressionControlAutoDiff.h
 *  \brief  In simulation part, permits to  use the optimal control stored in in optimization step to calculate de Monte Carlo optimal trajectory
 *          Auto diff version so templated by asset
 *  \author Xavier Warin
 */
namespace StOpt
{

/// \class SimulateStepRegressionControlAutoDiff SimulateStepRegressionControlAutoDiff.h
/// One step in forward simulation using an auto diff library
/// Templated object is the asset
  template< class Optimizer, typename  T>
class SimulateStepRegressionControlAutoDiff
{
private :

    std::shared_ptr<FullGrid>  m_pGridFollowing ; ///< global grid at following time step
    std::shared_ptr<Optimizer >          m_pOptimize ; ///< optimizer solving the problem for one point and one step
    std::vector< GridAndRegressedValueAutoDiff<T> >  m_control ; ///< to store the optimal control calculated in optimization

public :

    /// \brief default
    SimulateStepRegressionControlAutoDiff() {}
    virtual ~SimulateStepRegressionControlAutoDiff() {}

    /// \brief Constructor
    /// \param p_ar               Archive where continuation values are stored
    /// \param p_iStep            Step number identifier
    /// \param p_nameCont         Name use to store continuation valuation
    /// \param p_pGridFollowing   grid at following time step
    /// \param p_pOptimize        Optimize object defining the transition step
    /// \param p_world            MPI communicator
    SimulateStepRegressionControlAutoDiff(gs::BinaryFileArchive &p_ar,  const int &p_iStep,  const std::string &p_nameCont,
                                          const   std::shared_ptr<FullGrid> &p_pGridFollowing,
                                          const  std::shared_ptr<Optimizer > &p_pOptimize):    m_pGridFollowing(p_pGridFollowing), m_pOptimize(p_pOptimize)
  {
    std::vector<GridAndRegressedValue> control;
    gs::Reference< std::vector<GridAndRegressedValue> > (p_ar, (p_nameCont + "Control").c_str(), boost::lexical_cast<std::string>(p_iStep).c_str()).restore(0, &control);
    m_control.resize(control.size());
    for (int i=0; i < control.size(); ++i)
      {
        m_control[i].cloneFromDouble( std::static_pointer_cast<FullGrid>(control[i].getGrid()), control[i].getRegressor(),control[i]);
      }
  }


    /// \brief Define one step arbitraging between possible commands
    /// \param p_statevector    Vector of states (regime, stock descriptor, uncertainty)
    /// \param p_phiInOut       actual contract value modified at current time step by applying an optimal command (size : number of function to follow by number of simulations)
  void oneStep(std::vector<StateWithStocks<T> > &p_statevector, Eigen::Array<T, Eigen::Dynamic,  Eigen::Dynamic>  &p_phiInOut) const
  {
    int is = 0 ;
    // no OPEN MP as not safe with some auto diff libraries
    for (is = 0; is <  static_cast<int>(p_statevector.size()); ++is)
    {
      m_pOptimize->stepSimulateControlAutoDiff(m_pGridFollowing, m_control, p_statevector[is], p_phiInOut.col(is));
    }
  }
};
}
#endif /* SIMULATESTEPREGRESSIONCONTROLAUTODIFF_H */



