/*
 * PlotOutput.cc
 *
 *  Created on: 28 oct. 2013
 *      Author: CS
 */

#include "PlotOutput.hh"
#include "PlotLogger.hh"
#include <fstream>
#include "PlotLogger.hh"
#include "LayoutAuto.hh"
#include "LayoutVertical.hh"
#include "TimePlotNode.hh"
#include "Time/TimePlot.hh"

#include <boost/range/adaptor/reversed.hpp>

namespace plot {

PlotOutput::PlotOutput(AMDA::Parameters::ParameterManager& pParameterManager) :
		AMDA::Parameters::VisitorOfParamData(), ParamOutput(pParameterManager),
		_writeContextFile(false), _currentParamId(""), _outputStructure(OutputStructure::ONE_FILE_PER_INTERVAL), _filePrefix("plot") {

}

PlotOutput::~PlotOutput()
{

}

/**
 * @overload DataClient::establishConnection()
 */
void PlotOutput::establishConnection()
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::establishConnection");
	//create all needed parameters
	for (auto plot : _plots)
		plot->createParameters(_usedParametersId);

	//open connection for all needed parameters
	for (auto paramId : _usedParametersId)
	{
		LOG4CXX_DEBUG(gLogger,"PlotOutput::establishConnection - " << paramId);
		_parameterManager.getParameter(paramId)->openConnection(this);
	}
}

/**
 * @overload ParamOutput::init()
 */
void PlotOutput::init()
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::init");
	//init all needed parameters
	for (auto paramId : _usedParametersId)
	{
		try {
			//init parameter
			LOG4CXX_DEBUG(gLogger,"PlotOutput::init - " << paramId);
			_parameterManager.getParameter(paramId)->init(this, _timeIntervalList);
		} catch (...) {
			LOG4CXX_ERROR(_logger,
					"PlotOutput::init parameter : \""<< paramId <<"\" Error");
			throw;
		}
	}
}

/**
 * Gets parameter value from server and stores them into dedicated
 * structure.
 */
void PlotOutput::getDataFromServer()
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::getDataFromServer");
	// request data from server
	// get data and call visitor to store them into dedicated structure
	for (auto paramId : _usedParametersId)
	{
		try {
			_currentParamId = paramId;

			LOG4CXX_DEBUG(gLogger,"PlotOutput::getDataFromServer - " << paramId);

			do {
				_paramDataIndexInfo = _parameterManager.getParameter(paramId)->getAsync(this).get();
				 _parameterManager.getParameter(paramId)->getParamData(this)->accept(*this);
			} while (!_paramDataIndexInfo._noMoreTimeInt && !_paramDataIndexInfo._timeIntToProcessChanged);
		}catch (...) {
			LOG4CXX_ERROR(gLogger,
				"apply Error.\nCannot get or write data for parameter: \""
					+ paramId + "\".");
			throw;
		}
	}

	for (std::map<std::string, ParameterData>::iterator it =  _parameterValues.begin(); it != _parameterValues.end(); ++it) {
		LOG4CXX_DEBUG(gLogger,"PlotOutput::getDataFromServer - " << it->first <<  " - " << it->second.getSize());
	}
}

/**
 * @overload ParamOutput::apply()
 */
void PlotOutput::apply() {
	LOG4CXX_DEBUG(gLogger,"PlotOutput::apply");
	_currentTimeInterval = _timeIntervalList->begin();

	_files.clear();

	if (_timeIntervalList->empty())
	{
		LOG4CXX_DEBUG(gLogger,"PlotOutput::apply - Nothing to plot");
		return;
	}

	//Init context file if needed
	if (_writeContextFile)
	{
		std::stringstream contextFileName;
		contextFileName << _filePrefix << "_context.xml";
		_contextWriter.initWriter(contextFileName.str().c_str());
	}

	//set time intervals list to each plot
	for (auto plot : _plots)
		plot->setTimeIntervalListPtr(_timeIntervalList.get());

	if (_page->_superposeMode == false)
		drawOneIntervalByPage();
	else
		drawAllIntervalsInOnePage();

	_pls.reset();

	//reset context writer
	if (_writeContextFile)
		_contextWriter.closeWriter();

	// reset iterator on intervals
	_currentTimeInterval = _timeIntervalList->begin();
}

