//--------------------------------------------------------------------------------------------
// <copyright file="LqrSMACIObjectBroker-inline.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>
//  Implementation of the inline/template methods of the IObjectBroker class, an abstract base
//  class that provides a simplified CORBA interface.
// </summary>
//
// <owner>Mike Cookson</owner>
//--------------------------------------------------------------------------------------------

#ifndef __IOBJECTBROKER_INLINE_H__
#define __IOBJECTBROKER_INLINE_H__

#include "LqrSMACIObjectBroker.h"
#include <sstream>
#include <typeinfo>
#include <stdexcept>
#include <stdint.h>

namespace LiquidRobotics {
namespace SMACFramework {

template<typename RETURN_TYPE, typename NARROWING_TYPE>
RETURN_TYPE IObjectBroker::GetObjectFromStringizedName(const char* instanceStringized)
{
	using namespace std;
	using namespace CORBA;

	Object_var rawInstance = GetRawObjectFromStringizedName(instanceStringized);
	RETURN_TYPE retVal = NARROWING_TYPE::_narrow(rawInstance);

	if (CORBA::is_nil(retVal.in()))
	{
		stringstream stm;
		stm << "Narrow failed for stringized instance " << instanceStringized << " " << typeid(RETURN_TYPE).name();
		throw NarrowFailure(stm.str());
	}

	return retVal;
}

template<typename RETURN_TYPE, typename NARROWING_TYPE>
RETURN_TYPE IObjectBroker::GetRootInstance(const char* instanceName)
{
	using namespace std;
	using namespace CORBA;

	Object_var rawInstance = GetRawRootInstance(instanceName);
	RETURN_TYPE retVal = NARROWING_TYPE::_narrow(rawInstance);

	if (CORBA::is_nil(retVal.in()))
	{
		stringstream stm;
		stm << "Narrow failed for root instance " << instanceName << " " << typeid(RETURN_TYPE).name();
		throw NarrowFailure(stm.str());
	}

	return retVal;
}


template<typename RETURN_TYPE, typename NARROWING_TYPE>
RETURN_TYPE IObjectBroker::GetNamedInstance(const char* instanceName, const char* instanceKind, bool validateName, StringPairVectorSet* cachePtr)
{
	using namespace std;
	using namespace CORBA;

	Object_var rawInstance = GetRawNamedInstance(instanceName, instanceKind ? instanceKind : "", validateName, cachePtr);
	RETURN_TYPE retVal = NARROWING_TYPE::_narrow(rawInstance);

	if (CORBA::is_nil(retVal.in()))
	{
		stringstream stm;
		stm << "Narrow failed for named instance " << instanceName;

		if (instanceKind) stm << "/" << instanceKind;

		stm << " " << typeid(RETURN_TYPE).name();

		throw NarrowFailure(stm.str());
	}

	return retVal;
}

template<typename RETURN_TYPE, typename NARROWING_TYPE>
RETURN_TYPE IObjectBroker::GetDynamicObjectFromTypeCode(TypeCode_ptr typeCode)
{
	using namespace std;
	using namespace CORBA;

	Object_var rawInstance = GetRawDynamicObjectFromTypeCode(typeCode);
	RETURN_TYPE retVal = NARROWING_TYPE::_narrow(rawInstance);

	if (is_nil(retVal.in()))
	{
		stringstream stm;
		stm << "Narrow failed for " << typeid(NARROWING_TYPE).name();
		throw NarrowFailure(stm.str());
	}

	return retVal;
}

class AnyToCppVectorDefaultCopier
{
public:

	typedef CORBA::Any Any;
	typedef DynamicAny::DynAnySeq DynAnySeq;
	typedef CORBA::String_var String_var;

	template<typename T>
	void DoThrow(CORBA::Boolean testValue)
	{
		using namespace std;

		if (!testValue)
		{
			stringstream msg;
			msg << "AnyToCppVectorDefaultCopier failed to convert Any to target type " << typeid(T).name();
			throw runtime_error(msg.str());
		}
	}

