/*
 * LocalParamData.hh
 *
 *  Created on: Nov 24, 2014
 *      Author: AKKA
 */


#ifndef LOCALPARAMDATA_H_
#define LOCALPARAMDATA_H_

#include "LocalFileInterfaceConfig.hh"

#include <cstring>
#include <cstdint>

namespace AMDA {
namespace LocalFileInterface {

/*
 * @brief Time format
 */
typedef enum {ISO, DOUBLE} LocalTimeFormat;

/*
 * @brief Container type - CONTAINER_MATRIX not tested
 */
enum LocalContainerType
{
	CONTAINER_SCALAR,
	CONTAINER_VECTOR,
	CONTAINER_MATRIX
};

/*
 * @brief Param type
 */
enum LocalParamType
{
	TYPE_UNKNOWN,
	TYPE_FLOAT,
	TYPE_DOUBLE,
	TYPE_EPOCH16,
	TYPE_SHORT,
	TYPE_INT,
	TYPE_TT2000
};

/*
 * @brief Define the maximum dimension size
 */
#define PARAMPACKET_MAX_DIMSIZE   10000

/*
 * @brief Define the maximum bytes available for one packet
 */
#define PARAMPACKET_MAX_DATABYTES 80000

/*
 * @brief Define a packet to push to a ParamData
 */
class LocalParamDataPacket
{
public:
	/*
	 * @brief Constructor
	 */
	LocalParamDataPacket(void) : _isInit(false), _containerType(CONTAINER_SCALAR),
		_paramType(TYPE_UNKNOWN), _nbData(0), _nbMaxData(0), _dims(NULL), _size(0),
		_times(NULL), _datas(NULL), _noData(false), _startTime(0.), _stopTime(0.)
	{
	}

	/*
	 * @brief Destructor
	 */
	~LocalParamDataPacket(void)
	{
		//free allocated data if needed
		free();
	}

	/*
	 * @brief Init the packet. This function allocate all buffers used by a packet
	 */
	bool init(LocalContainerType containerType,
			LocalParamType paramType, int dim1 = 0, int dim2 = 0)
	{
		if (_isInit)
			//already init
			return false;

		_containerType = containerType;
		_paramType     = paramType;
		_size          = 0;

		//compute dimSize
		switch (_containerType)
		{
		case CONTAINER_SCALAR :
			//for a scalar, no dims defined and the size is 1
			_size = 1;
			_dims = NULL;
			break;
		case CONTAINER_VECTOR :
			//for a vector, 1 "dims" defined and the size is egal to this dimension size
			if (dim1 <= 0)
				return false;
			_size = dim1;
			_dims = new int[1];
			_dims[0] = dim1;
			break;
		case CONTAINER_MATRIX :
			//for a matrix, 2 "dims" defined and the size is egal to the multiplication of the two dimensions sizes
			if ((dim1 <= 0) || (dim2 <= 0))
				return false;
			_size = dim1 * dim2;
			_dims = new int[2];
			_dims[0] = dim1;
			_dims[1] = dim2;
			break;
		default:
			return false;
		}

		//check dimSize
		if ((_size <= 0) || (_size >= PARAMPACKET_MAX_DIMSIZE))
		{
			if (_dims != NULL)
			{
				delete[] _dims;
				_dims = NULL;
			}
			return false;
		}

		//init times and datas
		//use the PARAMPACKET_MAX_DATABYTES to determine the maximum data that's a packet can contain
		_nbData = 0;
		switch (_paramType)
		{
		case TYPE_FLOAT :
			_nbMaxData = PARAMPACKET_MAX_DATABYTES / (_size * sizeof(float));
			if (_nbMaxData < 1)
				_nbMaxData = 1;
			_times = new double[_nbMaxData];
			_datas = new float[_nbMaxData*_size];
			break;
		case TYPE_DOUBLE :
			_nbMaxData = PARAMPACKET_MAX_DATABYTES / (_size * sizeof(double));
			if (_nbMaxData < 1)
				_nbMaxData = 1;
			_times = new double[_nbMaxData];
			_datas = new double[_nbMaxData*_size];
			break;
		case TYPE_SHORT :
			_nbMaxData = PARAMPACKET_MAX_DATABYTES / (_size * sizeof(short));
			if (_nbMaxData < 1)
				_nbMaxData = 1;
			_times = new double[_nbMaxData];
			_datas = new short[_nbMaxData*_size];
			break;
		case TYPE_INT :
			_nbMaxData = PARAMPACKET_MAX_DATABYTES / (_size * sizeof(int));
			if (_nbMaxData < 1)
				_nbMaxData = 1;
			_times = new double[_nbMaxData];
			_datas = new int[_nbMaxData*_size];
			break;
		default :
			if (_dims != NULL)
			{
				delete[] _dims;
				_dims = NULL;
			}
			return false;
		}

		_isInit = true;
		return true;
	}

	/*
	 * @brief Free all allocated buffers. This function is called in the destructor
	 */
	void free()
	{
		if (!_isInit)
			return;

		//free all allocated buffers
		if (_dims != NULL)
		{
			delete[] _dims;
			_dims = NULL;
		}

		if (_times != NULL)
		{
			delete[] _times;
			_times = NULL;
		}

		if (_datas != NULL)
		{
			switch (_paramType)
			{
			case TYPE_FLOAT :
				delete[] (float*)_datas;
				_datas = NULL;
				break;
			case TYPE_DOUBLE :
				delete[] (double*)_datas;
				_datas = NULL;
				break;
			case TYPE_SHORT :
				delete[] (short*)_datas;
				_datas = NULL;
				break;
			case TYPE_INT :
				delete[] (int*)_datas;
				_datas = NULL;
				break;
			default :
				throw;
			}
		}

		_isInit = false;
	}

