//------------------------------------------------------------------------------
// <copyright file="LqrSMACBitConvert.h" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
// </summary>
//
// <owner></owner>
//------------------------------------------------------------------------------

#ifndef __LQRSMACBITCONVERT_H__
#define __LQRSMACBITCONVERT_H__

#include "LqrSMACSharedLib.h"
#include "LqrSMACByteArray.h"
#include <stdint.h>
#include <stdexcept>

namespace LiquidRobotics {
namespace SMACFramework {

class SHARED_LIB_PUBLIC BitConvert
{
public:

	enum Endianness
	{
		NATIVE_END,				// for encoding/decoding, use native endian format
		LITTLE_END,				// numeric values encode least significant bits first
		BIG_END					// numeric values encode most significant bits first
	};

	typedef size_t SizeType;

	static inline Endianness GetNativeEndian()
	{
		const uint16_t testValue = 1;
		const Endianness result = *((uint8_t*) &testValue) ? LITTLE_END : BIG_END;
		return result;
	}

	template<typename ValueType>
	static ValueType GetVarFromBytes(const uint8_t* inputData)
	{
		ValueType result;
		memcpy(&result, inputData, sizeof(result));
		return result;
	}

	template<typename ValueType, typename ContainerType>
	static ValueType GetVarFromContainer(const ContainerType& container)
	{
		const uint8_t* inputData = &container.front();
		return GetVarFromBytes<ValueType>(inputData);
	}

	template<typename ValueType, typename ContainerType>
	static ValueType GetVarFromContainer(const ContainerType& container, SizeType& idx)
	{
		using namespace std;

		if (container.size() < (idx+sizeof(container)-1))
			throw invalid_argument("container underflow in GetVarFromContainer");

		const uint8_t* inputData = &container.front();
		return GetVarFromBytes<ValueType>(inputData+idx);
	}

	template<typename ValueType, typename ContainerType>
	static void PushVarToContainer(const ValueType& val, ContainerType& container)
	{
		if (sizeof(val))
		{
			uint8_t buffer[sizeof(val)];

			PutVarToBytes(val, buffer);

			const uint8_t* pStart = buffer;
			const uint8_t* const pEnd = pStart+sizeof(buffer);

			while(pStart != pEnd)
				container.push_back(*pStart++);
		}
	}

	template<typename ValueType>
	static void PutVarToBytes(const ValueType& val, uint8_t* outputData)
	{
		memcpy(outputData, &val, sizeof(val));
	}

	template<typename ValueType>
	static void DecodeNumeric(const uint8_t* inputData, ValueType& val)
	{
		val = GetVarFromBytes<ValueType>(inputData);
	}

	template<typename ValueType>
	static void DecodeNumeric(const uint8_t* inputData, ValueType& val, const Endianness& endian)
	{
		val = ReverseNumericCopy<ValueType>(GetVarFromBytes<ValueType>(inputData), endian);
	}

	template<typename ValueType, typename ContainerType>
	static void DecodeNumeric(const ContainerType& container, ValueType& val, const size_t& idx)
	{
		val = GetVarFromContainer<ValueType,ContainerType>(container, idx);
	}

	template<typename ValueType, typename ContainerType>
	static void DecodeNumeric(const ContainerType& container, ValueType& val, const size_t& idx, const Endianness& endian)
	{
		val = ReverseNumericCopy<ValueType>(GetVarFromContainer<ValueType,ContainerType>(container, idx), endian);
	}

	template<typename ValueType>
	static void EncodeNumeric(const ValueType& val, uint8_t* outputData)
	{
		PutVarToBytes(val, outputData);
	}

	template<typename ValueType>
	static void EncodeNumeric(const ValueType& val, uint8_t* outputData, const Endianness& endian)
	{
		PutVarToBytes(ReverseNumericCopy<ValueType>(val, endian), outputData);
	}

	template<typename ValueType, typename ContainerType>
	static void EncodeNumeric(const ValueType& val, ContainerType& container, const Endianness& endian)
	{
		PutVarToContainer(ReverseNumericCopy<ValueType>(val, endian), container);
	}

	template<typename ValueType, typename ContainerType>
	static void PushNumeric(const ValueType& val, ContainerType& container, const Endianness& endian)
	{
		container.push_back(ReverseNumericCopy<ValueType>(val, endian));
	}

	static inline void ReverseBuffer(uint8_t* buffer, size_t bufferSize)
	{
		uint8_t* pStart = buffer;
		uint8_t* pEnd = &pStart[bufferSize-1];
		size_t times = bufferSize/2;

		while(times--)
		{
			const uint8_t temp = *pStart;
			*pStart++ = *pEnd;
			*pEnd-- = temp;
		}
	}

	template<typename ValueType>
	static void ReverseVar(ValueType& val)
	{
		ReverseBuffer((uint8_t*) &val, sizeof(val));
	}

	template<typename ValueType>
	static ValueType ReverseVarCopy(ValueType val)
	{
		ValueType valCopy = val;
		ReverseBuffer((uint8_t*) &valCopy, sizeof(valCopy));
		return valCopy;
	}

	template<typename ValueType>
	static void ReverseNumeric(ValueType& val, Endianness endian)
	{
		if ((sizeof(val) > 1) && (endian != NATIVE_END) && (GetNativeEndian() != endian))
			ReverseVar(val);
	}

	template<typename ValueType>
	static ValueType ReverseNumericCopy(const ValueType val, Endianness endian)
	{
		const ValueType result = ((sizeof(val) > 1) && (endian != NATIVE_END) && (GetNativeEndian() != endian)) ? ReverseVarCopy(val) : val;
		return result;
	}
};


}
}

#endif
