//------------------------------------------------------------------------------
// <copyright file="LqrSMACIdl.cpp" 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>
// Implements non-template methods for IDL wrappers
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#include "LqrSMACIdl.h"
#include <iostream>
#include <sstream>
#include <typeinfo>

using namespace std;
using namespace LiquidRobotics::SMAC;

namespace LiquidRobotics {
namespace SMACFramework {

template<typename T>
bool Decode(CORBA::Exception& ex, stringstream& msg)
{
	if (typeid(ex) == typeid(T))
	{
		T& derived = dynamic_cast<T&>(ex);
		const char* msgptr = derived.message.in();

		if (!msgptr) msgptr = "";

		if (*msgptr)
			msg << " with message='" << msgptr << "'";

		return true;
	}
	else
		return false;
}

template<typename T>
bool Decode2(CORBA::Exception& ex, stringstream& msg)
{
	if (typeid(ex) == typeid(T))
	{
		T& derived = dynamic_cast<T&>(ex);
		const char* parameterName = derived.parameterName.in();
		const char* msgptr = derived.message.in();

		if (!parameterName) parameterName = "";
		if (!msgptr) msgptr = "";

		if (*parameterName || *msgptr)
			msg << " with parameterName='" << parameterName << "' and message='" << msgptr << "'";

		return true;
	}
	else
		return false;
}

string DecodeCORBAException(CORBA::Exception& ex)
{
	stringstream msg;

	msg << ex._name();

	if 		(Decode<AlreadyRegistered>(ex, msg));
	else if (Decode2<BadParameter>(ex, msg));
	else if (Decode<BadFileControlBlock>(ex, msg));
	else if (Decode<BadProgramSelectionConfig>(ex, msg));
	else if (Decode<CommFailure>(ex, msg));
	else if (Decode<CRCCheckFailed>(ex, msg));
	else if (Decode<FileHasBeenDeleted>(ex, msg));
	else if (Decode<FileInUse>(ex, msg));
	else if (Decode<FileIsReadOnly>(ex, msg));
	else if (Decode<MD5CheckFailed>(ex, msg));
	else if (Decode<NotReady>(ex, msg));
	else if (Decode<NotRegistered>(ex, msg));
	else if (Decode<StorageIOError>(ex, msg));
	else if (Decode<StorageIsFull>(ex, msg));
	else if (Decode<StorageIsMissing>(ex, msg));
	else if (Decode<TooManyConcurrentCommands>(ex, msg));
	else if (Decode<TooManySubscribers>(ex, msg));
	else if (Decode<UnknownSubscriber>(ex, msg));
	else if (Decode<UnavailableInPowerOffState>(ex, msg));

	return msg.str();
}

#define SCW_HANDLE_EXCEPTION0(exName,waVar,res) \
			catch(exName & ex) { res.ptr() = new Any(); SCWHandleException(#exName, *waVar.ptr(), NULL); }

#define SCW_HANDLE_EXCEPTION(exName,waVar,res) \
			catch(exName & ex) { res.ptr() = new Any(); SCWHandleException(#exName, *waVar.ptr(), ex.message.in()); }

#define SCW_HANDLE_EXCEPTION2(exName,waVar,res) \
			catch(exName & ex) { res.ptr() = new Any(); SCWHandleException(#exName, *waVar.ptr(), ex.message.in(), ex.parameterName.in()); }

inline void SCWHandleException(const char* exName, SendCommandWorkaroundData& waVar, const char* data1, const char* data2 = NULL)
{
	waVar.exceptionThrown = true;
	waVar.exceptionName = exName;

	if (data1)
		waVar.param1 = data1;

	if (data2)
		waVar.param2 = data2;
}

ICommandableInvokerBase::ICommandableInvokerBase()
{
}

ICommandableInvokerBase::~ICommandableInvokerBase()
{
}

#define ICI_RETHROW0(typeName,waVar) \
			ICIRethrow0<typeName>(waVar.in(),#typeName);

#define ICI_RETHROW(typeName,waVar) \
			ICIRethrow<typeName>(waVar.in(),#typeName);

#define ICI_RETHROW2(typeName,waVar) \
			ICIRethrow2<typeName>(waVar.in(),#typeName);

template<typename T>
void ICIRethrow0(const SendCommandWorkaroundData& waData, const char* exName)
{
	if (!strcmp(exName, waData.exceptionName))
		throw T();
}

template<typename T>
void ICIRethrow(const SendCommandWorkaroundData& waData, const char* exName)
{
	if (!strcmp(exName, waData.exceptionName))
		throw T(waData.param1.in());
}

template<typename T>
void ICIRethrow2(const SendCommandWorkaroundData& waData, const char* exName)
{
	if (!strcmp(exName, waData.exceptionName))
		throw T(waData.param2.in(), waData.param1.in());
}

void ICommandableInvokerBase::operator()(const Any& command, const char* requester, const char* transactionID, Any_OUT_arg result)
{
	SendCommandWorkaroundData_var waVar;

	Invoke(command, (const char*) requester, (const char*) transactionID, result, waVar.out());

#if USING_SENDCOMMAND_WORKAROUND
	if (waVar->exceptionThrown)
	{
		ICI_RETHROW(AlreadyRegistered,waVar)
		ICI_RETHROW2(BadParameter,waVar)
		ICI_RETHROW(BadFileControlBlock,waVar)
		ICI_RETHROW(BadProgramSelectionConfig,waVar)
		ICI_RETHROW(CommFailure,waVar)
		ICI_RETHROW(CRCCheckFailed,waVar)
		ICI_RETHROW(FileHasBeenDeleted,waVar)
		ICI_RETHROW(FileInUse,waVar)
		ICI_RETHROW(FileIsReadOnly,waVar)
		ICI_RETHROW(MD5CheckFailed,waVar)
		ICI_RETHROW(NotReady,waVar)
		ICI_RETHROW(NotRegistered,waVar)
		ICI_RETHROW(StorageIOError,waVar)
		ICI_RETHROW(StorageIsFull,waVar)
		ICI_RETHROW(StorageIsMissing,waVar)
		ICI_RETHROW(TooManyConcurrentCommands,waVar)
		ICI_RETHROW(TooManySubscribers,waVar)
		ICI_RETHROW(UnknownSubscriber,waVar)
		ICI_RETHROW(UnavailableInPowerOffState,waVar)
		ICI_RETHROW0(WillCompleteAsynchronously,waVar)
		ThrowCORBAException2<BadParameter>("Client caught unsupported exception");
	}
#endif
}

ICommandableBase::ICommandableBase()
{
}

ICommandableBase::~ICommandableBase()
{
}

void ICommandableBase::SendCommandWorkaround(const Any& command, const char* requester, const char* transactionID, Any_OUT_arg result, SendCommandWorkaroundData_out waData)
{
	waData.ptr() = new SendCommandWorkaroundData();
	waData->exceptionThrown = false;

	try
	{
		SendCommand(command, requester, transactionID, result);
	}
	SCW_HANDLE_EXCEPTION(AlreadyRegistered,waData,result)
	SCW_HANDLE_EXCEPTION2(BadParameter,waData,result)
	SCW_HANDLE_EXCEPTION(BadFileControlBlock,waData,result)
	SCW_HANDLE_EXCEPTION(BadProgramSelectionConfig,waData,result)
	SCW_HANDLE_EXCEPTION(CommFailure,waData,result)
	SCW_HANDLE_EXCEPTION(CRCCheckFailed,waData,result)
	SCW_HANDLE_EXCEPTION(FileHasBeenDeleted,waData,result)
	SCW_HANDLE_EXCEPTION(FileInUse,waData,result)
	SCW_HANDLE_EXCEPTION(FileIsReadOnly,waData,result)
	SCW_HANDLE_EXCEPTION(MD5CheckFailed,waData,result)
	SCW_HANDLE_EXCEPTION(NotReady,waData,result)
	SCW_HANDLE_EXCEPTION(NotRegistered,waData,result)
	SCW_HANDLE_EXCEPTION(StorageIOError,waData,result)
	SCW_HANDLE_EXCEPTION(StorageIsFull,waData,result)
	SCW_HANDLE_EXCEPTION(StorageIsMissing,waData,result)
	SCW_HANDLE_EXCEPTION(TooManyConcurrentCommands,waData,result)
	SCW_HANDLE_EXCEPTION(TooManySubscribers,waData,result)
	SCW_HANDLE_EXCEPTION(UnknownSubscriber,waData,result)
	SCW_HANDLE_EXCEPTION(UnavailableInPowerOffState,waData,result)
	SCW_HANDLE_EXCEPTION0(WillCompleteAsynchronously,waData,result)
	catch(...)
	{
		SCWHandleException("BadParameter", *waData.ptr(), "Server caught expected exception", "n/a");
	}
}

}
}
