/**
 * Resampling.hh
 *
 *  Created on: 31 oct. 2012
 *      Author: AKKA IS
 */

#ifndef Resampling_HH_
#define Resampling_HH_

#include <list>
#include "ParamData.hh"
#include "DataTypeMath.hh"
#include "VisitorOfParamData.hh"
#include "Operation.hh"

#include "TimeInterval.hh"

namespace AMDA {
namespace Parameters {

namespace Resampling {

class MarkerAbstract {
public:
	MarkerAbstract() : _startTime(0.), _stopTime(0.), _samplingTime(0.), _markerMinTime(0.), _markerMaxTime(0.), _targetTime(0.) {
	}

	virtual bool init(double pStartTime, double pStopTime, int /*currentTimeIntervalIndex*/, double pSamplingTime) {
		_startTime = pStartTime;
		_stopTime = pStopTime;
		_samplingTime = pSamplingTime;
		return true;
	}

	virtual bool nextTarget() = 0;

	double getTargetTime() {
		return _targetTime;
	}

	double getMarkerMinTime() {
		return _markerMinTime;
	}

	double getMarkerMaxTime() {
		return _markerMaxTime;
	}

	virtual bool isFinished() = 0;

protected:
	bool setTargetTime(double pTargetTime) {
		_targetTime = pTargetTime;
		_markerMinTime = _targetTime - _samplingTime / 2.;
		if (_markerMinTime < _startTime) {
			_markerMinTime = _startTime;
		}
		_markerMaxTime = _targetTime + _samplingTime / 2.;
		return !isFinished();
	}

	double _startTime;
	double _stopTime;
	double _samplingTime;
	double _markerMinTime;
        double _markerMaxTime;
	double _targetTime;
};

class MarkerRefParam: public MarkerAbstract {
public:
	MarkerRefParam() : MarkerAbstract(), _refParamDataPtr(NULL), _crtRefIndex(-1) {
	}

	void setRefParamData(ParamData* refParamDataPtr) {
		_refParamDataPtr = refParamDataPtr;
	}

	bool init(double pStartTime, double pStopTime, int currentTimeIntervalIndex, double pSamplingTime) {
		MarkerAbstract::init(pStartTime, pStopTime, currentTimeIntervalIndex, pSamplingTime);
		if (_refParamDataPtr == NULL) {
			return false;
		}
		if (_refParamDataPtr->getIndexInfo()._endTimeIntIndexList.empty() || (currentTimeIntervalIndex == 0)) {
			_crtRefIndex = 0;
		}
		else {
			std::list<unsigned int>::iterator it = _refParamDataPtr->getIndexInfo()._endTimeIntIndexList.begin();
			std::advance(it, currentTimeIntervalIndex-1);
			_crtRefIndex = *it;
		}
		while (pStartTime > _refParamDataPtr->getTime(_crtRefIndex)) {
			++_crtRefIndex;
			if (_crtRefIndex >= (int)_refParamDataPtr->getDataNumber()) {
				_crtRefIndex = -1;
				return false;
			}
		}
		return setTargetTime(_refParamDataPtr->getTime(_crtRefIndex));
	}

	bool nextTarget() {
		if (_crtRefIndex < 0) {
			return false;
		}
		if (_refParamDataPtr == NULL) {
			_crtRefIndex = -1;
			return false;
		}
		++_crtRefIndex;
		if (_crtRefIndex >= (int)_refParamDataPtr->getDataNumber()) {
			_crtRefIndex = -1;
			return false;
		}
		return setTargetTime(_refParamDataPtr->getTime(_crtRefIndex));
	}

	bool isFinished() {
		return (_crtRefIndex == -1);
	}

private:
	ParamData* _refParamDataPtr;

	int _crtRefIndex;
};

class MarkerSampling: public MarkerAbstract {
public:
	MarkerSampling() : MarkerAbstract() {
	}

	bool init(double pStartTime, double pStopTime, int currentTimeIntervalIndex, double pSamplingTime) {
		MarkerAbstract::init(pStartTime, pStopTime, currentTimeIntervalIndex, pSamplingTime);
		return setTargetTime(pStartTime);
	}

	bool nextTarget() {
		return setTargetTime(getTargetTime() + _samplingTime);
	}

