//------------------------------------------------------------------------------
// <copyright file="VirtualRelayProgram.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 VirtualRelayProgram class
// </summary>
//
// <owner>Arash Ushani</owner>
//------------------------------------------------------------------------------

#include "CryptoProcessor.h"
#include "VirtualRelayMessage.h"
#include "VirtualRelayProgram.h"
#include "VirtualRelayProgramSettings.h"
#include "VirtualRelayController.h"
#include "FCCMessageEvent.h"
#include "ChannelStateEvent.h"
#include "ChannelRequestEvent.h"
#include "LqrSMACGetErrorDescription.h"
#include "LqrSMACLogger.h"
#include "PayloadMessage.h"
#include "FCCMessage.h"
#include "nsextend.h"
#include <vector>
#include <errno.h>
#include <stdint.h>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace CORBA;
using namespace LiquidRobotics::CryptoLib;
using namespace LiquidRobotics::EventLib;
using namespace LiquidRobotics::SMAC;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::PayloadInterface;

namespace LiquidRobotics {
namespace VirtualRelay {

const string VirtualRelayProgram::CMD_SET_SLEEP_TIME = "SETSLEEPTIME";
const string VirtualRelayProgram::CMD_SET_RETRY_RATE = "SETRETRYRATE";
const string VirtualRelayProgram::CMD_SET_NUM_RETRIES = "SETNUMRETRIES";
const string VirtualRelayProgram::CMD_SET_TELEMETRY_INTERRUPT = "SETTELEMINTERRUPT";
const string VirtualRelayProgram::CMD_SET_ACK_INTERRUPT = "SETACKINTERRUPT";
const string VirtualRelayProgram::CMD_SET_TIMEOUT = "SETTIMEOUT";

VirtualRelayProgram::VirtualRelayProgram(IObjectBroker& _broker, VirtualRelayProgramSettings& _settings, Logger& _logger)
: EventTransceiver(_settings.GetEventTransceiverSettings(), _broker, _logger)
, broker(_broker)
, settings(_settings)
, logger(_logger)
{
}

VirtualRelayProgram::~VirtualRelayProgram()
{
	Logger::Locked(logger)->Debug << "Virtual Relay Program Destructor..." << endl;
}

void VirtualRelayProgram::Initialize(Object_ptr _pServer, PayloadManagerClient::Ptr _pmClient)
{
	pmClient = _pmClient;
	Logger::Locked(logger)->Info << "--------------------------------" << endl;

	for (unsigned int i = 0; i<settings.GetControllerSettings().size(); i++)
	{
		int address = (boardID()<<8) + settings.GetControllerSettings().at(i).GetCommsTaskID();

		Logger::Locked(logger)->Debug << "Making Controller At Address: " << std::hex << (int) address << endl;
		controllers[address].reset(new VirtualRelayController(settings.GetControllerSettings()[i], logger));

		controllers[address]->SetBoardID(boardID());

		ScopedConnectionPtr controllerIncomingMessageConnection = controllers[address]->SubscribeToIncomingMessages(boost::bind(&VirtualRelayProgram::HandleIncomingMessageFromShore, this, _1, _2));
		ScopedConnectionPtr controllerChannelRequestConnection = controllers[address]->SubscribeToChannelRequests(boost::bind(&VirtualRelayProgram::HandleChannelRequests, this, _1, _2));
		ScopedConnectionPtr controllerTelemetryTimeoutConnection = controllers[address]->SubscribeToTelemetryTimeout(boost::bind(&VirtualRelayProgram::HandleTelemetryTimeout, this, _1));

		controllerIncomingMessageConnections.push_back(controllerIncomingMessageConnection);
		controllerChannelRequestConnections.push_back(controllerChannelRequestConnection);
		controllerTelemetryTimeoutConnections.push_back(controllerTelemetryTimeoutConnection);
	}

	SubscribeToEvents();

	// initializing event transceiver
	EventTransceiver::Initialize(_pServer);

	// initialize timer collection
	timerCollection.Initialize();

	CryptoProcessor::test();

	Logger::Locked(logger)->Info << "VirtualRelay Version " << std::dec << MAJOR << "." << MINOR << "." << BUILD << endl;

	boost::thread(boost::bind(&VirtualRelayProgram::GetVehicleUniqueID, this));
}

::CORBA::Double VirtualRelayProgram::Ping()
{
	using namespace LiquidRobotics::SMACFramework;
	stringstream msg;
	msg << "VirtualRelayProgram::Ping: ";

	::CORBA::Double result;
	timeval tv;
	int status = gettimeofday(&tv, NULL);
	int errSave = errno;

	if (!status)
	{
		result = tv.tv_usec;
		result /= 1000000.0;
		result += tv.tv_sec;
	}
	else
	{
		msg << "(gettimeofday returned error #" << errSave << " '" << GetErrorDescription(errSave) << "') ";
		result = 0;
	}

	Logger::Locked(logger)->Info.precision(15);
	Logger::Locked(logger)->Info << msg.str() << result << endl;
	return result;
}

::LiquidRobotics::SMAC::EnumerationData* VirtualRelayProgram::GetEnumerationData()
{
	EnumerationData_var result;
	result = new EnumerationData;
	return result._retn();
}

void VirtualRelayProgram::PowerOff(::LiquidRobotics::SMAC::PowerOffTimeType offDelay)
{
	Logger::Locked(logger)->Debug << "PowerOff offDelay=" << offDelay << endl;
}

void VirtualRelayProgram::PowerOn()
{
	Logger::Locked(logger)->Debug << "PowerOn" << endl;
}

::LiquidRobotics::SMAC::BoardIDType VirtualRelayProgram::boardID()
{
	return pmClient->GetMyBoardID();
}

::LiquidRobotics::SMAC::PayloadDescription* VirtualRelayProgram::description()
{
	Desc::CORBAEntity_var result = settings.GetPayloadManagerClientSettings().GetPayloadDescription().GetEntity();
	return result._retn();
}

::LiquidRobotics::SMAC::EPayloadEnableState VirtualRelayProgram::enableState()
{
	return EPES_ENABLED;
}

::LiquidRobotics::SMAC::PowerOffTimeType VirtualRelayProgram::requestOffTime()
{
	return (PowerOffTimeType) 1;
}

::LiquidRobotics::SMAC::PowerOnTimeType VirtualRelayProgram::requestOnTime()
{
	return (PowerOnTimeType) 1;
}

::LiquidRobotics::SMAC::ComponentVersion* VirtualRelayProgram::softwareVersion()
{
	ComponentVersion_var result;
	result = new ComponentVersion;
	stringstream ss;
	ss << MAJOR << "." << MINOR << "." << BUILD;
	result->versionString = CORBA::string_dup(ss.str().c_str());
	result->majorVersion = 1;
	result->minorVersion = 0;
	result->subminorVersion = 0;
	return result._retn();
}

::LiquidRobotics::SMAC::TaskIDType VirtualRelayProgram::taskID()
{
	return pmClient->GetMyTaskID();
}

void VirtualRelayProgram::ReadVehicleUniqueID()
{
	Logger::Locked(logger)->Info << "Sending Read Vehicle Unique ID Command" << endl;

	FCCMessage::COMM_MGR_RX_BUFFER readUniqueID;

	readUniqueID.header.type = 0x0A;
	readUniqueID.header.packetID = 0;
	readUniqueID.header.size = sizeof(FCCMessage::CMD_FMT_COMMON) + sizeof(FCCMessage::CMD_FMT_10);

	readUniqueID.type.fmtID = FCCMessage::FMT_RCV_CMD;
	readUniqueID.type.command = FCCMessage::CMD_READ_UNIQUE_ID;

	readUniqueID.command.field3 = 0;
	readUniqueID.command.field4 = 0;
	readUniqueID.command.field5 = 0;
	readUniqueID.command.field6 = 0;
	readUniqueID.command.field7 = 0;
	readUniqueID.command.latitude = 0;
	readUniqueID.command.longitude = 0;

	UInt8Vector msg((char*) &readUniqueID, ((char*) &readUniqueID) + sizeof(FCCMessage::COMM_HEADER) + readUniqueID.header.size - 2);
	VirtualRelayMessage::AppendCRC(msg);

	// Insert 0x0001 for ICD
	msg.insert(msg.begin(), 0);
	msg.insert(msg.begin(), 1);
	FireIncomingCommand(msg, (boardID()<<8) + taskID());
}

void VirtualRelayProgram::SetDefaultCommsToIridium()
{
	Logger::Locked(logger)->Info << "Setting Default Comms to Iridium" << endl;

	FCCMessage::COMM_MGR_RX_BUFFER setDefaultComms;

	setDefaultComms.header.type = 0x0A;
	setDefaultComms.header.packetID = 0;
	setDefaultComms.header.size = sizeof(FCCMessage::CMD_FMT_COMMON) + sizeof(FCCMessage::CMD_FMT_10);

	setDefaultComms.type.fmtID = FCCMessage::FMT_RCV_CMD;
	setDefaultComms.type.command = FCCMessage::CMD_SET_DEFAULT_COMM_CHANNEL;

	setDefaultComms.command.field3 = 1; // Iridium
	setDefaultComms.command.field4 = 0;
	setDefaultComms.command.field5 = 0;
	setDefaultComms.command.field6 = 0;
	setDefaultComms.command.field7 = 0;
	setDefaultComms.command.latitude = 0;
	setDefaultComms.command.longitude = 0;

	UInt8Vector msg((char*) &setDefaultComms, ((char*)&setDefaultComms) + sizeof(FCCMessage::COMM_HEADER) + setDefaultComms.header.size - 2);
	VirtualRelayMessage::AppendCRC(msg);

	// Insert 0x0001 for ICD
	msg.insert(msg.begin(), 0);
	msg.insert(msg.begin(), 1);

	FireIncomingCommand(msg, (boardID()<<8) + taskID());
}

void VirtualRelayProgram::SendCommand(const ::CORBA::Any& command, const char* requester, const char* transactionID, ::CORBA::Any_OUT_arg result)
{
	using namespace LiquidRobotics::SMACFramework;
	const char* cmd0;

	if (!(command >>= cmd0))
	{
		Logger::Locked(logger)->Error << "Incoming command type is not a string" << endl;
		ThrowCORBAException2<BadParameter>();
	}

	Logger::Locked(logger)->Info << "Virtual Relay Command:" << cmd0 << "' requester='" << requester << "' transactionID='" << transactionID << "'" << endl;

	const string cmd = cmd0;

	const string resultStr = ProcessCommand(cmd);
	Logger::Locked(logger)->Info << "Returning result='" << resultStr << "'" << endl;

	result.ptr() = new Any;
	*result.ptr() <<= resultStr.c_str();
}

string VirtualRelayProgram::ProcessCommand(const string& command)
{
	string cmd = boost::to_upper_copy(command);
	string result = "ERROR";

	try
	{
		std::string configStr, addressStr, settingStr;
		unsigned int i;

		cmd.erase( 0, cmd.find_first_not_of(" \t") );
		i = cmd.find_first_of(" \t");
		if (i==string::npos)
			return result;
		configStr = cmd.substr(0, i);
		cmd.erase(0, i);

		cmd.erase( 0, cmd.find_first_not_of(" \t") );
		i = cmd.find_first_of(" \t");
		if (i==string::npos)
			return result;
		addressStr = cmd.substr(0, i);
		cmd.erase(0, i);

		cmd.erase( 0, cmd.find_first_not_of(" \t") );
		i = cmd.find_first_of(" \t");
		if (i==string::npos)
			settingStr = cmd;
		else
			settingStr = cmd.substr(0, i);
		cmd.erase(0, i);

		Logger::Locked(logger)->Info << "Command: " << configStr << ", " << addressStr << ", " << settingStr << endl;

		int address;
		int setting;

		try
		{
			address = boost::lexical_cast<int>(addressStr.c_str());
		}
		catch (boost::bad_lexical_cast ex)
		{
			try
			{
				stringstream ss;
				ss << std::hex << addressStr;
				ss >> address;
			}
			catch (...)
			{
				result = "Cannot Parse Address from: " + addressStr;
				return result;
			}
		}

		if (address<0x100)
			address += (boardID()<<8);

		try
		{
			setting = boost::lexical_cast<int>(settingStr.c_str());
		}
		catch (boost::bad_lexical_cast ex)
		{
			try
			{
				stringstream ss;
				ss << std::hex << settingStr;
				ss >> setting;
			}
			catch (...)
			{
				result = "Cannot Parse setting from: " + settingStr;
				return result;
			}
		}

		if (controllers.find(address)==controllers.end())
		{
			stringstream ss;
			ss << "Invalid Address Received: " << address;
			result = ss.str();
		}
		else
		{
			ControllerPtr controller = controllers[address];

			bool success = true;

			if (configStr.compare(CMD_SET_SLEEP_TIME)==0)
				controller->SetSleepTime(setting);
			else if (configStr.compare(CMD_SET_RETRY_RATE)==0)
				controller->SetRetryRate(setting);
			else if (configStr.compare(CMD_SET_NUM_RETRIES)==0)
				controller->SetRetries(setting);
			else if (configStr.compare(CMD_SET_TELEMETRY_INTERRUPT)==0)
				controller->SetTelemetryInterrupt(setting==1);
			else if (configStr.compare(CMD_SET_ACK_INTERRUPT)==0)
				controller->SetAckInterrupt(setting==1);
			else if (configStr.compare(CMD_SET_TIMEOUT)==0)
				controller->SetTimeout(setting);
			else
			{
				success = false;
				stringstream ss;
				ss << "Unknown command received: " << configStr;
				result = ss.str();
			}

			if (success)
			{
				stringstream ss;
				ss << "Set " << configStr << " for 0x" << std::hex << address << std::dec << " to " << setting;
				result = ss.str();

				settings.Save(VIRTUAL_RELAY_CONFIG_FILE);
			}
		}
	}
	catch (std::exception& ex)
	{
		Logger::Locked(logger)->Error << "Exception occurred while processing command: " << typeid(ex).name() << ", " << ex.what() << endl;
	}
	catch (...)
	{
		Logger::Locked(logger)->Error << "Caught unknown exception while processing command: " << endl;
	}

	Logger::Locked(logger)->Info << result << endl;
	return result;
}

void VirtualRelayProgram::GetVehicleUniqueID()
{
	receivedUniqueID = false;

	boost::this_thread::sleep(posix_time::milliseconds(5000));

	while (!receivedUniqueID)
	{
		ReadVehicleUniqueID();
		boost::this_thread::sleep(posix_time::milliseconds(120000));
	}
}

void VirtualRelayProgram::HandleIncomingMessageFromShore(const VirtualRelayController& sender, const UInt8Vector& incomingMessage)
{
	FireIncomingCommand(incomingMessage, sender.GetAddress());
}

void VirtualRelayProgram::HandleChannelRequests(const VirtualRelayController& sender, bool state)
{
	FireChannelRequestEvent(state, sender.GetProtocolRevision());
}

void VirtualRelayProgram::HandleTelemetryTimeout(VirtualRelayController& sender)
{
	// Default back to Iridium
	SetDefaultCommsToIridium();

	// Queue messages to be sent back through the Float C&C (up to a max of 10 at a time)
	int max = 10;
	while (sender.HighestPriorityMessage()>=VirtualRelayPacketComparision::COMMAND_RESPONSE && max-->0)
	{
		Logger::Locked(logger)->Info << "Have a high priority message to send back over Iridium." << endl;
		UInt8Vector msgToSendBack = sender.PopMessage();

		FireMessageViaIridium(msgToSendBack);
	}
}

void VirtualRelayProgram::FireIncomingCommand(const VirtualRelayProgram::UInt8Vector& command, int sourceAddress)
{
	FCCMessageEvent event(settings.GetFCCIncomingMessageEvent(), broker);

	event.SetSource(sourceAddress);
	event.SetDestination(FCCMessage::FLOAT_TARGET_COMMAND_INTERFACE);
	event.SetMessage(command);

	Logger::Locked(logger)->Info << "Firing ";
	event.Describe(Logger::Locked(logger)->Info);
	Logger::Locked(logger)->Info << endl;
	TransmitEvent(event);
	Logger::Locked(logger)->Debug << "Fired!" << endl;
}

void VirtualRelayProgram::FireMessageViaIridium(const UInt8Vector& message)
{
	FCCMessageEvent event(settings.GetFCCIncomingMessageEvent(), broker);

	event.SetSource((boardID()<<8) + taskID());
	event.SetDestination(FCCMessage::FLOAT_TARGET_FORWARD_MESSAGE);

	UInt8Vector toSend = message;

	FCCMessage::LRI_FORWARD_INFO header;
	header.msgFormat = 0x02;
	header.destination = FCCMessage::FLOAT_TARGET_IRIDIUM_INTERFACE;
	header.source = (boardID()<<8) + taskID();

	toSend.insert(toSend.begin(), (char*) &header, ((char*) &header) + sizeof(FCCMessage::LRI_FORWARD_INFO));
	event.SetMessage(toSend);

	Logger::Locked(logger)->Info << "Firing Message Via Iridium: ";
	event.Describe(Logger::Locked(logger)->Info);
	Logger::Locked(logger)->Info << endl;
	TransmitEvent(event);
	Logger::Locked(logger)->Debug << "Fired Message Via Iridium: ";

	for (unsigned int i=0; i<toSend.size(); i++)
		Logger::Locked(logger)->Debug << utos(toSend[i], 16, 2) << " ";
	Logger::Locked(logger)->Debug << endl;
}

void VirtualRelayProgram::FireChannelRequestEvent(bool state, int protocolRevision)
{
	RequestEvent event(settings.GetRequestEvent(), broker);
	event.SetProtocolRevision(protocolRevision);

	if (state)
		event.SetActiveRequest();
	else
		event.SetCancelRequest();

	Logger::Locked(logger)->Info << "Firing ";
	event.Describe(Logger::Locked(logger)->Info);
	Logger::Locked(logger)->Info << "!" << endl;
	TransmitEvent(event);
	Logger::Locked(logger)->Debug << "Fired!" << endl;
}

void VirtualRelayProgram::FCCOutgoingMessageEventHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	const FCCMessageEvent* evtPtr = NULL;