/**
 * @brief Init new page - Create also the new file if necessary
 *
 */
bool PlotOutput::initNewPage(int intervalIndex, std::string& ttName)
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::initNewPage");
	std::stringstream plotFilePrefix;

	//create new plplot stream for new file :
	// * if ONE_FILE : one file for all time interval
	// * if ONE_FILE_PER_INTERVAL : one file for one time interval
	bool newFile = ((_pls == nullptr) || (_outputStructure == OutputStructure::ONE_FILE_PER_INTERVAL));
	if (newFile)
		_pls.reset(new plstream());

	//set file prefix
	plotFilePrefix.str("");
	if ((_timeIntervalList->size() > 1) &&
		(_outputStructure == OutputStructure::ONE_FILE_PER_INTERVAL) &&
		!_page->_superposeMode)
		plotFilePrefix << _filePrefix << "_" << ttName << "_" << intervalIndex << "_";
	else
		plotFilePrefix << _filePrefix << "_";

	//draw page
	_page->draw(_pls, newFile, plotFilePrefix.str().c_str());

	return newFile;
}

/*
 * @brief Sequence to draw one interval by page
 */
void PlotOutput::drawOneIntervalByPage()
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::drawOneIntervalByPage");
	while (_currentTimeInterval != _timeIntervalList->end())
	{
		//get parameters data
		getDataFromServer();

		//init the page
		bool newFile = initNewPage(_currentTimeInterval->_index, _currentTimeInterval->_ttName);

		// Compute panel position depending on the page layout
		computePanelBounds();

		// Fix time axes legend visibility in relation with the request
		fixPanelTimeAxesVisibility();

		// Initialize panel plot
		for (auto plot : _plots) {
			// set current plplot stream for plot,
			plot->setPlStream(_pls);
			// set link to parameters data
			plot->setParameterValues(&_parameterValues);
			// prepare plot area for each panel
			plot->preparePlotArea(_currentTimeInterval->_startTime, _currentTimeInterval->_stopTime, _currentTimeInterval->_index);
		}

		// Compute and set panel plot area position depending on the panel constraints
		computePanelPlotAreaBounds();

		// Compute and set left axis tickmark width depending on the panel constraints
		computePanelLegendPosition();

		//write page context
		if (_writeContextFile)
		{
			_contextWriter.startElement("page");
			_contextWriter.addAttribute("startTime",std::to_string(_currentTimeInterval->_startTime).c_str());
			_contextWriter.addAttribute("stopTime",std::to_string(_currentTimeInterval->_stopTime).c_str());
			_contextWriter.addAttribute("superposeMode", "false");
			if (!_currentTimeInterval->_ttName.empty())
			{
				_contextWriter.addAttribute("ttName",_currentTimeInterval->_ttName.c_str());
				_contextWriter.addAttribute("ttIndex",std::to_string(_currentTimeInterval->_index).c_str());
				_contextWriter.addAttribute("ttNbIntervals", std::to_string(_currentTimeInterval->_ttTotalIntervals).c_str());
			}
			_page->writeContext(_contextWriter);
		}

		//Draw all PanelPlot -
		for (auto plot : _plots) {
			//draw the panel plot output
			bool dataPloted = plot->draw(
					_currentTimeInterval->_startTime,
					_currentTimeInterval->_stopTime,
					_currentTimeInterval->_index,
					//is first intervals?
					_currentTimeInterval == _timeIntervalList->begin(),
					//is second intervals?
					_currentTimeInterval == std::prev(_timeIntervalList->end()));
			if (!dataPloted) {
				plot->_panel->drawNoData(_pls);
			}
			//write plot context
			if (_writeContextFile)
				plot->writeContext(_contextWriter, _currentTimeInterval);
			//prepare for the next time interval
			plot->resetPlot();
		}

		if (_writeContextFile)
			_contextWriter.endElement(); // page

		//reset all data
		for (auto paramId : _usedParametersId)
			_parameterValues[paramId].reset();

		//add file to file list for post processing
		if (newFile)
			_files.push_back(_page->_fileName);

		//go to next interval
		++_currentTimeInterval;
	}
}