	bool isFinished() {
		return (_targetTime < _startTime || _targetTime > _stopTime);
	}
};


/**
 * Define type Resampling.
 */
class ResamplingAbstract: public Operation {
public:
	/**
	 * @brief constructor
	 */
	ResamplingAbstract(Process& pProcess, bool pUseNearestValue) :
			Operation(pProcess), _isNewInt(true), _useNearestValue(pUseNearestValue) {

	}
	/**
	 * @brief destructor.
	 */
	virtual ~ResamplingAbstract() {
	}
	/**
	 * @brief compute static data
	 */
	virtual void init() = 0;

	/**
	 * @overload Operation::reset(double pStartTime, double pTimeInt)
	 * @brief reset static data to process another TimeInterval
	 */
	virtual void reset() {
		Operation::reset();
		resetResampling();
		_isNewInt = true;
	}

	/*
	 * @brief return sampling value
	 */
	virtual double getSampling() = 0;

	/*
	 * @brief reset resampling
	 */
	virtual void resetResampling() = 0;

protected:
	/**
	 *@brief  tag to know if a new time interval is set
	 */
	bool _isNewInt;

	/*
	 * @brief  Define the resampling strategy
	 */
	bool _useNearestValue;
};

/**
 * Simple Resampling abstract class, depand of paramData type
 */
template<typename TParamData>
class ResamplingSimpleAbstract: public ResamplingAbstract {

public:
	/**
	 * @brief  Element type of paramData
	 */
	typedef typename TParamData::ElementType ElementType;

	/**
	 * @brief  Constructor
	 */
	ResamplingSimpleAbstract(Process& pProcess, MarkerAbstract* pMarkerPtr, TimeIntervalListSPtr pTimeIntervalList, double gapSize, TParamData &param, bool pUseNearestValue) :
			ResamplingAbstract(pProcess, pUseNearestValue),
					_markerPtr(pMarkerPtr),
					_timeIntervalList(pTimeIntervalList),
					_currentTimeInterval(_timeIntervalList->begin()), _currentTimeIntervalIndex(0), _samplingMode(INTERPOLATION), _nearestTime(NAN), _lastTime(0),
					_leftEffect(true), _paramOutput(new TParamData()), _paramInput(param),
					_gapSize(gapSize) {
		_paramDataOutput = _paramOutput;
	}

	/**
	 * @brief  Destructor
	 */
	virtual ~ResamplingSimpleAbstract() {
	}
	;

	/**
	 * @brief Type List of Element type of paramData
	 */
	typedef std::list<ElementType> ListValue;

	/**
	 * @overload ResamplingAbstract::ResamplingAbstract::write()
	 */
	void write(ParamDataIndexInfo &pParamDataIndexInfo) {
		if (_isNewInt)
		{
			_markerPtr->init((*_currentTimeInterval)._startTime, (*_currentTimeInterval)._stopTime, _currentTimeIntervalIndex, getSampling());
			_isNewInt = false;
		}

		if ((pParamDataIndexInfo._nbDataToProcess > 0)) {
			//init _val with the first data value (to init dimensions)
			if (pParamDataIndexInfo._startIndex == 0)
			{
				_val = _paramInput.get(pParamDataIndexInfo._startIndex);
				_nearestValue = _paramInput.get(pParamDataIndexInfo._startIndex);
				_val << NotANumber();
				_nearestValue << NotANumber();
			}

			switch (_samplingMode)
			{
			case AVERAGE :
				writeAverage(pParamDataIndexInfo);
				break;
			case NEARESTVALUE :
				writeNearestValue(pParamDataIndexInfo);
				break;
			case INTERPOLATION :
				writeInterpolation(pParamDataIndexInfo);
				break;
			}
		}
		if (pParamDataIndexInfo._timeIntToProcessChanged || pParamDataIndexInfo._noMoreTimeInt) {
			terminated();
		}
	}