	AnyToCppVectorDefaultCopier(const Any& from, uint8_t& to)
	{ DoThrow<uint8_t>(from >>= Any::to_octet(to)); }
	AnyToCppVectorDefaultCopier(const Any& from, int8_t& to)
	{ CORBA::Char temp; DoThrow<int8_t>(from >>= Any::to_char(temp)); to = temp; }
	AnyToCppVectorDefaultCopier(const Any& from, uint16_t& to)
	{ DoThrow<uint16_t>(from >>= to); }
	AnyToCppVectorDefaultCopier(const Any& from, int16_t& to)
	{ DoThrow<int16_t>(from >>= to); }
	AnyToCppVectorDefaultCopier(const Any& from, uint32_t& to)
	{ CORBA::ULong temp; DoThrow<uint32_t>(from >>= temp); to = temp; }
	AnyToCppVectorDefaultCopier(const Any& from, int32_t& to)
	{ CORBA::Long temp; DoThrow<int32_t>(from >>= temp); to = temp; }
	AnyToCppVectorDefaultCopier(const Any& from, float& to)
	{ DoThrow<float>(from >>= to); }
	AnyToCppVectorDefaultCopier(const Any& from, double& to)
	{ DoThrow<double>(from >>= to); }
	AnyToCppVectorDefaultCopier(const Any& from, std::string& to)
	{ const char* temp; DoThrow<std::string>(from >>= temp); to = temp; }
	AnyToCppVectorDefaultCopier(const Any& from, std::vector<bool>::reference bitref)
	{ bool temp; DoThrow<bool>(from >>= Any::to_boolean(temp)); bitref = temp; }
};

class CppVectorToAnyDefaultCopier
{
public:

	typedef DynamicAny::DynAnySeq DynAnySeq;

	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const uint8_t& from)
	{ to[idx]->insert_octet(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const int8_t& from)
	{ to[idx]->insert_char(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const uint16_t& from)
	{ to[idx]->insert_ushort(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const int16_t& from)
	{ to[idx]->insert_short(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const uint32_t& from)
	{ to[idx]->insert_ulong(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const int32_t& from)
	{ to[idx]->insert_long(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const float& from)
	{ to[idx]->insert_float(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const double& from)
	{ to[idx]->insert_double(from); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const std::string& from)
	{ to[idx]->insert_string(from.c_str()); }
	CppVectorToAnyDefaultCopier(DynAnySeq& to, _CORBA_ULong idx, const bool& from)
	{ to[idx]->insert_boolean(from); }
};

template<typename VECTOR_TYPE>
void IObjectBroker::AnyToCppVector(VECTOR_TYPE& to, const Any& from)
{
	return AnyToCppVector<VECTOR_TYPE,AnyToCppVectorDefaultCopier>(to, from);
}

template<typename VECTOR_TYPE, class COPIER>
void IObjectBroker::AnyToCppVector(VECTOR_TYPE& to, const Any& from)
{
	using namespace std;
	using namespace DynamicAny;

	// (ORB isn't used) CheckState("AnyToStringVector");

	// import the any into a DynArray
	TypeCode_var fromType;
	fromType = from.type();

	DynArray_var dynArrayV;
	dynArrayV = GetDynamicObjectFromTypeCode<DynArray_var,DynArray>(fromType.in());

	// after this point, the dynArrayV must be destroyed
	AnySeq* anySeqPtr = NULL;
	try
	{
		// get the DynArray from the Any
		dynArrayV->from_any(from);

		// get the elements of the sequence
		anySeqPtr = dynArrayV->get_elements();

		// clear the size initially, but reserve enough space for the entire set
		const _CORBA_ULong seqLen = anySeqPtr->length();
		to.resize(seqLen);

		// now copy each string
		for (_CORBA_ULong idx = 0; idx < seqLen; ++idx)
			COPIER((*anySeqPtr)[idx], to[idx]);

		// release the sequence ptr
		AnySeq* temp = anySeqPtr;
		anySeqPtr = NULL;
		delete temp;
	}
	catch(...)
	{
		if (anySeqPtr) delete anySeqPtr;
		dynArrayV->destroy();
		throw;
	}

	dynArrayV->destroy();
}

template<typename VECTOR_TYPE>
CORBA::Any_var IObjectBroker::CppVectorToAny(const VECTOR_TYPE& from, TypeCode_ptr resultType)
{
	return CppVectorToAny<VECTOR_TYPE,CppVectorToAnyDefaultCopier>(from, resultType);
}

template<typename VECTOR_TYPE, class COPIER>
CORBA::Any_var IObjectBroker::CppVectorToAny(const VECTOR_TYPE& from, TypeCode_ptr resultType)
{
	typedef typename VECTOR_TYPE::const_iterator C_ITER_TYPE;
	using namespace DynamicAny;

	Any_var result;

	// create a type code for the string array
	TypeCode_var tcV = ORB()->create_array_tc(from.size(), resultType);

	// create an instance of the dynamic array
	DynArray_var dynArrayV;
	dynArrayV = GetDynamicObjectFromTypeCode<DynArray_var,DynArray>(tcV.in());

	// after this point, the dynArrayV must be destroyed
	DynAnySeq* dynSeqPtr = NULL;
	try
	{
		// get the elements of the sequence
		dynSeqPtr = dynArrayV->get_elements_as_dyn_any();

		// add items at each element
		_CORBA_ULong i = 0;

		for (C_ITER_TYPE it = from.begin(); it != from.end(); ++it)
			COPIER(*dynSeqPtr, i++, *it);

		// compute the result
		result = dynArrayV->to_any();

		// release the sequence ptr
		DynAnySeq* temp = dynSeqPtr;
		dynSeqPtr = NULL;
		delete temp;
	}
	catch(...)
	{
		if (dynSeqPtr) delete dynSeqPtr;
		dynArrayV->destroy();
		throw;
	}

	dynArrayV->destroy();
	return result;
}

template<typename CPP_TYPE>
CORBA::Any_var IObjectBroker::CppVectorToAny(const CPP_TYPE* from, size_t numElems, TypeCode_ptr resultType)
{
	return CppVectorToAny<CPP_TYPE,CppVectorToAnyDefaultCopier>(from, numElems, resultType);
}

template<typename CPP_TYPE, class COPIER>
CORBA::Any_var IObjectBroker::CppVectorToAny(const CPP_TYPE* from, size_t numElems, TypeCode_ptr resultType)
{
	using namespace DynamicAny;

	Any_var result;

	// create a type code for the string array
	TypeCode_var tcV = ORB()->create_array_tc(numElems, resultType);

	// create an instance of the dynamic array
	DynArray_var dynArrayV;
	dynArrayV = GetDynamicObjectFromTypeCode<DynArray_var,DynArray>(tcV.in());

	// after this point, the dynArrayV must be destroyed
	DynAnySeq* dynSeqPtr = NULL;
	try
	{
		// get the elements of the sequence
		dynSeqPtr = dynArrayV->get_elements_as_dyn_any();

		// add items at each element
		for (_CORBA_ULong i = 0; i < ((_CORBA_ULong) numElems); ++i)
			COPIER(*dynSeqPtr, i, from[i]);

		// compute the result
		result = dynArrayV->to_any();

		// release the sequence ptr
		DynAnySeq* temp = dynSeqPtr;
		dynSeqPtr = NULL;
		delete temp;
	}
	catch(...)
	{
		if (dynSeqPtr) delete dynSeqPtr;
		dynArrayV->destroy();
		throw;
	}

	dynArrayV->destroy();
	return result;
}

}
}

#endif