void PlotOutput::drawAllIntervalsInOnePage()
{
	LOG4CXX_DEBUG(gLogger,"PlotOutput::drawAllIntervalsInOnePage");
	/*
	 * First step : init page, panel and plot area
	 */

	double globalStartTime = _timeIntervalList->front()._startTime;
	double globalStopTime  = _timeIntervalList->back()._stopTime;

	//get all data for all intervals
	while (_currentTimeInterval != _timeIntervalList->end())
	{
		//get parameters data
		getDataFromServer();

		//go to next interval
		++_currentTimeInterval;
	}

	//init the page
	initNewPage(0,_timeIntervalList->begin()->_ttName);

	//Compute panel position depending on the page layout
	computePanelBounds();

	// Fix time axes legend visibility in relation with the request
	fixPanelTimeAxesVisibility();

	//Initialize panel plot
	for (auto plot : _plots)
	{
		// set current plplot stream for plot,
		plot->setPlStream(_pls);
		// set link to parameters data
		plot->setParameterValues(&_parameterValues);
		// prepare plot area for each panel
		plot->preparePlotArea(globalStartTime, globalStopTime, 0);
	}

	// Compute and set panel plot area position depending on the panel constraints
	computePanelPlotAreaBounds();

	// Compute and set left axis tickmark width depending on the panel constraints
	computePanelLegendPosition();

	//write page context
	if (_writeContextFile)
	{
		_contextWriter.startElement("page");
		_contextWriter.addAttribute("startTime",std::to_string(_currentTimeInterval->_startTime).c_str());
		_contextWriter.addAttribute("stopTime",std::to_string(_currentTimeInterval->_stopTime).c_str());
		_contextWriter.addAttribute("superposeMode", "true");
		_page->writeContext(_contextWriter);
	}

	/*
	 * Second step : Draw plot for each intervals
	 */
	_currentTimeInterval = _timeIntervalList->begin();

	bool dataPloted[_plots.size()];
	while (_currentTimeInterval != _timeIntervalList->end())
	{
		//Draw all PanelPlot -
		int plotIndex = 0;
		for (auto plot : _plots)
		{
			if (_currentTimeInterval == _timeIntervalList->begin()) {
				dataPloted[plotIndex] = false;
			}
			//draw the panel plot output
			bool dataPlotedForInt = plot->draw(
					_currentTimeInterval->_startTime,
					_currentTimeInterval->_stopTime,
					_currentTimeInterval->_index,
					//is first interval?
					_currentTimeInterval == _timeIntervalList->begin(),
					//is second interval?
					_currentTimeInterval == std::prev(_timeIntervalList->end()));
			if (!dataPloted[plotIndex])
				dataPloted[plotIndex] = dataPlotedForInt;
			if (_writeContextFile && (_currentTimeInterval == _timeIntervalList->begin()))
				//write plot context
				plot->writeContext(_contextWriter, _currentTimeInterval);
			++plotIndex;
		}

		//go to next interval
		++_currentTimeInterval;
	}

	if (_writeContextFile)
		_contextWriter.endElement(); /* page */

	/*
	 * Third step : reset plots and data and prepare for post processing
	 */
	int plotIndex = 0;
	for (auto plot : _plots) {
		if (!dataPloted[plotIndex]) {
			plot->_panel->drawNoData(_pls);
		}
		plot->resetPlot();
		++plotIndex;
	}

	//reset all data
	for (auto paramId : _usedParametersId)
		_parameterValues[paramId].reset();

	//add file to file list for post processing
	_files.push_back(_page->_fileName);
}