	/**
	 * @brief compute and write last value, right effect
	 */
	virtual void terminated() {
		if (_markerPtr->isFinished()) {
			return;
		}
		switch (_samplingMode)
		{
		case AVERAGE :
			do {
				if (!_mem.empty()) {
					_val = average(_mem,_paramInput.getDim1(),_paramInput.getDim2());
				}
				else {
					_val << NotANumber();
				}
				_paramOutput->push(_val);
				_paramDataOutput->pushTime(_markerPtr->getTargetTime());
				_mem.clear();
			} while(_markerPtr->nextTarget());
			break;
		case INTERPOLATION :
		case NEARESTVALUE :
			//Fix  side-off effects - Right boundary - CONSTANTS
			do {
				if ((_markerPtr->getTargetTime() - _lastTime) <= _gapSize)
					_val = _valMoins1;
				else
					_val << NotANumber();
				_paramOutput->pushTime(_markerPtr->getTargetTime());
				_paramOutput->push(_val);
			
			} while(_markerPtr->nextTarget());
			break;
		}
	}

	/**
	 * @brief compute and write new value in average mode
	 */
	void writeNearestValue(ParamDataIndexInfo &pParamDataIndexInfo)
	{
		for (unsigned int index = pParamDataIndexInfo._startIndex;
				index < pParamDataIndexInfo._startIndex
					+ pParamDataIndexInfo._nbDataToProcess; index++)
		{
			double timeCurrent = _paramInput.getTime(index);

			if (timeCurrent < _markerPtr->getMarkerMaxTime())
			{
				if (timeCurrent < _markerPtr->getMarkerMinTime()) {
					continue;
				}
				if (isNAN(_nearestTime))
				{
					_nearestValue = _paramInput.get(index);
					_nearestTime = timeCurrent;
				}
				else if (abs(_nearestTime-_markerPtr->getTargetTime()) > abs(timeCurrent-_markerPtr->getTargetTime()))
				{
					_nearestValue = _paramInput.get(index);
					_nearestTime = timeCurrent;
				}
			} else {
				_paramOutput->push(_nearestValue);
				_paramDataOutput->pushTime(_markerPtr->getTargetTime());
				if(!_markerPtr->nextTarget())
					break;
				while (_markerPtr->getMarkerMaxTime() < timeCurrent)
				{
					if (timeCurrent - _lastTime < _gapSize) {
						//Interpolation
						double coefT = (float) (_markerPtr->getTargetTime() - _lastTime) / (timeCurrent - _lastTime);
						_val = interpolation(_valMoins1, _paramInput.get(index), coefT, _paramInput.getDim1(),_paramInput.getDim2());
					}
					else {
						_val << NotANumber();
					}
					_paramOutput->push(_val);
					_paramDataOutput->pushTime(_markerPtr->getTargetTime());
					_nearestTime << NotANumber();
					if(!_markerPtr->nextTarget())
						break;
				}
				_nearestValue = _paramInput.get(index);
				_nearestTime = timeCurrent;
			}

			_lastTime = _paramInput.getTime(index);
			_valMoins1 = _paramInput.get(index);
		}
	}

	/**
	 * @brief compute and write new value in average mode
	 */
	void writeAverage(ParamDataIndexInfo &pParamDataIndexInfo) {
		for (unsigned int index = pParamDataIndexInfo._startIndex;
				index
						< pParamDataIndexInfo._startIndex
								+ pParamDataIndexInfo._nbDataToProcess;
				index++) {

			double timeCurrent = _paramInput.getTime(index);

			if (timeCurrent < _markerPtr->getMarkerMaxTime()) {
				if (timeCurrent >= _markerPtr->getMarkerMinTime()) { 
					_mem.push_back(_paramInput.get(index));
				}
			} else {
				if (!_mem.empty()) {
					_val = average(_mem,_paramInput.getDim1(),_paramInput.getDim2());
				}
				else if (timeCurrent - _lastTime < _gapSize) {
					//Interpolation
					double coefT = (float) (_markerPtr->getTargetTime() - _lastTime) / (timeCurrent - _lastTime);
					_val = interpolation(_valMoins1, _paramInput.get(index), coefT, _paramInput.getDim1(),_paramInput.getDim2());
				}
				else {
					_val << NotANumber();
				}
				_paramOutput->push(_val);
				_paramDataOutput->pushTime(_markerPtr->getTargetTime());
				_mem.clear();

				if(!_markerPtr->nextTarget())
					break;

				while (_markerPtr->getMarkerMaxTime() < timeCurrent)
				{
					if (timeCurrent - _lastTime < _gapSize) {
						//Interpolation
						double coefT = (float) (_markerPtr->getTargetTime() - _lastTime) / (timeCurrent - _lastTime);
						_val = interpolation(_valMoins1, _paramInput.get(index), coefT, _paramInput.getDim1(),_paramInput.getDim2());
					}
					else
						_val << NotANumber();
					_paramOutput->push(_val);
					_paramDataOutput->pushTime(_markerPtr->getTargetTime());
					if(!_markerPtr->nextTarget())
						break;
				}
				if ((timeCurrent >= _markerPtr->getMarkerMinTime()) && (timeCurrent < _markerPtr->getMarkerMaxTime())) {
					_mem.push_back(_paramInput.get(index));
				}
			}
			_lastTime = _paramInput.getTime(index);
			_valMoins1 = _paramInput.get(index);
		}
	}

