#include <nq/nesquant.hpp>
#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib::MonteCarlo;
using namespace NesQuant::MonteCarlo;
using namespace NesQuant::PricingEngines;

using namespace std;

int main() {
    try {
// i = EuropeanOption[100, 1, CallOption]
// svjdm = SVJDModel[100, 0.03, 0, 0.15, 0.00001, 0.15, 2, 0, 0, 0, 0]

        Option::Type type = Option::Call;
        double underlying = 100;
        double strike = 100;
        double maturity = 1;

        double riskFreeRate = 0.03;
        double dividendYield = 0.0;
        double volatility = 0.15;
        double volatilityOfVolatility = 0.00001;
        double steadyStateVolatility = 0.15;
        double meanReversionRate = 2;
        double correlationUnderlyingVolatility = 0;
        double jumpIntensity = 0;
        double jumpMean = 0;
        double jumpStandardDeviation = 0;

        long timeSteps = 1000;
        long seed = 0;

        SVJDEngine engine;

        // downcast the Arguments struct to the appropriate type...
        SVJDArguments* underArgs =
            dynamic_cast<SVJDArguments*>(engine.arguments());
        QL_ENSURE(underArgs != 0, "dynamic_cast failed");
        // ... and set the values
        underArgs->type = (Option::Type)type;
        underArgs->underlying = underlying;
        underArgs->strike = strike;
        underArgs->dividendYield = dividendYield;
        underArgs->riskFreeRate = riskFreeRate;
        underArgs->residualTime = maturity;
        underArgs->volatility = volatility;

        underArgs->volatilityOfVolatility = volatilityOfVolatility;
        underArgs->steadyStateVolatility = steadyStateVolatility;
        underArgs->meanReversionRate = meanReversionRate;
        underArgs->correlationUnderlyingVolatility
            = correlationUnderlyingVolatility;

        underArgs->jumpIntensity = jumpIntensity;
        underArgs->jumpMean = jumpMean;
        underArgs->jumpStandardDeviation = jumpStandardDeviation;

        engine.calculate(); //

        const OptionValue* results =
        dynamic_cast<const OptionValue*>(engine.results());
        QL_ENSURE(results != 0, "dynamic_cast failed");

        std::cout << "value : " << results->value << std::endl;

/*
        MCSVJDEngine(Size maxTimeStepsPerYear,
                     bool antitheticVariate,
                     Size requiredSamples,
                     double requiredTolerance,
                     Size maxSamples,
                     long seed)
*/
/*
        MCSVJDEngine<PseudoRandom> mcengine(timeSteps, false, 
                                   3000, Null<double>(), 
                                   Null<int>(), seed);
*/
        MCSVJDEngine<PseudoRandom> mcengine(timeSteps, false, 
                                   Null<int>(), 0.1, 
                                   Null<int>(), seed);

        // downcast the Arguments struct to the appropriate type...
        SVJDArguments* mcunderArgs =
            dynamic_cast<SVJDArguments*>(mcengine.arguments());
        QL_ENSURE(mcunderArgs != 0, "dynamic_cast failed");
        // ... and set the values
        mcunderArgs->type = (Option::Type)type;
        mcunderArgs->underlying = underlying;
        mcunderArgs->strike = strike;
        mcunderArgs->dividendYield = dividendYield;
        mcunderArgs->riskFreeRate = riskFreeRate;
        mcunderArgs->residualTime = maturity;
        mcunderArgs->volatility = volatility;

        mcunderArgs->volatilityOfVolatility = volatilityOfVolatility;
        mcunderArgs->steadyStateVolatility = steadyStateVolatility;
        mcunderArgs->meanReversionRate = meanReversionRate;
        mcunderArgs->correlationUnderlyingVolatility
            = correlationUnderlyingVolatility;

        mcunderArgs->jumpIntensity = jumpIntensity;
        mcunderArgs->jumpMean = jumpMean;
        mcunderArgs->jumpStandardDeviation = jumpStandardDeviation;

        mcengine.calculate();

        // now read additional results
        const OptionValue* mcresults =
        dynamic_cast<const OptionValue*>(mcengine.results());
        QL_ENSURE(mcresults != 0, "dynamic_cast failed");

        std::cout << "value : " << mcresults->value << std::endl;
        std::cout << "errorEstimate : " << mcresults->errorEstimate << std::endl;

        return 0;
    } catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cout << "unknown error" << std::endl;
        return 1;
    }
}