/**
 * @brief Gets a list of plots on the same panel
 */
std::vector<boost::shared_ptr<PanelPlotOutput>> PlotOutput::getPlots(Panel* panel_){
	std::vector<boost::shared_ptr<PanelPlotOutput>> plots;
	for(auto plot : _plots){
		if(plot->_panel->_id == panel_->_id){
			plots.push_back(plot);
		}
	}
	return plots;
}

void PlotOutput::computePanelBounds(void) {
	// Nothing to plot -> nothing to compute !
	// Nothing to compute if layout type is manual
	if ((_plots.empty() == true) ||
		(_page->_layoutProperties.getType() == LayoutType::MANUAL))	{
		return;
	}

	LOG4CXX_DEBUG(gLogger,"PlotOutput::computePanelBounds...");

	// Compute page XY ratio
	std::tuple<float, float> pageSizeInMm = _page->getSizeInMm();

	double xyRatio 	= std::get<0>(pageSizeInMm) / std::get<1>(pageSizeInMm);

	// Build a new layout depending on the layout type
	Layout *pLayout;
	if (_page->_layoutProperties.getType() == LayoutType::AUTO) {
		pLayout = new LayoutAuto (
					_page->_layoutProperties.getPanelHeight(),
					_page->_layoutProperties.getPanelSpacing(),
					_page->_layoutProperties.getFirstPanelHeightFactor(),
					_page->_layoutProperties.isExpand(),
					xyRatio,
					_page->_layoutProperties.isOnlyLowerTimeAxesLegend());
	} else {
		pLayout = new LayoutVertical (
					_page->_layoutProperties.getPanelHeight(),
					_page->_layoutProperties.getPanelSpacing(),
					_page->_layoutProperties.getFirstPanelHeightFactor(),
					_page->_layoutProperties.isExpand(),
					xyRatio,
					_page->_layoutProperties.isOnlyLowerTimeAxesLegend());
	}

	// Compute panel bounds depending on the constraints
	pLayout->computePanelsPosition (_plots);

	delete pLayout;
}