	/**
	 * @brief compute and write new value in interpolation mode
	 */
	void writeInterpolation(ParamDataIndexInfo &pParamDataIndexInfo) {
		for (unsigned int index = pParamDataIndexInfo._startIndex;
				index
						< pParamDataIndexInfo._startIndex
								+ pParamDataIndexInfo._nbDataToProcess;
				index++) {

			double timeCurrent = _paramInput.getTime(index);

			//Fix  side-off effects - Left boundary - CONSTANTS
			if (_leftEffect) {
				while (_markerPtr->getTargetTime() <= timeCurrent) {
					if (abs(timeCurrent - _markerPtr->getTargetTime()) <= _gapSize) {
						_val = _paramInput.get(index);
					} else {
						_val << NotANumber();
					}
					_paramDataOutput->pushTime(_markerPtr->getTargetTime());
					_paramOutput->push(_val);
					if (!_markerPtr->nextTarget())
						break;
				}
				_leftEffect = false;
			} else {
				if (_markerPtr->getTargetTime() <= timeCurrent) {
					while (_markerPtr->getTargetTime() <= timeCurrent) {
						double deltaT = (float) (timeCurrent - _lastTime);
						if (deltaT <= _gapSize) {
							double coefT = (float) (_markerPtr->getTargetTime() - _lastTime) / deltaT;
							_val = interpolation(_valMoins1, _paramInput.get(index), coefT, _paramInput.getDim1(),_paramInput.getDim2());
						} else {
							_val << NotANumber();
						}
						_paramDataOutput->pushTime(_markerPtr->getTargetTime());
						_paramOutput->push(_val);
						if (!_markerPtr->nextTarget())
							break;
					}
				}
			}
			_lastTime = _paramInput.getTime(index);
			_valMoins1 = _paramInput.get(index);
		}
	}

	/**
	 * @overload ResamplingAbstract::ResamplingAbstract::init()
	 */
	virtual void init() {
		//Compute Sampling mode
		if (_useNearestValue)
		{
			_samplingMode = NEARESTVALUE;
			_nearestValue = ElementType();
			_nearestTime << NotANumber();
		}
		else
		{
			float minSampling = _paramInput.getMinSampling();

			if ((minSampling * 2) < getSampling()) {
				_samplingMode = AVERAGE;
			}
		}

		_isNewInt = true;
	}

	/**
	 * @overload ResamplingAbstract::reset(double pStartTime, double pTimeInt)
	 */
	virtual void resetResampling() {
		if (_currentTimeInterval == _timeIntervalList->end())
			return;

		// Get next TimeInteval;
		++_currentTimeInterval;
		++_currentTimeIntervalIndex;
		_samplingMode = INTERPOLATION;
		_lastTime = 0;
		_leftEffect = true;
		_valMoins1 << NotANumber();
		_val<< NotANumber();
		_mem.clear();

		init();
	}

	virtual double getSampling() = 0;

	/**
	 * @brief sampling mode
	 */
	enum SamplingMode {
		INTERPOLATION, AVERAGE, NEARESTVALUE
	};

private:

	/**
	 * @brief stored val to compute average in average mode
	 */
	ListValue _mem;

	MarkerAbstract* _markerPtr;
	
	TimeIntervalListSPtr _timeIntervalList;

	TimeIntervalList::iterator _currentTimeInterval;

	int _currentTimeIntervalIndex;

	/**<
	 * @brief current sampling mode
	 */
	SamplingMode _samplingMode;

	/**<
	 * @brief last computed value
	 */
	ElementType _val;

	/**
	 * @brief nearest time
	 */
	double _nearestTime;

	/**
	 * @brief nearest value
	 */
	ElementType _nearestValue;