	try
	{
		evtPtr = static_cast<const FCCMessageEvent*>(&receivedEvent);
	}
	catch(std::exception& ex)
	{
		Logger::Locked(logger)->Error << "Error making event pointer" << endl;
		Logger::Locked(logger)->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while processing FCC Message Event" << endl;
	}

	// Finish handling event asynchronously so we don't hold up the SMC's comms
	if (evtPtr)
		boost::thread(boost::bind(&VirtualRelayProgram::FCCOutgoingMessageEventHandler_Asynch, this, *evtPtr));
	else
		Logger::Locked(logger)->Error << "Event Pointer for FCCMessageEvent is null." << endl;

}

void VirtualRelayProgram::FCCOutgoingMessageEventHandler_Asynch(const FCCMessageEvent& receivedEvent)
{
	try
	{
		Logger::Locked(logger)->Info << "Received ";
		receivedEvent.Describe(Logger::Locked(logger)->Info);
		Logger::Locked(logger)->Info << endl;

		int thisTask = (boardID()<<8) + taskID();
		int dest = receivedEvent.GetDestination();

		UInt8Vector messageBytes = *(receivedEvent.GetMessage());
		int messageType = (messageBytes[8] + (messageBytes[9]<<8)) & 0x7FFF;

		if (messageType == PayloadMessage::FLOAT_TELEMETRY_DATA)
			messageBytes.erase(messageBytes.begin(), messageBytes.begin()+10);
		else if (messageType == PayloadMessage::ACK_REQUESTED_PACKET)
			messageBytes.erase(messageBytes.begin(), messageBytes.begin()+14);
		else
		{
			Logger::Locked(logger)->Error << "Unknown Message Type: " << messageType << endl;
			return;
		}

		FCCMessage::PCOMM_MGR_TX_BUFFER level2Message = (FCCMessage::PCOMM_MGR_TX_BUFFER) &messageBytes[0];

		// Who is this message for?
		if (dest==thisTask)
		{
			// Check to see if this packet contains the vehicle's unique ID
			if (!receivedUniqueID && level2Message->header.type==FCCMessage::ACK_WITH_UNIQUE_ID)
			{
				for (ControllerMap::iterator it = controllers.begin(); it!=controllers.end(); it++)
				{
					(*it).second->SetVehicleUniqueID(level2Message->ackUniqueID.uniqueID);

					// Now that we have the vehicle unique ID, we can start up the controller(s)
					(*it).second->Start();
				}

				Logger::Locked(logger)->Info << "Vehicle unique ID is: 0x" << std::hex << level2Message->ackUniqueID.uniqueID << endl;
				receivedUniqueID = true;
			}
		}
		else if (controllers.find(dest)!=controllers.end())
		{
			controllers[dest]->OutgoingMessage(messageBytes);
		}
		else if (dest==0xFFFF)
		{
			Logger::Locked(logger)->Info << "Message was broadcast" << endl;
		}
	}
	catch(std::exception& ex)
	{
		Logger::Locked(logger)->Error << "Caught exception while handling FCC Outgoing Message:" << typeid(ex).name() << " '" << ex.what() << endl;
	}
}

void VirtualRelayProgram::ChannelStateHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	const ChannelStateEvent* evtPtr = NULL;

