//---------------------------------------------------------------------------------
// <copyright file="ProxySC.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>
// 	Implementation of ProxySC, which encapsulates the work of maintaining a storage
//  controller for a particular destination address and then forwarding calls to
//  the target CORBA implementation.
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include "ProxySC.h"
#include "LqrSMACIdl.h"
#include <fstream>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include "DataSetEvent.h"
#include "SendCommandCompleteEvent.h"
#include "FCCMessage_Utilities.h"
#include "LqrSMACGetErrorDescription.h"
#include "LqrSMACDetermineFileExtension.h"
#include "LqrSMACIObjectBroker-inline.h"
#include "LqrSMACTempFile.h"
#include "nsextend.h"
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

#ifdef NEW_FILESYSTEM
#undef NEW_FILESYSTEM
#endif
#ifdef BOOST_FILESYSTEM_VERSION
#if BOOST_FILESYSTEM_VERSION == 3
#define NEW_FILESYSTEM
#endif
#endif

using namespace std;
using namespace boost::filesystem;
using namespace LiquidRobotics::EventLib;
using namespace LiquidRobotics::SMAC;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::PayloadInterface::FCCMessage;

inline string DescribeError(int errNum)
{
	stringstream stm;

	stm << "error #" << errNum << " (" << GetErrorDescription(errNum) << ")";
	return stm.str();
}