	/**<
	 * @brief last input value
	 */
	ElementType _valMoins1;
	/**<
	 * @brief time of  last input value
	 */
	double _lastTime;

	/**
	 * @brief left effect indicator in interpolation mode
	 */
	bool _leftEffect;

	/**
	 * @brief real ParamData Output
	 */
	TParamData *_paramOutput;

	/**
	 * @brief real ParamData Input
	 */
	TParamData& _paramInput;

	/**
	 * @brief gap size for input data
	 */
	double _gapSize;
}
;

/**
 * Resampling implementation with a given sampling time , depand of paramData type
 */
template<typename TParamData>
class ResamplingWithSamplingTime : public ResamplingSimpleAbstract<TParamData> {

public:
	/**
	 * @brief  Constructor
	 */
	ResamplingWithSamplingTime(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList,
			double sampling, double gapSize, TParamData &param, bool  pUseNearestValue) :
			ResamplingSimpleAbstract<TParamData>(pProcess, &_marker, pTimeIntervalList,
						gapSize, param, pUseNearestValue), _samplingTime(sampling) {
	}

	/**
	 * @brief  Destructor
	 */
	virtual ~ResamplingWithSamplingTime() {
	}

	double getSampling() {
		return _samplingTime;
	}

private:
	MarkerSampling _marker;

	double _samplingTime;
};


/**
 * Resampling implementation with a given reference parameter for time definition , depand of paramData type
 */
template<typename TParamData>
class ResamplingWithRefParam: public ResamplingSimpleAbstract<TParamData> {

public:
	/**
	 * @brief  Constructor
	 */
	ResamplingWithRefParam(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList,
			TParamData &param, ParamData &refParamData, double gapSize, bool  pUseNearestValue) :
			ResamplingSimpleAbstract<TParamData>(pProcess, &_marker, pTimeIntervalList,
					gapSize, param, pUseNearestValue), _refParamData(refParamData) {
		_marker.setRefParamData(&refParamData);
	}

	/**
	 * @brief  Destructor
	 */
	virtual ~ResamplingWithRefParam() {
	}

	double getSampling() {
		return _refParamData.getMinSampling();
        }

private:
	MarkerRefParam _marker;

	ParamData& _refParamData;
};

/**
 * create the real Resampling implementation with visitor design pattern
 */
class CreateResampling: public VisitorOfParamData {
public:
	enum ResamplingType {
		RT_WITHSAMPLINGTIME,
		RT_WITHREFTIMELIST
	};

	/**
	 * @brief constructor
	 */
	CreateResampling(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList,
			double sampling, double gapSize, ParamData &paramData, bool useNearestValue) :
			_type(ResamplingType::RT_WITHSAMPLINGTIME),
			_process(pProcess), _paramData(paramData), _resampling(NULL), _timeIntervalList(
					pTimeIntervalList), _sampling(sampling), _gapSize(
					gapSize), _refParamData(paramData), _useNearestValue(useNearestValue) {
		_paramData.accept(*this);
	}