void PlotOutput::computePanelPlotAreaBounds(void) {
	if ((_plots.empty() == true))	{
		// Nothing to plot -> nothing to compute
		return;
	}

	if (_page->_layoutProperties.getType() == LayoutType::MANUAL) {
		//Specific treatment for manual layout
		computePanelPlotAreaBoundsForManualLayout();
		return;
	}

	LOG4CXX_DEBUG(gLogger,"PlotOutput::computePanelPlotAreaBounds...");

	// Retrieve plotarea minimal dimensions for each (MaxWidth constraint) panels

	double plotAreaMinX = 0.0, plotAreaMaxX = 1.0;
	Bounds plotAreaBounds;
	for (auto plot : _plots) {
		if (plot->getLayoutConstraint() == PanelConstraint::MaxWidth) {
			plot->getPlotAreaBounds(plotAreaBounds);

			if (plotAreaBounds._x > plotAreaMinX)
				plotAreaMinX = plotAreaBounds._x;

			if ((plotAreaBounds._x + plotAreaBounds._width) < plotAreaMaxX)
				plotAreaMaxX = (plotAreaBounds._x + plotAreaBounds._width);
		}
	}

	// Force plot area position for panels with MaxWidth constraint
	for (auto plot : _plots) {
		if (plot->getLayoutConstraint() == PanelConstraint::MaxWidth) {
			plot->forcePlotAreaPosAndWidth(plotAreaMinX, plotAreaMaxX - plotAreaMinX);
		}
	}

	// Fix legends alignment
	double leftMax, rightMax;
	leftMax << NotANumber();
	rightMax << NotANumber();
	for (auto plot : _plots) {
		if (plot->typeName() != TIMEPLOT_NODENAME)
			continue;
		for (Axes::iterator it = plot->_panel->_axes.begin(); it != plot->_panel->_axes.end(); ++it) {
			boost::shared_ptr<Axis> lAxis = it->second;

			if (lAxis == nullptr)
				continue;

			if (!lAxis->_visible || !lAxis->_used)
				continue;

			switch (lAxis->_position) {
				case PlotCommon::Position::POS_LEFT:
					if (isNAN(leftMax))
						leftMax = lAxis->getLegendOffset();
					else
						leftMax = std::max(leftMax, lAxis->getLegendOffset());
					break;
				case PlotCommon::Position::POS_RIGHT:
					if (isNAN(rightMax))
						rightMax = lAxis->getLegendOffset();
					else
						rightMax = std::max(rightMax, lAxis->getLegendOffset());
					break;
				default:
					//Nothing to do
					break;
			}
		}
	}

	for (auto plot : _plots) {
		if (plot->typeName() != TIMEPLOT_NODENAME)
			continue;
		for (Axes::iterator it = plot->_panel->_axes.begin(); it != plot->_panel->_axes.end(); ++it) {
			boost::shared_ptr<Axis> lAxis = it->second;

			if (lAxis == nullptr)
				continue;

			if (!lAxis->_visible || !lAxis->_used)
				continue;

			switch (lAxis->_position) {
				case PlotCommon::Position::POS_LEFT:
					if (!isNAN(leftMax))
						lAxis->setLegendOffset(leftMax);
					break;
				case PlotCommon::Position::POS_RIGHT:
					if (!isNAN(rightMax))
						lAxis->setLegendOffset(rightMax);
					break;
				default:
					//Nothing to do
					break;
			}
		}
	}
}

void PlotOutput::computePanelPlotAreaBoundsForManualLayout(void) {
	// Nothing to plot -> nothing to compute !
	// Can be used only for a plot with manual layout
	if ((_plots.empty() == true) ||
		(_page->_layoutProperties.getType() != LayoutType::MANUAL))	{
		return;
	}

	LOG4CXX_DEBUG(gLogger,"PlotOutput::computePanelPlotAreaBoundsForManualLayout...");

	//Regroup panels by left positions (only for timePlot)
	std::map<double, std::vector<boost::shared_ptr<PanelPlotOutput>>> leftPanelsPosList;
	for (auto plot : _plots) {
		if (plot->typeName() != TIMEPLOT_NODENAME)
			continue;
		leftPanelsPosList[plot->_panel->_bounds._x].push_back(plot);
	}

	//Align plot area
	Bounds plotAreaBounds;
	for (auto leftPanelPos : leftPanelsPosList) {
		double plotAreaLeftPos = 0;
		double plotAreaRightPos = 1;
		for (auto plot : leftPanelPos.second) {
			plot->getPlotAreaBounds(plotAreaBounds);

			if (plotAreaBounds._x > plotAreaLeftPos)
				plotAreaLeftPos = plotAreaBounds._x;

			if ((plotAreaBounds._x + plotAreaBounds._width) < plotAreaRightPos)
				plotAreaRightPos = (plotAreaBounds._x + plotAreaBounds._width);

		}

		for (auto plot : leftPanelPos.second) {
			plot->getPlotAreaBounds(plotAreaBounds);
			plot->forcePlotAreaPosAndWidth(plotAreaLeftPos, plotAreaRightPos - plotAreaLeftPos);
		}
	}
}