	try
	{
		evtPtr = static_cast<const ChannelStateEvent*>(&receivedEvent);
	}
	catch(std::exception& ex)
	{
		Logger::Locked(logger)->Error << "Error making event pointer" << endl;
		Logger::Locked(logger)->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while processing Channel State Event" << endl;
	}

	if (evtPtr)
	{
		try
		{
			Logger::Locked(logger)->Info << "Received ";
			((ChannelStateEvent) *evtPtr).Describe(Logger::Locked(logger)->Info);
			Logger::Locked(logger)->Info << endl;

			int protoRev = evtPtr->GetProtocolRevision();
			bool channelOn = evtPtr->GetChannelState();
			int signalStrength = 0xFF;

			if (evtPtr->HasSignalStrength())
				signalStrength = evtPtr->GetSignalStrength();

			for (ControllerMap::iterator it = controllers.begin(); it!=controllers.end(); it++)
			{
				if ( (*it).second->GetProtocolRevision()==protoRev )
				{
					(*it).second->UpdateChannelState(channelOn);
					if (signalStrength!=0xFF)
						(*it).second->UpdateSignalStrength(signalStrength);
				}
			}

		}
		catch(std::exception& ex)
		{
			Logger::Locked(logger)->Error << "Error handling event" << endl;
			Logger::Locked(logger)->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while processing Channel State Event" << endl;
		}
	}
	else
	{
		Logger::Locked(logger)->Error << "Have null event pointer when processing Channel State Event" << endl;
	}
}