	/**
	 * @brief constructor
	 */
	CreateResampling(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList,
			ParamData &paramData, ParamData& refParamData, double gapSize, bool useNearestValue) :
			_type(ResamplingType::RT_WITHREFTIMELIST),
			_process(pProcess), _paramData(paramData), _resampling(NULL), _timeIntervalList(
					pTimeIntervalList), _sampling(0.), _gapSize(
					gapSize),_refParamData(refParamData), _useNearestValue(useNearestValue) {
		_paramData.accept(*this);
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataScalaireShort *)
	 */
	virtual void visit(ParamDataScalaireShort *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataScalaireShort>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataScalaireShort &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataScalaireShort>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataScalaireShort &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataScalaireFloat *)
	 */
	virtual void visit(ParamDataScalaireFloat *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataScalaireFloat>(_process,
							_timeIntervalList, _sampling, _gapSize,
							dynamic_cast<ParamDataScalaireFloat &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataScalaireFloat>(_process,
							_timeIntervalList,
							dynamic_cast<ParamDataScalaireFloat &>(_paramData),
							_refParamData,
							_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataScalaireDouble *)
	 */
	virtual void visit(ParamDataScalaireDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataScalaireDouble>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataScalaireDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataScalaireDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataScalaireDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataScalaireLongDouble *)
	 */
	virtual void visit(ParamDataScalaireLongDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataScalaireLongDouble>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataScalaireLongDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataScalaireLongDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataScalaireLongDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataScalaireInt *)
	 */
	virtual void visit(ParamDataScalaireInt *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataScalaireInt>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataScalaireInt &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataScalaireInt>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataScalaireInt &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataLogicalData *)
	 */
	virtual void visit(ParamDataLogicalData *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithSamplingTime<ParamDataLogicalData>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataLogicalData &>(_paramData), true);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithRefParam<ParamDataLogicalData>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataLogicalData &>(_paramData),
				_refParamData,
				_gapSize, true);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DShort *)
	 */
	virtual void visit(ParamDataTab1DShort *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DShort>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DShort &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab1DShort>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DShort &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DFloat *)
	 */
	virtual void visit(ParamDataTab1DFloat *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DFloat>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DFloat &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab1DFloat>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DFloat &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DDouble *)
	 */
	virtual void visit(ParamDataTab1DDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DDouble>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab1DDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DLongDouble *)
	 */
	virtual void visit(ParamDataTab1DLongDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DLongDouble>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DLongDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab1DLongDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DLongDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DInt *)
	 */
	virtual void visit(ParamDataTab1DInt *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DInt>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DInt &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab1DInt>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DInt &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab1DLogicalData *)
	 */
	virtual void visit(ParamDataTab1DLogicalData *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithSamplingTime<ParamDataTab1DLogicalData>(_process,
				_timeIntervalList, _sampling, _gapSize,
				dynamic_cast<ParamDataTab1DLogicalData &>(_paramData), true);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithRefParam<ParamDataTab1DLogicalData>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab1DLogicalData &>(_paramData),
				_refParamData,
				_gapSize, true);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DShort *)
	 */
	virtual void visit(ParamDataTab2DShort *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DShort>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DShort &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab2DShort>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab2DShort &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DFloat *)
	 */
	virtual void visit(ParamDataTab2DFloat *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DFloat>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DFloat &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab2DFloat>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab2DFloat &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DDouble *)
	 */
	virtual void visit(ParamDataTab2DDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DDouble>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab2DDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab2DDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DLongDouble *)
	 */
	virtual void visit(ParamDataTab2DLongDouble *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DLongDouble>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DLongDouble &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab2DLongDouble>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab2DLongDouble &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DInt *)
	 */
	virtual void visit(ParamDataTab2DInt *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DInt>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DInt &>(_paramData), _useNearestValue);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			_resampling = new ResamplingWithRefParam<ParamDataTab2DInt>(_process,
				_timeIntervalList,
				dynamic_cast<ParamDataTab2DInt &>(_paramData),
				_refParamData,
				_gapSize, _useNearestValue);
			break;
		}
	}

	/**
	 * @overload VisitorOfParamData::visit(ParamDataTab2DLogicalData *)
	 */
	virtual void visit(ParamDataTab2DLogicalData *) {
		switch (_type)
		{
		case ResamplingType::RT_WITHSAMPLINGTIME :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithSamplingTime<ParamDataTab2DLogicalData>(_process,
					_timeIntervalList, _sampling, _gapSize,
					dynamic_cast<ParamDataTab2DLogicalData &>(_paramData), true);
			break;
		case ResamplingType::RT_WITHREFTIMELIST :
			// !! Force the use of the nearest value
			_resampling = new ResamplingWithRefParam<ParamDataTab2DLogicalData>(_process,
					_timeIntervalList,
					dynamic_cast<ParamDataTab2DLogicalData &>(_paramData),
					_refParamData,
					_gapSize, true);
                        break;
                }
	}

	/**
	 * @brief Good resampling getter
	 */
	ResamplingAbstract* getResampling() const {
		return _resampling;
	}
private:
	ResamplingType _type;
	Process &_process;
	ParamData &_paramData;
	ResamplingAbstract *_resampling;
	TimeIntervalListSPtr _timeIntervalList;
	double _sampling;
	double _gapSize;
	ParamData& _refParamData;
	bool _useNearestValue;
};

} /* Resampling */
} /* AMDA */
} /* Parameters */

#endif /* Resampling_HH_ */