#pragma once

#include "GaussianExpectationLib.h"
#include "InterestRateTimeSliceProcess.h"
#include "../general_model/MFMBase.h"
#include "../1_dimensional/Function1d.h"
#include "../generic/Algorithm.h"
#include "../two_currency_model/ProcessCorrelations3d.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Stores data of a random FX process at discrete time step X_Ti, used to convert between a domestic and foreign currency,
// This process is not centered on zero and contains a drift term.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class FXTimeSliceProcess : public TimeSliceProcess
{

private:

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// true if spot values are exponential
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool m_log_fit;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Estimated variance of Ms_2 where s_2 are points on m_grid
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	double m_uncorrelated_variance;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// True if drift has been set up on this process
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool m_has_drift;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Fit the fx drift function f_{k-1} on this grid
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	gelib::d_vector m_drift_fitting_grid;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Domestic and foreign currencies at the same time slice as this FX process
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	const InterestRateTimeSliceProcess* m_domestic_currency_process;

	const InterestRateTimeSliceProcess* m_foreign_currency_process;

	std::unique_ptr<gelib::PiecewisePoly1d> m_forward_drift_function;

	double m_spot_fx;


	FXTimeSliceProcess(std::unique_ptr<TimeSliceProcess> base_process, std::unique_ptr<OptionPricer> pricer, double spot_fx, const InterestRateTimeSliceProcess* domestic_currency_process, const InterestRateTimeSliceProcess* foreign_currency_process, const MFMBase* model, bool log_fit);

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Construct FX process which calculates DFX call options by employing a Hull-White model
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	FXTimeSliceProcess(std::unique_ptr<TimeSliceProcess> base_process, double spot_fx, const InterestRateTimeSliceProcess* domestic_currency_process, const InterestRateTimeSliceProcess* foreign_currency_process, const MFMBase* model, const std::string& ub_error, double beta, double mux_price, double muy_price, double Cxx_price, double Cyy_price, double Cxy_price);

	static std::unique_ptr<FXTimeSliceProcess> construct(std::unique_ptr<TimeSliceProcess> base_process, double spot_fx, const InterestRateTimeSliceProcess* domestic_currency_process, const InterestRateTimeSliceProcess* foreign_currency_process, const MFMBase* model, const std::string& ub_error, double beta, double mux_price, double muy_price, double Cxx_price, double Cyy_price, double Cxy_price);

public:

	bool log_fit() const;

	gelib::d_vector& get_drift_fitting_grid();

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// retrieve variance of yarrow
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	double get_uncorrelated_grid_variance() const;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// set variance of yarrow TODO remove when this class can caluclate the variance by itself
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void set_uncorrelated_grid_variance(double variance);

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Override the standard fit and interpolation functions for log-fitting of FX values
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void fit_spot(const std::string& extrapolation) override;
	void interpolate_spot(gelib::d_vector const& x, gelib::d_vector& f_x) const override;
	double interpolate_spot(double x) const override;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Given points on uncorrelated domestic currency (x_arrow), and foreign currency (z_arrow) grids,
	// calculate the drift term on a vector of fx (y) points.
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	double mean_drift_at_T0() const;
	gelib::d_vector& mean_drift(SliceProcessCorrelations3d const& fwd_increment_correlations, double x_arrow, double z_arrow, gelib::d_vector const & y_arrow, gelib::d_vector & mu_y) const;

	void set_up_drift(SliceProcessCorrelations3d const & fwd_increment_correlations, std::string const& extrapolation, gelib::d_vector const& fwd_y_grid, gelib::d_vector & fwd_drift_fitting_grid);

	double get_spot_fx() const;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Construct a sequence of contiguous FX processes at given times. 
	// The constructed processes will calculate DFX call options by employing a Hull-White model
	// controlled by the last 5 parameters at each time slice
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	static std::vector<std::unique_ptr<FXTimeSliceProcess>> construct(
		double grid_spread, gelib::d_vector const& grid_centers, size_t number_of_gridpoints, const MFMBase* model,
		gelib::d_vector const& times, gelib::d_vector const& process_volatilities_squared, gelib::d_vector const& process_variances,
		double mean_reversion_param_domestic, double mean_reversion_param_foreign, double spot_fx,
		std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> const & domestic_currency_processes, std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> const & foreign_currency_processes,
		gelib::d_vector const& mux_price, gelib::d_vector const& muy_price, gelib::d_vector const& Cxx_price, gelib::d_vector const& Cyy_price, gelib::d_vector const& Cxy_price);


};
