// Copyright (C) 2019 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#include <Eigen/Dense>
#include <iostream>
#include <memory>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include "StOpt/core/grids/OneDimRegularSpaceGrid.h"
#include "StOpt/core/grids/OneDimData.h"
#include "test/c++/tools/simulators/MeanRevertingSimulator.h"
#include "test/c++/tools/simulators/AR1Simulator.h"
#include "test/c++/tools/BasketOptions.h"
#include "test/c++/tools/simulators/BlackScholesSimulator.h"
#include "test/c++/tools/dp/OptimizeSwing.h"
#include "test/c++/tools/dp/OptimizeFictitiousSwing.h"
#include "test/c++/tools/dp/OptimizeGasStorage.h"
#include "test/c++/tools/dp/OptimizeGasStorageTree.h"
#include "test/c++/tools/dp/OptimizeGasStorageSwitchingCost.h"
#include "test/c++/tools/dp/OptimizeLake.h"
#include "test/c++/tools/dp/OptimizeThermalAsset.h"
#include "test/c++/python/MeanRevertingWrap.h"
#include "test/c++/python/MeanReverting1DAssetsWrap.h"
#include "test/c++/python/MeanRevertingSimulatorTreeWrap.h"
#include "test/c++/python/BlackScholesSimulatorWrap.h"
#include "test/c++/python/OneDimDataWrap.h"
#include "test/c++/python/AR1Wrap.h"


/** \file Pybind11Optimizers.cpp
 * \brief Map Optimizers for Swing
 * \author Xavier Warin
 */

using namespace Eigen;
using namespace std;
using namespace StOpt;

namespace py = pybind11;



class OptimizerSwingBlackScholes : public  OptimizeSwing<BasketCall, BlackScholesSimulator>
{
public :
    OptimizerSwingBlackScholes(const BasketCall &p_payoff, const int &p_nPointStock): OptimizeSwing<BasketCall, BlackScholesSimulator>(p_payoff, p_nPointStock) {}

    inline void setSimulator(const std::shared_ptr<BlackScholesSimulatorWrap> &p_simulator)
    {
        OptimizeSwing::setSimulator(std::static_pointer_cast< BlackScholesSimulator >(p_simulator));
    }
};

class OptimizerFictitiousSwingBlackScholes : public  OptimizeFictitiousSwing<BasketCall, BlackScholesSimulator>
{
public :
    OptimizerFictitiousSwingBlackScholes(const BasketCall &p_payoff, const int &p_nPointStock, const int &p_ndim): OptimizeFictitiousSwing<BasketCall, BlackScholesSimulator>(p_payoff, p_nPointStock, p_ndim) {}

    inline void setSimulator(const std::shared_ptr<BlackScholesSimulatorWrap> &p_simulator)
    {
        OptimizeFictitiousSwing::setSimulator(std::static_pointer_cast< BlackScholesSimulator >(p_simulator));
    }
};


class OptimizeGasStorageMeanReverting : public OptimizeGasStorage< MeanRevertingSimulator< OneDimData<OneDimRegularSpaceGrid, double> > >
{
public :
    OptimizeGasStorageMeanReverting(const double   &p_injectionRate, const double &p_withdrawalRate,  const double &p_injectionCost, const double &p_withdrawalCost):
        OptimizeGasStorage(p_injectionRate, p_withdrawalRate, p_injectionCost, p_withdrawalCost) {}

    inline void setSimulator(const std::shared_ptr<MeanRevertingWrap> &p_simulator)
    {
        OptimizeGasStorage:: setSimulator(std::static_pointer_cast< MeanRevertingSimulator< StOpt::OneDimData< StOpt::OneDimRegularSpaceGrid, double> > >(p_simulator));
    }
};

class OptimizeGasStorageSwitchingCostMeanReverting : public OptimizeGasStorageSwitchingCost< MeanRevertingSimulator< OneDimData<OneDimRegularSpaceGrid, double> > >
{
public :
    OptimizeGasStorageSwitchingCostMeanReverting(const double   &p_injectionRate, const double &p_withdrawalRate,  const double &p_injectionCost, const double &p_withdrawalCost,
            const double &p_switchCost):
        OptimizeGasStorageSwitchingCost(p_injectionRate, p_withdrawalRate, p_injectionCost, p_withdrawalCost,  p_switchCost) {}


    OptimizeGasStorageSwitchingCostMeanReverting(const double &p_injectionRate, const double &p_withdrawalRate,
            const double &p_injectionCost, const double &p_withdrawalCost,
            const double &p_switchCost,
            const std::shared_ptr<RegimeCurve > &p_regime) :
        OptimizeGasStorageSwitchingCost(p_injectionRate, p_withdrawalRate, p_injectionCost, p_withdrawalCost, p_switchCost,
                                        std::static_pointer_cast<StOpt::OneDimData<StOpt::OneDimSpaceGrid, int> >(p_regime)) {}

    inline void setSimulator(const std::shared_ptr<MeanRevertingWrap> &p_simulator)
    {
        OptimizeGasStorageSwitchingCost:: setSimulator(std::static_pointer_cast< MeanRevertingSimulator< StOpt::OneDimData< StOpt::OneDimRegularSpaceGrid, double> > >(p_simulator));
    }
};