void PlotOutput::fixPanelTimeAxesVisibility(void) {
	if ((_plots.empty() == true) ||
		(_page->_layoutProperties.getType() == LayoutType::MANUAL) ||
		!_page->_layoutProperties.isOnlyLowerTimeAxesLegend())     {
		return;
	}

	LOG4CXX_DEBUG(gLogger,"PlotOutput::fixPanelTimeAxesVisibility...");

	bool isFirstTimePlot = true;
	for (auto plot : boost::adaptors::reverse(_plots)) {
		if (plot->typeName() != TIMEPLOT_NODENAME) {
			continue;
		}
		if (!plot->isStandalone()) {
			continue;
		}
		if (isFirstTimePlot) {
			isFirstTimePlot = false;
			continue;
		}
		TimePlot* timePlot = reinterpret_cast<TimePlot*>(plot.get());
		TimeAxis* timeAxis = timePlot->getTimeAxis();
		timeAxis->setShowLegend(false);
		timeAxis->setShowTickMark(false);
	}
}

void PlotOutput::computePanelLegendPosition(void) {
	// Nothing to plot -> nothing to compute !
	if ((_plots.empty() == true))	{
		return;
	}

	LOG4CXX_DEBUG(gLogger,"PlotOutput::computePanelLegendPosition...");

	// Retrieve left axis tickmark size for each (MaxWidth constraint) panels

	int maxLeftAxisTickMarkWitdh = 0;
	int leftAxisTickMarkWitdh = 0;
	for (auto plot : _plots) {
		//if manual layout => only apply on timePlots
		if ((_page->_layoutProperties.getType() == LayoutType::MANUAL) && (plot->typeName() != TIMEPLOT_NODENAME))
			continue;
		if (plot->getLayoutConstraint() == PanelConstraint::MaxWidth) {
			leftAxisTickMarkWitdh = plot->getLeftAxisTickMarkWidth();

			if (leftAxisTickMarkWitdh > maxLeftAxisTickMarkWitdh)
				maxLeftAxisTickMarkWitdh = leftAxisTickMarkWitdh;
		}
	}
	// Force plot left axis tickmark max width for panels with MaxWidth constraint
	for (auto plot : _plots) {
		//if manual layout => only apply on timePlots
		if ((_page->_layoutProperties.getType() == LayoutType::MANUAL) && (plot->typeName() != TIMEPLOT_NODENAME))
			continue;
		if (plot->getLayoutConstraint() == PanelConstraint::MaxWidth) {
			plot->forceLeftAxisTickMarkWidth(maxLeftAxisTickMarkWitdh);
		}
	}
}

/**
 * @brief Get the list of indexes used for a vector parameter
 */
template<typename Type>
std::vector<AMDA::Common::ParameterIndexComponent> PlotOutput::getParamUsedIndexes(std::string paramId, AMDA::Parameters::ParamDataSpec<std::vector<Type>>* pParamData)
{
	std::vector<AMDA::Common::ParameterIndexComponent> indexes;

	for (auto plot : _plots) {
		std::vector<AMDA::Common::ParameterIndexComponent> plotIndexes =
				plot->getParamUsedIndexes(paramId,pParamData->get(_paramDataIndexInfo._startIndex).size());

		for (auto index : plotIndexes)
		{
			//push indexes
			if (std::find(indexes.begin(),indexes.end(),index) != indexes.end())
				continue;
			indexes.push_back(index);
		}
	}

	if (indexes.empty())
		indexes.push_back(AMDA::Common::ParameterIndexComponent(-1,-1));

	if (std::find(indexes.begin(),indexes.end(),AMDA::Common::ParameterIndexComponent(-1,-1)) != indexes.end())
	{
		indexes.clear();
		for (unsigned int i = 0; i < pParamData->get(_paramDataIndexInfo._startIndex).size(); ++i)
			indexes.push_back(AMDA::Common::ParameterIndexComponent(i));
	}

	_parameterValues[paramId].setDim1Size(pParamData->get(_paramDataIndexInfo._startIndex).size());

	return indexes;
}

/**
 * @brief Get the list of indexes used for a Tab2D parameter
 */