namespace LiquidRobotics {
namespace PayloadInterface {

ProxySC::ProxySC(BoardIDType _boardID, TaskIDType _taskID, const StorageController::Config& _scConfig, IObjectBroker& _broker,
		PayloadManagerClient& _pmClient, Logger& _logger, size_t _maxFileSize)
: boardID(_boardID)
, taskID(_taskID)
, maxFileSize(_maxFileSize)
, scConfig(Reconfig(_scConfig, GetAddress(_boardID, _taskID), _broker))
, broker(_broker)
, pmClient(_pmClient)
, logger(_logger)
, sc(scConfig)
, transID(1)
{
	stringstream srcID;
	srcID << "PayloadInterface:ProxySC:" << ultos(GetAddress(), 16, 4);

	cmdSrcID = srcID.str();
}

ProxySC::~ProxySC()
{
}

void ProxySC::ExecuteCommand(const ByteArray& command, long transNumber, unsigned destID, bool ascii, bool retFile, bool wantsAck)
{
	CORBAAny_var response;
	string ext;
	bool signalling = false;
	TransactionInfoPtr transInfo;
	transInfo.reset(new TransactionInfo(false, MakeTransactionID(), destID, 0, retFile, transNumber));

	try
	{
		if (!command.size())
			throw runtime_error("Command content is nil");

		CORBAAny_var fileContent(new CORBAAny);

		if (ascii)
		{
			string temp((const char*) &command.front(), command.size());

			StringToAny(temp, fileContent.inout());
		}
		else
			BytesToAny(command, fileContent.inout());

		// if wantsAck == false, StartRemoteCommand returns false, even on successful completion
		if (StartRemoteCommand(fileContent.in(), response, ext, transInfo, wantsAck) && wantsAck)
		{
			ByteArrayPtr bytes = AnyToBytes(response.in());

			signalling = true;
			HandleCommandCompletion(*transInfo, *bytes, true, true);
		}
	}
	catch(exception& ex)
	{
		stringstream msg;
		msg << "Caught " << typeid(ex).name() << " (" << ex.what() << ")"
			<< " while attempting to start command execution on sc=0x" << ultos(GetAddress(), 16, 4);
		string msg2 = msg.str();

		Logger::Locked(logger)->Error << msg2 << endl;

		if (!signalling && wantsAck)
		{
			ByteArray bytes((const ByteArray::byte*) msg2.data(), msg2.length());
			HandleCommandCompletion(*transInfo, bytes, true, false);
		}
	}

}

void ProxySC::ExecuteFileCompletion(StorageController::FileIDType fileID)
{
	CORBAAny_var response;
	string ext;
	bool signalling = false;
	TransactionInfoPtr transInfo;
	transInfo.reset(new TransactionInfo(true, MakeTransactionID(), 0, fileID));

	try
	{
		CORBAAny_var fileContent = GetFileContent(fileID);

		if (StartRemoteCommand(fileContent.in(), response, ext, transInfo))
		{
			ByteArrayPtr bytes = AnyToBytes(response.in());
			string fileName = StoreResponseFile(fileID, *bytes, ext);

			signalling = true;
			string remoteName = fileName;
			newFileSignal(*this, fileName, remoteName, transInfo->DestID());
		}
	}
	catch(exception& ex)
	{
		stringstream msg;
		msg << "Caught " << typeid(ex).name() << " (" << ex.what() << ")"
			<< " while attempting to start command execution on sc=0x" << ultos(GetAddress(), 16, 4);
		string msg2 = msg.str();

		Logger::Locked(logger)->Error << msg2 << endl;

		if (!signalling)
		{
			ByteArray bytes((const ByteArray::byte*) msg2.data(), msg2.length());
			string fileName = StoreResponseFile(fileID, bytes, ".TXT");
			string remoteName = fileName;

			newFileSignal(*this, fileName, remoteName, transInfo->DestID());
		}
	}
}

ProxySC::CORBAAny_var ProxySC::GetFileContent(StorageController::FileIDType fileID)
{
	CORBAAny_var result(new CORBAAny);
	REPORTED_NAME emptyName = RealNameToReported(".");
	StorageController::FileState::PtrList matching = sc.GetFileStateList(fileID, emptyName.paddedFileName);

	if (matching.size() != 1)
	{
		stringstream stm;
		stm << matching.size() << " matches to file ID 0x" << ultos(fileID, 16, 8) << endl;
		throw runtime_error(stm.str());
	}

	StorageController::FileState::Ptr fileItem = matching.front();

	if (fileItem->GetFileSize())
	{
		ByteArray fileContent;
		sc.ReadFile(fileContent, fileItem->GetFileID(), 0, fileItem->GetFileSize());

		if (!DetermineFileExtension(fileContent).compare(".TXT"))
		{
			string tempStr;
			tempStr.assign((const char*) &fileContent.at(0), fileContent.size());

			StringToAny(tempStr, result.inout());
		}
		else
			BytesToAny(fileContent, result.inout());
	}
	else
	{
		const string tempStr;
		StringToAny(tempStr, result.inout());
	}

	return result;
}

bool ProxySC::StartRemoteCommand(const CORBAAny& command, CORBAAny_var& result, string& ext, TransactionInfoPtr transInfo, bool saveResponse)
{
	bool commandComplete = false;
	bool caught = false;
	string caughtMessage;
	ICommandable_var cmdV;

	string ior = GetIORForTarget(GetAddress(), broker, pmClient);

	if (!ior.length())
	{
		stringstream stm;
		stm << "No registration for sc=0x" << ultos(GetAddress(), 16, 4);
		caughtMessage = stm.str();
		caught = true;
	}
	else
	{
		try
		{
			cmdV = broker.GetObjectFromStringizedName<ICommandable_var,ICommandable>(ior.c_str());
		}
		catch(...)
		{
			stringstream stm;
			stm << "Failed to get a command proxy for sc=0x" << ultos(GetAddress(), 16, 4);
			caughtMessage = stm.str();
			caught = true;
		}
	}

	bool addedToMap = false;

	if (!caught)
	{
		try
		{
			if (saveResponse)
			{
				StoreTransInfo(transInfo);
				addedToMap = true;
			}

			ICommandableInvoker<ICommandable_var> invoker(cmdV);
			invoker(command, cmdSrcID.c_str(), transInfo->TransID().c_str(), result);

			if (saveResponse)
			{
				EventPropertyValue evp(result.in(), broker);
				EventPropertyValue::StorageType sType = evp.GetStorageType();
				ByteArrayPtr bytes;

				if ((sType == EventPropertyValue::ST_STRING) || (sType == EventPropertyValue::ST_UINT8_VECTOR))
				{
					bytes = AnyToBytes(evp);
					ext = DetermineFileExtension(*bytes);
					commandComplete = true;
				}
				else
				{
					stringstream msg;
					msg << "Command " << transInfo->TransID() << " returned with unsupported format type " << evp.DescribeStorageType() << endl;
					throw runtime_error(msg.str());
				}
			}
		}
		catch(LiquidRobotics::SMAC::WillCompleteAsynchronously& wca)
		{
			Logger::Lock logLock(logger);
			logger.Info << "Command will complete asynchronously with transaction tag " << transInfo->TransID() << " (sc=0x" << ultos(GetAddress(), 16, 4) << ")" << endl;
		}
		catch(CORBA::Exception& exc)
		{
			stringstream stm;
			stm << "Caught CORBA exception " << DecodeCORBAException(exc) << " while forwarding command to remote server (sc=0x" << ultos(GetAddress(), 16, 4) << ")";
			caughtMessage = stm.str();
			caught = true;
		}
		catch(std::exception& exc)
		{
			stringstream stm;
			stm << "Caught exception " << typeid(exc).name() << " '" << exc.what() << "' while forwarding command to remote server (sc=0x" << ultos(GetAddress(), 16, 4) << ")";
			caughtMessage = stm.str();
			caught = true;
		}
		catch(...)
		{
			stringstream stm;
			stm << "Caught generic exception while forwarding command to remote server (sc=0x" << ultos(GetAddress(), 16, 4) << ")";
			caughtMessage = stm.str();
			caught = true;
		}
	}

	if (addedToMap && (caught || commandComplete))
	{
		try 			{ ClearTransInfo(transInfo->TransID()); }
		catch (...) 	{ }
	}

	if (caught)
		throw runtime_error(caughtMessage);

	return commandComplete;
}

string ProxySC::StoreResponseFile(StorageController::FileIDType cmdFileID, const ByteArray& responseContent, const string& ext)
{
	// command responses are named as the file ID of the command file in hex (8 chars) followed by extension
	stringstream newFileNameStm;
	newFileNameStm << ultos(cmdFileID, 16, 8) << ext;

	string newFileName = newFileNameStm.str();
	string reportedName = RealNameToReportedStr(newFileName);

	// ensure that the file does not already exist
	try
	{
		sc.DeleteFile(StorageController::NO_FILE_ID, reportedName, true);
	}
	catch(...)
	{
	}

	// get the full path of the new file
	StorageController::Config scConfig = sc.GetConfig();
	string newFilePath = StorageController::FileState::GetActualFilePath(scConfig.GetStorageDirectoryName(), newFileName, false);

	// write the content to the file
	int fd;
	int errSave;

	do
	{
		fd = open(newFilePath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
		errSave = errno;
	} while((fd == -1) && (errSave == EINTR));

	if (fd == -1)
	{
		Logger::Lock logLock(logger);
		logger.Error << "Could not create command response file at " << newFilePath << ": " << DescribeError(errSave) << endl;
	}

	else
	{
		int status;

		if (responseContent.size())
			do
			{
				status = write(fd, &responseContent.front(), responseContent.size());
				errSave = errno;
			} while((status == -1) && (errSave == EINTR));
		else
			status = 0;

		close(fd);

		if (status == -1)
		{
			unlink(newFilePath.c_str());

			Logger::Lock logLock(logger);
			logger.Error
				<< "Failed to write response file content of " << responseContent.size() << " bytes to " << newFilePath << ": "
				<< DescribeError(errSave) << endl;
		}

		else if (((string::size_type) status) != responseContent.size())
		{
			unlink(newFilePath.c_str());

			Logger::Lock logLock(logger);
			logger.Error
				<< "Wrote only " << status << " bytes of " << responseContent.size() << " to response file " << newFilePath << ": disk full?" << endl;
		}

		else
		{
			StorageController::FileState::Ptr item;
			bool added = false;

			try
			{
				sc.AddFile(newFileName);
				added = true;
				item = sc.GetFileState(newFileName);
			}
			catch(std::exception& exc)
			{
				if (!added) unlink(newFilePath.c_str());

				Logger::Lock logLock(logger);
				logger.Error
					<< "Caught " << typeid(exc).name() << " (" << exc.what() << ") while attempting to add response file " << newFilePath << " to storage controller";
				throw;
			}
		}
	}

	return newFileName;
}

ProxySC::AddressType ProxySC::GetAddress() const
{
	return GetAddress(boardID, taskID);
}

ProxySC::AddressType ProxySC::GetAddress(BoardIDType bid, TaskIDType tid)
{
	AddressType result = bid;

	result <<= 8;
	result += tid;

	return result;
}

ProxySC::BoardIDType ProxySC::GetBoardID() const
{
	return boardID;
}

StorageController& ProxySC::GetSC()
{
	return sc;
}

ProxySC::TaskIDType ProxySC::GetTaskID() const
{
	return taskID;
}

void ProxySC::Subscribe(const NewFileSlotType& slot)
{
	ScopedConnectionPtr conn;
	conn.reset(new ScopedConnection(newFileSignal.connect(slot)));
	connectionList.push_back(conn);
}

void ProxySC::Subscribe(const CompleteSignalSlotType& slot)
{
	ScopedConnectionPtr conn;
	conn.reset(new ScopedConnection(completeSignal.connect(slot)));
	connectionListComplete.push_back(conn);
}

string ProxySC::GetIORForTarget(AddressType address, IObjectBroker& broker, PayloadManagerClient& pmClient)
{
	string resultIOR;

	PayloadManagerClient::PayloadDescriptionList::Ptr descList = pmClient.GetPayloadDescriptions();

	for(PayloadManagerClient::PayloadDescriptionList::const_iterator it = descList->begin(); it != descList->end(); ++it)
	{
		PayloadManagerClient::PayloadDescription::Ptr desc = *it;
		const AddressType add0 = GetAddress(desc->GetBoardID(), desc->GetTaskID());

		if (add0 == address)
		{
			resultIOR = desc->GetIOR();
			break;
		}
	}

	return resultIOR;
}

bool ProxySC::HaveCommandableTarget(AddressType address, IObjectBroker& broker, PayloadManagerClient& pmClient)
{
	bool result = false;
	string ior = GetIORForTarget(address, broker, pmClient);

	if (ior.length())
		try
		{
			ICommandable_var scV;
			scV = broker.GetObjectFromStringizedName<ICommandable_var,ICommandable>(ior.c_str());
			result = true;
		}
		catch(...)
		{
		}

	return result;
}

bool ProxySC::HaveCommandableTarget(AddressType address, bool& implementsSC, IObjectBroker& broker, PayloadManagerClient& pmClient)
{
	bool result = false;
	string ior = GetIORForTarget(address, broker, pmClient);

	implementsSC = false;

	if (ior.length())
	{
		try
		{
			ICommandable_var scV;
			scV = broker.GetObjectFromStringizedName<ICommandable_var,ICommandable>(ior.c_str());
			result = true;
		}
		catch(...)
		{
		}

		try
		{
			IStorageController_var scV;
			scV = broker.GetObjectFromStringizedName<IStorageController_var,IStorageController>(ior.c_str());
			implementsSC = true;
		}
		catch(...)
		{
		}
	}

	return result;
}

StorageController::Config ProxySC::Reconfig(const StorageController::Config& _scConfig, AddressType address, IObjectBroker& broker)
{
	StorageController::Config reconfig(_scConfig);
	string addrStr = ultos(address, 16, 4);
	path scDirPath(_scConfig.GetStorageDirectoryName());

	if (!exists(scDirPath) || !is_directory(scDirPath))
	{
		string scDirPathString = scDirPath.string();
		stringstream stm;
		stm << "Storage controller base directory '" << scDirPathString << "' for ProxySC does not exist";

		throw runtime_error(stm.str());
	}

	path myPath = scDirPath;
	myPath /= addrStr;

	if (!exists(myPath))
		create_directory(myPath);
	else if (!is_directory(myPath))
	{
		stringstream stm;
		stm << myPath.string() << " (the root for the storage controller associated with address 0x" << addrStr << ") exists, but it is not a directory!";

		throw runtime_error(stm.str());
	}

	path configFilePath = myPath;
	configFilePath /= "sc-config.xml";

	path stateFilePath = myPath;
	stateFilePath /= "state.xml";

	if (!exists(configFilePath))
	{
		path filesPath = myPath;
		filesPath /= "files";

		if (!exists(filesPath))
			create_directory(filesPath);
		else if (!is_directory(filesPath))
		{
			stringstream stm;
			stm << filesPath.string() << " (for files associated with address 0x" << addrStr << ") exists, but it is not a directory!";

			throw runtime_error(stm.str());
		}

		reconfig.SetStateFileName(stateFilePath.string().c_str());
		reconfig.SetStorageDirectoryName(filesPath.string().c_str());
		reconfig.Save(configFilePath.string());
	}
	else if (!is_regular_file(configFilePath))
	{
		stringstream stm;
		stm << configFilePath.string() << " (the config file for the storage controller associated with address 0x" << addrStr << ") eixsts, but it is not a regular file!";

		throw runtime_error(stm.str());
	}

	else
		reconfig.Load(configFilePath.string());

	return reconfig;
}

ByteArrayPtr ProxySC::AnyToBytes(const CORBAAny& anyVal) const
{
	EventPropertyValue evp(anyVal, broker);
	return AnyToBytes(evp);
}

ByteArrayPtr ProxySC::AnyToBytes(const EventPropertyValue& anyVal) const
{
	ByteArrayPtr result;
	EventPropertyValue::StorageType sType = anyVal.GetStorageType();

	switch(sType)
	{
	case EventPropertyValue::ST_STRING:
	{
		result.reset(new ByteArray);

		const string str = anyVal.GetString();
		const size_t strLen = str.length();

		result->reserve(strLen);

		for (size_t idx = 0; idx < strLen; ++idx)
			result->push_back(str[idx]);

		break;
	}
	case EventPropertyValue::ST_UINT8_VECTOR:
	{
		result = anyVal.GetByteArray();
		break;
	}

	default:
	{
		stringstream msg;
		msg << "Unsupported conversion to byte array from " << anyVal.DescribeStorageType();

		throw runtime_error(msg.str());
	}

	}

	return result;
}

void ProxySC::BytesToAny(const ByteArray& bytes, CORBAAny& result) const
{
	EventPropertyValue evp(broker);
	evp.SetByteArray(bytes);
	evp.CopyOut(result);
}

void ProxySC::StringToAny(const string& str, CORBAAny& result) const
{
	EventPropertyValue evp(broker);
	evp.SetString(str);
	evp.CopyOut(result);
}

void ProxySC::ClearTransInfo(const string& transID)
{
	TransactionMap::iterator it = transactionMap.find(transID);

	if (it == transactionMap.end())
	{
		stringstream msg;
		msg << "No such transaction ID '" << transID << "' was found";
		throw invalid_argument(msg.str());
	}

	transactionMap.erase(it);
}

void ProxySC::StoreTransInfo(TransactionInfoPtr info)
{
	transactionMap[info->TransID()] = info;
}

string ProxySC::MakeTransactionID()
{
	stringstream transStm;
	transStm << cmdSrcID << ":" << ultos(transID++, 16, 8);

	return transStm.str();
}

ProxySC::TransactionInfoPtr ProxySC::GetTransInfo(const string& transID)
{
	TransactionMap::iterator it = transactionMap.find(transID);

	if (it == transactionMap.end())
	{
		stringstream msg;
		msg << "No such transaction ID '" << transID << "' was found";
		throw invalid_argument(msg.str());
	}

	return it->second;
}

void ProxySC::ProcessCommandCompletion(const SendCommandCompleteEvent& completion)
{
	const string transID = completion.TransactionID();
	TransactionInfoPtr transInfo;

	try							{ transInfo = GetTransInfo(transID); }
	catch(invalid_argument& ex)	{ return; }

	const bool complete = completion.Complete();
	const bool succeeded = completion.Successful();

	if (complete)
		ClearTransInfo(transID);

	if (transInfo->FileOriented())
	{
		bool error = false;
		string errMsg;

		if (completion.HasResultFile())
		{
			string fileName = completion.ResultFile();

			try
			{
				ByteArray temp;
				bool reading = true;
				fstream fs(fileName.c_str(), ios_base::in | ios_base::binary);

				while(reading)
				{
					temp.resize(2048);
					fs.read((char*) &temp.front(), temp.size());
					temp.resize(fs.gcount());
					reading = fs.good() && fs.gcount();

					if (temp.size())
						transInfo->Write(temp);
				}
			}
			catch(exception& ex)
			{
				stringstream msg;
				msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' while transferring result file data to temp file";
				errMsg = msg.str();
				error = true;
			}

			if (completion.ResultFileOwnedByReceiver())
				unlink(fileName.c_str());
		}
		else
		{
			SendCommandCompleteEvent::CORBAAnyVar dataVar = completion.Result();

			try
			{
				ByteArrayPtr data = AnyToBytes(dataVar.in());

				transInfo->Write(*data);
			}
			catch(CORBA::Exception& ex)
			{
				stringstream msg;
				msg << "Caught " << DecodeCORBAException(ex) << " while storing command completion data to temp file";
				errMsg = msg.str();
				error = true;
			}
			catch(std::exception& ex)
			{
				stringstream msg;
				msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' while storing command completion data to temp file";
				errMsg = msg.str();
				error = true;
			}
			catch(...)
			{
				errMsg = "File handling error";
			}
		}

		if (complete || error)
		{
			ByteArray fileContent;

			if (!error)
			{
				try
				{
					transInfo->GetContent(fileContent);
				}
				catch(std::exception& ex)
				{
					stringstream msg;
					msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' reading command completion content from temp file";
					errMsg = msg.str();
					error = true;
				}
			}

			if (error)
			{
				if (!complete)
					ClearTransInfo(transID);

				fileContent.assign((const byte*) errMsg.data(), errMsg.length());
			}

			string fileName = StoreResponseFile(transInfo->FileID(), fileContent, DetermineFileExtension(fileContent));
			string remoteName = path(fileName).filename().string();
			newFileSignal(*this, fileName, remoteName, transInfo->DestID());
		}
	}
	else
	{
		ByteArrayPtr resultingData;
		string msgStr;

		if (completion.HasResultFile())
		{
			string fileName = completion.ResultFile();

			try
			{
				fstream fs(fileName.c_str(), ios_base::in | ios_base::binary);

				fs.seekg(0, ios_base::end);
				streampos fsize = fs.tellg();
				fs.seekg(0, ios_base::beg);

				if (fsize > maxFileSize)
					fsize = maxFileSize;

				resultingData.reset(new ByteArray());
				resultingData->resize(fsize);
				fs.read((char*) &resultingData->front(), resultingData->size());
			}
			catch(std::exception& ex)
			{
				stringstream msg;
				msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' while obtaining content of file " << fileName;
				msgStr = msg.str();
			}

			if (completion.ResultFileOwnedByReceiver())
				unlink(fileName.c_str());
		}
		else
		{
			SendCommandCompleteEvent::CORBAAnyVar dataVar = completion.Result();

			try
			{
				resultingData = AnyToBytes(dataVar.in());
			}
			catch(CORBA::Exception& ex)
			{
				stringstream msg;
				msg << "Caught " << DecodeCORBAException(ex) << " while getting resulting command completion data";

				msgStr = msg.str();
			}
			catch(std::exception& ex)
			{
				stringstream msg;
				msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' while getting resulting command completion data";

				msgStr = msg.str();
			}
			catch(...)
			{
				msgStr = "Caught exception while getting resulting command completion data";
			}
		}

		if (msgStr.length())
		{
			if (!complete)
				ClearTransInfo(transID);

			resultingData.reset(new ByteArray((const byte*) msgStr.data(), msgStr.length()));
		}

		else if (!resultingData)
			resultingData.reset(new ByteArray());

		HandleCommandCompletion(*transInfo, *resultingData, complete || msgStr.length(), succeeded);
		//completeSignal(*this, *resultingData, transInfo->TransNumber(), transInfo->DestID(), complete, transInfo->ResultNum(1));
	}
}

void ProxySC::HandleCommandCompletion(TransactionInfo& transInfo, const ByteArray& bytes, bool complete, bool success)
{
	bool signal = true;

	if (transInfo.ReturnFile())
	{
		signal = false;
		transInfo.Write(bytes);

		if (complete)
		{
			ByteArray fileContent;

			try
			{
				transInfo.GetContent(fileContent);
			}
			catch(std::exception& ex)
			{
				stringstream msg;
				msg << "Caught " << typeid(ex).name() << " '" << ex.what() << "' reading command completion content from temp file";

				string errMsg = msg.str();

				fileContent.assign((const byte*) errMsg.data(), errMsg.length());
			}

			string fileName = StoreResponseFile(transInfo.TransNumber(), fileContent, DetermineFileExtension(fileContent));

			string remoteName = fileName;
			newFileSignal(*this, fileName, remoteName, transInfo.DestID());

			stringstream responseMsg;
			responseMsg << fileContent.size() << " bytes in " << remoteName;

			string responseStr = responseMsg.str();

			ByteArray responseAsData;
			responseAsData.assign((const byte*) responseStr.data(), responseStr.length());

			completeSignal(*this, responseAsData, transInfo.TransNumber(), transInfo.DestID(), true, 1, success);
		}
	}

	if (signal)
		completeSignal(*this, bytes, transInfo.TransNumber(), transInfo.DestID(), complete, transInfo.ResultNum(1), success);
}

void ProxySC::ProcessNewFileDataSet(const DataSetEvent& dataSet, BoardAddressType destinationID)
{
	if (!dataSet.HasGeneratorAddress())
		return;

	EventPropertyValue::UInt8 evt_boardID, evt_taskID;

	dataSet.GetGeneratorAddress(evt_boardID, evt_taskID);

	if ((boardID != evt_boardID) || (taskID != evt_taskID))
		return;

	// get the name of a file to be created
	string scDir = sc.GetConfig().GetStorageDirectoryName();
	string newFileName = dataSet.SuggestSaveFileName(scDir, true, 8);
#ifdef NEW_FILESYSTEM
	string fileBaseName = path(newFileName).filename().string();
#else
	string fileBaseName = path(newFileName).filename();
#endif

	// ensure that the actual file does not exist
	StorageController::FileState::Ptr existingState;

	try														{ existingState = sc.GetFileState(newFileName); }
	catch(StorageController::FileState::FileNotFound& ex) 	{ }

	if (existingState)
	{
		string reportedName = existingState->GetReportedFileName();
		existingState.reset();
		sc.DeleteFile(StorageController::NO_FILE_ID, reportedName, true);
	}

	// convert the new name to the reported name
	string reportedName = RealNameToReportedStr(newFileName);

	// ensure that the file does not already exist
	try														{ sc.DeleteFile(StorageController::NO_FILE_ID, reportedName, true); }
	catch(...)												{ }

	// now store the file
	dataSet.SaveContentToFile(newFileName);

	sc.AddFile(fileBaseName);
	StorageController::FileState::Ptr newFileState = sc.GetFileState(fileBaseName);

	string remoteFileName = path(fileBaseName).filename().string();
	newFileSignal(*this, fileBaseName, remoteFileName, destinationID);
}

}
}