void VirtualRelayProgram::ShutdownHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	Logger::Locked(logger)->Info << "Shutting down..." << endl;

	for (ControllerMap::iterator it = controllers.begin(); it!=controllers.end(); it++)
		(*it).second->Shutdown();

	broker.QueueShutdown();
}

void VirtualRelayProgram::SubscribeToEvents()
{
	// subscribe to the shutdown event
	InformSignalType& sig1 = SignalForEventName(settings.GetShutdownEvent().GetName());
	connectionToShutdownEvent.reset(new ScopedConnection(sig1.connect(boost::bind(&VirtualRelayProgram::ShutdownHandler, this, _1, _2))));

	// Subscribe to Outgoing FCC Messages
	InformSignalType& sig2 = SignalForEventName(settings.GetFCCOutgoingMessageEvent().GetName());
	connectionToFCCIncomingMessageEvent.reset(new ScopedConnection(sig2.connect(boost::bind(&VirtualRelayProgram::FCCOutgoingMessageEventHandler, this, _1, _2))));

	// Subscribe to Channel State Messages
	InformSignalType& sig3 = SignalForEventName(settings.GetChannelStateEvent().GetName());
	connectionToChannelStateEvent.reset(new ScopedConnection(sig3.connect(boost::bind(&VirtualRelayProgram::ChannelStateHandler, this, _1, _2))));
}

}
}