template<typename Type>
std::vector<AMDA::Common::ParameterIndexComponent> PlotOutput::getParamUsedIndexes(std::string paramId, AMDA::Parameters::ParamDataSpec<AMDA::Parameters::Tab2DData<Type>>* pParamData)
{
	std::vector<AMDA::Common::ParameterIndexComponent> indexes;

	for (auto plot : _plots) {
		std::vector<AMDA::Common::ParameterIndexComponent> plotIndexes = plot->getParamUsedIndexes(paramId,
				pParamData->get(_paramDataIndexInfo._startIndex).getDim1Size(),
				pParamData->get(_paramDataIndexInfo._startIndex).getDim2Size());

		for (auto index : plotIndexes)
		{
			if (std::find(indexes.begin(),indexes.end(),index) != indexes.end())
				continue;
			indexes.push_back(index);
		}
	}

	if (indexes.empty())
		indexes.push_back(AMDA::Common::ParameterIndexComponent(-1,-1));

	if (std::find(indexes.begin(),indexes.end(),AMDA::Common::ParameterIndexComponent(-1,-1)) != indexes.end())
	{
		indexes.clear();
		for (int i = 0; i < pParamData->get(_paramDataIndexInfo._startIndex).getDim1Size(); ++i)
			for (int j = 0; j < pParamData->get(_paramDataIndexInfo._startIndex).getDim2Size(); ++j)
				indexes.push_back(AMDA::Common::ParameterIndexComponent(i,j));
	}

	_parameterValues[paramId].setDim1Size(pParamData->get(_paramDataIndexInfo._startIndex).getDim1Size());
	_parameterValues[paramId].setDim2Size(pParamData->get(_paramDataIndexInfo._startIndex).getDim2Size());

	return indexes;
}

/**
 * @brief Get the list of indexes used for a parameter
 */
template<class ParamData>
double PlotOutput::getParamGapSize(std::string paramId, ParamData* pParamData)
{
	try {
		AMDA::Parameters::ParameterSPtr crtParam = _parameterManager.getParameter(paramId);
		return _parameterManager.getComputedGapSize(crtParam->getGapThreshold(),
			pParamData->getMinSampling());
	} catch(...) {
		LOG4CXX_ERROR(gLogger,
				"apply Error.\nCannot get parameter gap size: \""
					+ paramId + "\".");
		throw;
	}
}

/***************************** VISITORS ********************************/

template<typename Type>
void PlotOutput::scalarVisit(AMDA::Parameters::ParamDataSpec<Type> * pParamData) {
	double gapSize = getParamGapSize(_currentParamId, pParamData);
	bool gapDetected = false;

	ParameterData& crtParameterData = _parameterValues[_currentParamId];
	crtParameterData.setParamGapSize(gapSize);

	if (_paramDataIndexInfo._nbDataToProcess == 0)
		return;

	AMDA::Common::ParameterIndexComponent componentIndex = AMDA::Common::ParameterIndexComponent(-1,-1);

	if (crtParameterData._indexes.size() == 0) {
		crtParameterData.setDim1Size(1);
	}

	crtParameterData.preAllocate(_paramDataIndexInfo._nbDataToProcess + _paramDataIndexInfo._startIndex);

	for (unsigned int index = _paramDataIndexInfo._startIndex; index < _paramDataIndexInfo._nbDataToProcess + _paramDataIndexInfo._startIndex; ++index) {
		crtParameterData.addTime(pParamData->getTime(index), pParamData->getMinSampling(), gapDetected);
		crtParameterData.addValue(isNAN(pParamData->get(index)) ? NAN: (double)pParamData->get(index), componentIndex, gapDetected);
	}
}

