// Copyright (C) 2019 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl_bind.h>
#include <pybind11/stl.h>
#include "StOpt/sddp/SDDPFinalCut.h"
#include "StOpt/sddp/SimulatorSDDPBase.h"
#include "StOpt/sddp/OptimizerSDDPBase.h"
#include "StOpt/python/BinaryFileArchiveStOpt.h"

/** \file Pybind11SDDPUnitTest.cpp
 * \brief test for SDDP mapping
 * \author Xavier Warin
 */

namespace py = pybind11;


std::pair<double, double> backwardForwardSDDPTestMapping(std::shared_ptr<StOpt::OptimizerSDDPBase> &,
        const int &,
        const Eigen::ArrayXd &,
        const StOpt::SDDPFinalCut &,
        const Eigen::ArrayXd &,
        const Eigen::ArrayXi &,
        const std::string &,
        const std::string &,
        const std::string &,
        int &,
        double &,
        const int &)
{
    return std::make_pair(0., 0.);
}

py::dict backwardForwardSDDPTestMappingWrap(std::shared_ptr<StOpt::OptimizerSDDPBase>    a,
        const int    &b,
        const Eigen::ArrayXd &c,
        const StOpt::SDDPFinalCut &d,
        const Eigen::ArrayXd &e,
        const Eigen::ArrayXi &f,
        const std::string &g,
        const std::string &h,
        const std::string &i,
        int  j,
        double  k,
        const int &l)
{
    std::pair<double, double> retLoc = backwardForwardSDDPTestMapping(a, b, c, d, e, f, g, h, i, j, k, l);
    py::dict dicReturn;
    dicReturn["BackWardEstimation"] = retLoc.first;
    dicReturn["ForwardEStimation"] = retLoc.second;
    dicReturn["NumberOfSDDPIterations"] = j;
    dicReturn["AccuracyReached"] = k;
    return dicReturn;

}

/// \brief backwardSDDP wrapper
/// \return value obtained in optimization
double backwardSDDPTestMappingWrap(
    std::shared_ptr<StOpt::OptimizerSDDPBase>,
    std::shared_ptr<StOpt::SimulatorSDDPBase>,
    const Eigen::ArrayXd &,
    const Eigen::ArrayXd &,
    const StOpt::SDDPFinalCut &,
    const std::shared_ptr<BinaryFileArchiveStOpt> &,
    const std::string &,
    const std::shared_ptr<BinaryFileArchiveStOpt> &)
{
    return 0. ;
}

double	forwardSDDPTestMappingWrap(
    std::shared_ptr<StOpt::OptimizerSDDPBase>,
    std::shared_ptr<StOpt::SimulatorSDDPBase>,
    const Eigen::ArrayXd &,
    const Eigen::ArrayXd &,
    const StOpt::SDDPFinalCut &,
    const bool &,
    const std::shared_ptr<BinaryFileArchiveStOpt > &,
    const std::shared_ptr<BinaryFileArchiveStOpt> &,
    const std::string &)
{
    return 0. ;
}

class SimulTest : public StOpt::SimulatorSDDPBase
{
public:
    SimulTest() {}
    int getNbSimul() const
    {
        return 0;
    }
    int getNbSample() const
    {
        return 0;
    }
    void updateDateIndex(const int &) {}
    Eigen::VectorXd getOneParticle(const int &) const
    {
        return Eigen::VectorXd::Zero(1);
    }
    Eigen::MatrixXd getParticles() const
    {
        return Eigen::MatrixXd::Zero(1, 1);
    }
    void resetTime() {}
    void updateSimulationNumberAndResetTime(const int &) {}
};


class OptimizeTest : public StOpt::OptimizerSDDPBase
{
    std::shared_ptr< SimulTest> m_simul;

public :
    OptimizeTest() {}
    OptimizeTest(const std::shared_ptr< SimulTest > &p_simul): m_simul(p_simul) {}

    Eigen::ArrayXd oneStepBackward(const StOpt::SDDPCutOptBase &, const std::tuple< std::shared_ptr<Eigen::ArrayXd>, int, int > &, const Eigen::ArrayXd &, const int &) const
    {
        return Eigen::ArrayXd::Zero(1) ;
    }

    double oneStepForward(const Eigen::ArrayXd &, Eigen::ArrayXd &,  Eigen::ArrayXd &, const StOpt::SDDPCutOptBase &, const int &) const
    {
        return 0;
    }
    void updateDates(const double &, const double &) {}
    Eigen::ArrayXd oneAdmissibleState(const double &)
    {
        return Eigen::ArrayXd();
    }
    int getStateSize() const
    {
        return 0;
    }
    std::shared_ptr< StOpt::SimulatorSDDPBase > getSimulatorBackward() const
    {
        return m_simul;
    }
    std::shared_ptr< StOpt::SimulatorSDDPBase > getSimulatorForward() const
    {
        return m_simul;
    }

};


PYBIND11_MODULE(StOptSDDPUnitTest, m)
{
    /// map test
    py::class_<SimulTest, std::shared_ptr<SimulTest>, StOpt::SimulatorSDDPBase >(m, "SimulTest")
    .def(py::init<>())
    ;

    py::class_<OptimizeTest, std::shared_ptr<OptimizeTest>, StOpt::OptimizerSDDPBase >(m, "OptimizeTest")
    .def(py::init< const std::shared_ptr< SimulTest >& >())
    .def("getSimulatorBackward", &OptimizeTest::getSimulatorBackward);
    ;

    m.def("backwardForwardSDDPTestMapping", &backwardForwardSDDPTestMappingWrap);
    m.def("backwardSDDPTestMapping", &backwardSDDPTestMappingWrap);
    m.def("forwardSDDPTestMapping", &forwardSDDPTestMappingWrap);
}
