//--------------------------------------------------------------------------------
// <copyright file="EventPropertyValue.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>
// 	Declarations for EventPropertyValue class, which wraps a CORBA Any instance to
//  store one of several basic types and which interoperates with STL vector and
//  STL string.
// </summary>
//
// <owner>Mike Cookson</owner>
//--------------------------------------------------------------------------------

#ifndef __EVENTLIB_EVENTPROPERTYVALUE_H__
#define __EVENTLIB_EVENTPROPERTYVALUE_H__

#include "LqrSMACByteArray.h"
#include "LqrSMACDeclareException.h"
#include "LqrSMACIObjectBroker.h"
#include "LqrSMACSharedLib.h"
#include "LqrSMACXmlDOM.h"
#include <ostream>
#include <string>
#include <vector>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include "EventLibIDL.hh"

namespace LiquidRobotics {
namespace EventLib {

class SHARED_LIB_PUBLIC EPVFormatConfig;

class SHARED_LIB_PUBLIC EventPropertyValue
: public LiquidRobotics::SMACFramework::XmlDOM::IXmlDOMPersistent
{
public:

	enum StorageType
	{
		ST_UINT8 = 1,
		ST_INT8,
		ST_UINT16,
		ST_INT16,
		ST_UINT32,
		ST_INT32,
		ST_SINGLE_FLOAT,
		ST_DOUBLE_FLOAT,
		ST_STRING,
		ST_BOOLEAN,
		ST_NULL,
		ST_UNSUPPORTED,

		ST_VECTOR = 64,

		ST_UINT8_VECTOR = ST_UINT8 | ST_VECTOR,
		ST_INT8_VECTOR = ST_INT8 | ST_VECTOR,
		ST_UINT16_VECTOR = ST_UINT16 | ST_VECTOR,
		ST_INT16_VECTOR = ST_INT16 | ST_VECTOR,
		ST_UINT32_VECTOR = ST_UINT32 | ST_VECTOR,
		ST_INT32_VECTOR = ST_INT32 | ST_VECTOR,
		ST_SINGLE_FLOAT_VECTOR = ST_SINGLE_FLOAT | ST_VECTOR,
		ST_DOUBLE_FLOAT_VECTOR = ST_DOUBLE_FLOAT | ST_VECTOR,
		ST_STRING_VECTOR = ST_STRING | ST_VECTOR,
		ST_BOOLEAN_VECTOR = ST_BOOLEAN | ST_VECTOR,

		ST_TYPE_MASK = ~ST_VECTOR
	};

	typedef std::string string;

	static void FromString(const string& str, StorageType& result);
	static string ToString(const StorageType& stype);

	typedef ::CORBA::Any Any;
	typedef ::CORBA::Any_var Any_var;
	typedef boost::shared_ptr<EventPropertyValue> Ptr;
	typedef LiquidRobotics::SMACFramework::ByteArray ByteArray;
	typedef LiquidRobotics::SMACFramework::ByteArrayPtr ByteArrayPtr;
	typedef LiquidRobotics::SMACFramework::IObjectBroker IObjectBroker;
	typedef std::ostream ostream;
	typedef LiquidRobotics::SMACFramework::XmlDOM::XmlNode XmlNode;
	typedef LiquidRobotics::SMACFramework::XmlDOM::XmlNVPBase XmlNVPBase;

	typedef uint8_t UInt8;
	typedef int8_t Int8;
	typedef uint16_t UInt16;
	typedef int16_t Int16;
	typedef uint32_t UInt32;
	typedef int32_t Int32;
	typedef float SingleFloat;
	typedef double DoubleFloat;
	typedef std::string String;
	typedef bool Boolean;

	typedef std::vector<UInt8> UInt8Vector;
	typedef boost::shared_ptr<UInt8Vector> UInt8VectorPtr;
	typedef std::vector<Int8> Int8Vector;
	typedef boost::shared_ptr<Int8Vector> Int8VectorPtr;
	typedef std::vector<UInt16> UInt16Vector;
	typedef boost::shared_ptr<UInt16Vector> UInt16VectorPtr;
	typedef std::vector<Int16> Int16Vector;
	typedef boost::shared_ptr<Int16Vector> Int16VectorPtr;
	typedef std::vector<UInt32> UInt32Vector;
	typedef boost::shared_ptr<UInt32Vector> UInt32VectorPtr;
	typedef std::vector<Int32> Int32Vector;
	typedef boost::shared_ptr<Int32Vector> Int32VectorPtr;
	typedef std::vector<SingleFloat> SingleFloatVector;
	typedef boost::shared_ptr<SingleFloatVector> SingleFloatVectorPtr;
	typedef std::vector<DoubleFloat> DoubleFloatVector;
	typedef boost::shared_ptr<DoubleFloatVector> DoubleFloatVectorPtr;
	typedef std::vector<String> StringVector;
	typedef boost::shared_ptr<StringVector> StringVectorPtr;
	typedef std::vector<Boolean> BooleanVector;
	typedef boost::shared_ptr<BooleanVector> BooleanVectorPtr;

	typedef unsigned long size_type;

	DECLARE_EXCEPTION(ExtractionFailed,std::runtime_error);

	EventPropertyValue(IObjectBroker& _broker);
	EventPropertyValue(const EventPropertyValue& other);
	EventPropertyValue(const Any_var& anyVar, IObjectBroker& _broker);
	EventPropertyValue(const Any& any, IObjectBroker& _broker);

	EventPropertyValue& operator=(const EventPropertyValue& other);
	EventPropertyValue& operator=(const Any_var& anyVar);
	EventPropertyValue& operator=(const Any& any);

	bool operator==(const EventPropertyValue& other) const;

	StorageType GetStorageType() const;

	ostream& Describe(ostream& stm, bool verbose = false) const;

	string DescribeStorageType() const;
	static string DescribeStorageType(StorageType storageType);

	size_t GetNumElements() const;

	void CopyOut(Any& any) const;

	void ToBinary(ByteArray& result, bool append = false) const;
	void ToBinary(ByteArray& result, const EPVFormatConfig& fmt, bool append = false) const;

	void Load(XmlNode& node, const XmlNVPBase& nvp);
	void Save(XmlNode& node, const XmlNVPBase& nvp) const;

	string GetAsString() const;
	void SetAsString(StorageType stype, const string& newValue);

	Int8 GetInt8() const;
	Int8VectorPtr GetInt8Vector() const;
	void SetInt8(Int8 newValue);
	void SetInt8Vector(const Int8* pStart, size_type length);
	void SetInt8Vector(const Int8Vector& newValue);

	UInt8 GetUInt8() const;
	UInt8VectorPtr GetUInt8Vector() const;
	void SetUInt8(UInt8 newValue);
	void SetUInt8Vector(const UInt8* pStart, size_type length);
	void SetUInt8Vector(const UInt8Vector& newValue);

	ByteArrayPtr GetByteArray() const;
	void SetByteArray(const ByteArray& newValue);

	Int16 GetInt16() const;
	Int16VectorPtr GetInt16Vector() const;
	void SetInt16(Int16 newValue);
	void SetInt16Vector(const Int16* pStart, size_type length);
	void SetInt16Vector(const Int16Vector& newValue);

	UInt16 GetUInt16() const;
	UInt16VectorPtr GetUInt16Vector() const;
	void SetUInt16(UInt16 newValue);
	void SetUInt16Vector(const UInt16* pStart, size_type length);
	void SetUInt16Vector(const UInt16Vector& newValue);

	Int32 GetInt32() const;
	Int32VectorPtr GetInt32Vector() const;
	void SetInt32(Int32 newValue);
	void SetInt32Vector(const Int32* pStart, size_type length);
	void SetInt32Vector(const Int32Vector& newValue);

	UInt32 GetUInt32() const;
	UInt32VectorPtr GetUInt32Vector() const;
	void SetUInt32(UInt32 newValue);
	void SetUInt32Vector(const UInt32* pStart, size_type length);
	void SetUInt32Vector(const UInt32Vector& newValue);

	SingleFloat GetSingleFloat() const;
	SingleFloatVectorPtr GetSingleFloatVector() const;
	void SetSingleFloat(SingleFloat newValue);
	void SetSingleFloatVector(const SingleFloat* pStart, size_type length);
	void SetSingleFloatVector(const SingleFloatVector& newValue);

	DoubleFloat GetDoubleFloat() const;
	DoubleFloatVectorPtr GetDoubleFloatVector() const;
	void SetDoubleFloat(DoubleFloat newValue);
	void SetDoubleFloatVector(const DoubleFloat* pStart, size_type length);
	void SetDoubleFloatVector(const DoubleFloatVector& newValue);

	String GetString() const;
	StringVectorPtr GetStringVector() const;
	void SetString(String newValue);
	void SetStringVector(const String* pStart, size_type length);
	void SetStringVector(const StringVector& newValue);

	String GetStringVariant() const;
	void SetStringVariant(String newValue);

	Boolean GetBoolean() const;
	BooleanVectorPtr GetBooleanVector() const;
	void SetBoolean(Boolean newValue);
	void SetBooleanVector(const Boolean* pStart, size_type length);
	void SetBooleanVector(const BooleanVector& newValue);

private:

	friend class boost::serialization::access;

	void load(boost::archive::xml_iarchive& ar, const unsigned int version);
	void save(boost::archive::xml_oarchive& ar, const unsigned int version) const;

	BOOST_SERIALIZATION_SPLIT_MEMBER()

	template<typename T>
	static void AppendBytes(const T& val, ByteArray& output, bool swapBytes = false);

	template<typename TV>
	static void AppendCollectionBytes(const TV& val, ByteArray& output, bool swapBytes = false);

	template<typename TRet,typename TTemp>
	TRet GetScalar(EventPropertyValue::StorageType storageType) const;

	template<typename TRet,typename TTemp,typename TInter>
	TRet GetScalar(EventPropertyValue::StorageType storageType) const;

	template<typename VECTOR_TYPE>
	boost::shared_ptr<VECTOR_TYPE> GetVector() const;

	template<typename TRet,typename TTemp>
	void SetScalar(TRet newValue);

	template<typename TRet,typename TTemp,typename TInter>
	void SetScalar(TRet newValue);

	template<typename VECTOR_TYPE>
	void SetVector(const VECTOR_TYPE& newValue, CORBA::TypeCode_ptr resultType);

	template<typename CPP_TYPE>
	void SetVector(const CPP_TYPE* pStart, size_type length, CORBA::TypeCode_ptr resultType);

	template<typename TU, typename TS>
	void ToBinaryWithLength(ByteArray& result, const EPVFormatConfig& fmt, bool append, const StorageType stype) const;

	static void Copy(Any_var& to, const Any& from);

	::CORBA::Any_var theValue;
	IObjectBroker& broker;
};

std::ostream& operator<<(std::ostream& stm, const EventPropertyValue& epv);

}
}

#endif