template<typename Type>
void PlotOutput::vectorVisit(AMDA::Parameters::ParamDataSpec<std::vector<Type>> * pParamData) {
	double gapSize = getParamGapSize(_currentParamId, pParamData);
	bool gapDetected = false;

	ParameterData& crtParameterData = _parameterValues[_currentParamId];
	crtParameterData.setParamGapSize(gapSize);

	if (_paramDataIndexInfo._nbDataToProcess == 0)
		return;

	//init parameter values container
	if (crtParameterData._indexes.size() == 0)
		crtParameterData._indexes =
			getParamUsedIndexes<Type>(_currentParamId,pParamData);

	crtParameterData.preAllocate(_paramDataIndexInfo._nbDataToProcess
		+ _paramDataIndexInfo._startIndex);

	for (unsigned int i = _paramDataIndexInfo._startIndex; i < _paramDataIndexInfo._nbDataToProcess + _paramDataIndexInfo._startIndex; ++i) {
		double time = pParamData->getTime(i);
		crtParameterData.addTime(time, pParamData->getMinSampling(), gapDetected);
		for (auto& index : crtParameterData._indexes) {
			crtParameterData.addValue(
				isNAN(pParamData->get(i)[index.getDim1Index()]) ? NAN: (double)pParamData->get(i)[index.getDim1Index()],
				index, gapDetected);
		}
	}
}

template<typename Type>
void PlotOutput::tab2DVisit(AMDA::Parameters::ParamDataSpec<AMDA::Parameters::Tab2DData<Type> > * pParamData) {
	double gapSize = getParamGapSize(_currentParamId, pParamData);
	bool gapDetected = false;

	ParameterData& crtParameterData = _parameterValues[_currentParamId];
	crtParameterData.setParamGapSize(gapSize);

	if (_paramDataIndexInfo._nbDataToProcess == 0)
		return;

	//init parameter values container
	if (crtParameterData._indexes.size() == 0)
		crtParameterData._indexes =
			getParamUsedIndexes<Type>(_currentParamId,pParamData);

	crtParameterData.preAllocate(_paramDataIndexInfo._nbDataToProcess
		+ _paramDataIndexInfo._startIndex);

	for (unsigned int i = _paramDataIndexInfo._startIndex; i < _paramDataIndexInfo._nbDataToProcess + _paramDataIndexInfo._startIndex; ++i) {
		double time = pParamData->getTime(i);
		crtParameterData.addTime(time, pParamData->getMinSampling(), gapDetected);
		for (auto& index : crtParameterData._indexes) {
			crtParameterData.addValue(
				isNAN(pParamData->get(i)[index.getDim1Index()][index.getDim2Index()]) ? NAN: (double)pParamData->get(i)[index.getDim1Index()][index.getDim2Index()],
				index, gapDetected);
		}
	}
}

/**
 * @overload VisitorOfParamData::visit(ParamDataScalaireShort *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataScalaireShort * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataScalaireFloat *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataScalaireFloat * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataScalaireDouble *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataScalaireDouble * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataScalaireLongDouble *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataScalaireLongDouble * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataScalaireInt *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataScalaireInt * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataLogicalData *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataLogicalData * pParamData) {
	this->scalarVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DShort *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab1DShort *pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DFloat *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataTab1DFloat * pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DDouble *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataTab1DDouble *pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DLongDouble *)
 */
void PlotOutput::visit(
		AMDA::Parameters::ParamDataTab1DLongDouble *pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DInt *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab1DInt *pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab1DLogicalData *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab1DLogicalData *pParamData) {
	this->vectorVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DShort *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DShort *pParamData) {
	this->tab2DVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DFloat *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DFloat *pParamData) {
	this->tab2DVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DDouble *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DDouble *pParamData) {
	this->tab2DVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DLongDouble *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DLongDouble *pParamData) {
	this->tab2DVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DInt *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DInt *pParamData) {
	this->tab2DVisit(pParamData);
}

/**
 * @overload VisitorOfParamData::visit(ParamDataTab2DLogicalData *)
 */
void PlotOutput::visit(AMDA::Parameters::ParamDataTab2DLogicalData *pParamData) {
	this->tab2DVisit(pParamData);
}

} /* namespace plot */