	/*
	 * @brief Add one record (a pair of one time and a data)
	 * If the result is false and "full" parameter is true => the packet is full
	 */
	bool addData(double time, void* data, bool& full)
	{
		//add a record in the packet
		full = false;

		if (!_isInit)
			return false;

		full = (_nbData >= _nbMaxData);
		if (full)
			//cannot add more data in the packet
			return false;

		//set data
		void *pos = _datas;
		int sizeToCopy;

		switch (_paramType)
		{
		case TYPE_FLOAT :
			sizeToCopy = _size * sizeof(float);
			break;
		case TYPE_DOUBLE :
			sizeToCopy = _size * sizeof(double);
			break;
		case TYPE_SHORT :
			sizeToCopy = _size * sizeof(short);
			break;
		case TYPE_INT :
			sizeToCopy = _size * sizeof(int);
			break;
		default:
			return false;
		}
		pos = (void*)((intptr_t)pos + (_nbData * sizeToCopy));

		memcpy(pos,data,sizeToCopy);

		//set time
		_times[_nbData] = time;

		++_nbData;

		full = (_nbData >= _nbMaxData);
		return true;
	}

	/*
	 * @brief Get number of record contained by the packet
	 */
	int getNbData(void)
	{
		if (!_isInit)
			return 0;
		return _nbData;
	}

	/*
	 * @brief Get one time by record index
	 */
	double getTime(int index)
	{
		if (!_isInit)
			return 0.;
		if (index >= _nbData)
			return 0.;
		return _times[index];
	}

	/*
	 * @brief Get the first dimension size.
	 */
	int getDim1Size(void)
	{
		if (!_isInit)
			return 0;
		switch (_containerType)
		{
		case CONTAINER_VECTOR :
		case CONTAINER_MATRIX :
			if (_dims != NULL)
				return _dims[0];
			break;
		default:
			return 0;
		}
		return 0;
	}

	/*
	 * @brief Get the second dimension size.
	 */
	int getDim2Size(void)
	{
		if (!_isInit)
			return 0;
		switch (_containerType)
		{
		case CONTAINER_MATRIX :
			if (_dims != NULL)
				return _dims[1];
			break;
		default:
			return 0;
		}
		return 0;
	}

	/*
	 * @brief Get the full size of a data of a record
	 */
	int getDimsSize(void)
	{
		return _size;
	}

	/*
	 * @brief Get data type for a record
	 */
	LocalParamType getType()
	{
		return _paramType;
	}

	/*
	 * @brief Get one data value from record with the index "index"
	 * And from the dimensions indexes
	 */
	bool getDataValue(void *val, int index, int dim1Index = 0, int dim2Index = 0)
	{
		if (!_isInit)
			return false;
		void *pos = NULL;

		int valueSize = 0;
		switch (_paramType)
		{
			case TYPE_FLOAT :
				valueSize = sizeof(float);
				break;
			case TYPE_DOUBLE :
				valueSize = sizeof(double);
				break;
			case TYPE_SHORT :
				valueSize = sizeof(short);
				break;
			case TYPE_INT :
				valueSize = sizeof(int);
				break;
			default:
				return false;
		}

		switch (_containerType)
		{
		case CONTAINER_SCALAR :
			pos = (void*)((intptr_t)_datas + (valueSize * _size * index));
			break;
		case CONTAINER_VECTOR :
			pos = (void*)((intptr_t)_datas + (valueSize * _size * index));
			pos = (void*)((intptr_t)pos + (dim1Index * valueSize));
			break;
		case CONTAINER_MATRIX :
			pos = (void*)((intptr_t)_datas + (valueSize * _size * index));
			pos = (void*)((intptr_t)pos + (dim1Index * _dims[1] * valueSize));
			pos = (void*)((intptr_t)pos + (dim2Index * valueSize));
			break;
		default :
			return false;
		}
		memcpy(val,pos,valueSize);
		return true;
	}

	bool isNoData() {
		return _noData;
	}

	double getStartTime() {
		return _startTime;
	}

	double getStopTime() {
		return _stopTime;
	}

	void setNoData(double startTime, double stopTime) {
		_noData = true;
		_startTime = startTime;
		_stopTime  = stopTime;
	}

private:
	/*
	 * @brief Flag to know if the packet is init
	 */
	bool _isInit;

	/*
	 * @brief Container type for data of the packet
	 */
	LocalContainerType _containerType;

	/*
	 * @brief Param type for data of the packet
	 */
	LocalParamType     _paramType;

	/*
	 * @brief Number of record defined in the packet
	 */
	int                _nbData;

	/*
	 * @brief Maximum number of record that's can be defined in the packet
	 */
	int                _nbMaxData;

	/*
	 * @brief Dimensions definition
	 */
	int*               _dims;

	/*
	 * @brief Full size of a data of a record
	 */
	int                _size;

	/*
	 * @brief times buffer
	 */
	double*            _times;

	/*
	 * @brief record data buffer
	 */
	void*              _datas;

	bool               _noData;

	double             _startTime;

	double             _stopTime;
};

} /* LocalFileInterface */
} /* AMDA */

#endif