class OptimizeGasStorageMeanRevertingTree : public OptimizeGasStorageTree< MeanRevertingSimulatorTree< OneDimData<OneDimRegularSpaceGrid, double> > >
{
public :
    OptimizeGasStorageMeanRevertingTree(const double   &p_injectionRate, const double &p_withdrawalRate,  const double &p_injectionCost, const double &p_withdrawalCost):
        OptimizeGasStorageTree(p_injectionRate, p_withdrawalRate, p_injectionCost, p_withdrawalCost) {}

    inline void setSimulator(const std::shared_ptr<MeanRevertingSimulatorTreeWrap> &p_simulator)
    {
        OptimizeGasStorageTree:: setSimulator(std::static_pointer_cast< MeanRevertingSimulatorTree< StOpt::OneDimData< StOpt::OneDimRegularSpaceGrid, double> > >(p_simulator));
    }
};

class OptimizeLakeAR1 : public OptimizeLake<AR1Simulator>
{
public :
    explicit OptimizeLakeAR1(const double &p_withdrawalRate) : OptimizeLake(p_withdrawalRate) {}

    inline void setSimulator(const std::shared_ptr<AR1Wrap> &p_simulator)
    {
        OptimizeLake::setSimulator(p_simulator);
    }
};

class OptimizeThermalAssetMeanReverting1DAssets: public OptimizeThermalAsset<MeanReverting1DAssetsSimulator<OneDimData<OneDimRegularSpaceGrid, double> > >
{
public :
    OptimizeThermalAssetMeanReverting1DAssets(const double &p_switchCostFromOff): OptimizeThermalAsset(p_switchCostFromOff)
    {}

    inline void setSimulator(const std::shared_ptr<MeanReverting1DAssetsWrap> &p_simulator)
    {
        OptimizeThermalAsset:: setSimulator(std::static_pointer_cast< MeanReverting1DAssetsSimulator< StOpt::OneDimData< StOpt::OneDimRegularSpaceGrid, double> > >(p_simulator));
    }
    inline void resetSimulatorDirection(const bool &p_bForward)
    {
        OptimizeThermalAsset::resetSimulatorDirection(p_bForward);
    }
};


PYBIND11_MODULE(Optimizers, m)
{

    // map classical swing with Black Scholes
    py::class_<OptimizerSwingBlackScholes, std::shared_ptr<OptimizerSwingBlackScholes>, OptimizerDPBase > (m, "OptimizerSwingBlackScholes")
    .def(py::init<const BasketCall &, const int & >())
    .def("setSimulator", &OptimizerSwingBlackScholes::setSimulator)
    ;
    // map  swing in nD  with Black Scholes
    py::class_<OptimizerFictitiousSwingBlackScholes, std::shared_ptr<OptimizerFictitiousSwingBlackScholes>, OptimizerDPBase > (m, "OptimizerFictitiousSwingBlackScholes")
    .def(py::init<const BasketCall &, const int &, const int & >())
    .def("setSimulator", &OptimizerFictitiousSwingBlackScholes::setSimulator)
    ;
    // map gas storage
    py::class_<OptimizeGasStorageMeanReverting, std::shared_ptr<OptimizeGasStorageMeanReverting>, OptimizerDPBase >(m, "OptimizeGasStorageMeanReverting")
    .def(py::init<const double &, const double &,  const double &, const double & >())
    .def("setSimulator", &OptimizeGasStorageMeanReverting::setSimulator)
    ;
    // map gas storage with switching cost
    py::class_<OptimizeGasStorageSwitchingCostMeanReverting, std::shared_ptr<OptimizeGasStorageSwitchingCostMeanReverting>, OptimizerDPBase >(m, "OptimizeGasStorageSwitchingCostMeanReverting")
    .def(py::init<const double &, const double &,  const double &, const double &, const double & >())
    .def(py::init<const double &, const double &, const double &, const double &, const double &,  const std::shared_ptr<RegimeCurve > &> ())
    .def("setSimulator", &OptimizeGasStorageSwitchingCostMeanReverting::setSimulator)
    ;
    // map gas storage with tree
    py::class_<OptimizeGasStorageMeanRevertingTree, std::shared_ptr<OptimizeGasStorageMeanRevertingTree>, OptimizerDPTreeBase >(m, "OptimizeGasStorageMeanRevertingTree")
    .def(py::init<const double &, const double &,  const double &, const double & >())
    .def("setSimulator", &OptimizeGasStorageMeanRevertingTree::setSimulator)
    ;

    // map lake
    py::class_<OptimizeLakeAR1, std::shared_ptr<OptimizeLakeAR1>, OptimizerDPBase >(m, "OptimizeLakeAR1")
    .def(py::init<const double &>())
    .def("setSimulator", &OptimizeLakeAR1::setSimulator)
    ;

    // map thermal asset
    py::class_<OptimizeThermalAssetMeanReverting1DAssets, std::shared_ptr<OptimizeThermalAssetMeanReverting1DAssets>, OptimizerSwitchBase>(m, "OptimizeThermalAssetMeanReverting1DAssets")
    .def(py::init<const double &>())
    .def("setSimulator", &OptimizeThermalAssetMeanReverting1DAssets::setSimulator)
    .def("resetSimulatorDirection", &OptimizeThermalAssetMeanReverting1DAssets::resetSimulatorDirection)
    ;
}

