//-----------------------------------------------------------------------------------
// <copyright file="TelemetrySubsystem.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 TelemetrySubsystem, which factors out the telemetry composition
//  objects for PayloadInterfaceController and SV3RemoteController
// </summary>
//
// <owner>Mike Cookson</owner>
//-----------------------------------------------------------------------------------

#include "TelemetrySubsystem.h"
#include "PayloadInterfaceController.h"
#include "TelemetrySettingsChangedEvent.h"

using namespace std;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::EventLib;

namespace LiquidRobotics {
namespace PayloadInterface {

TelemetrySubsystem::TelemetrySubsystem(
		const string& _telemConfigPath,
		EventTransceiver& _evtTrans,
		Logger& _logger,
		const PayloadInterfaceController_Config& _config,
		IObjectBroker& _broker,
		PayloadManagerClient& _pmClient)

: telemConfigPath(_telemConfigPath)
, evtTrans(_evtTrans)
, logger(_logger)
, config(_config)
, broker(_broker)
, pmClient(_pmClient)
{
}

void TelemetrySubsystem::Initialize(bool startTimeoutThread)
{
	// subscribe to event receive generally to handle telemetry
	connectionToEventReceive = evtTrans.SubscribeAllEvents(boost::bind(&TelemetrySubsystem::EventToTelemetry, this, _1, _2));

	// configure EventTransciever to create the appropriate derived class for TelemetryChanged events
	const EventSettings& telemetryChangedEvent = config.GetTelemetryChangedEventSettings();
	evtTrans.RegisterEventClassFactory(telemetryChangedEvent.GetType(), new EventTransceiver::TEventClassFactory<TelemetrySettingsChangedEvent>);

	// subscribe to the received event
	EventTransceiver::InformSignalType& sig1 = evtTrans.SignalForEventName(telemetryChangedEvent.GetName());
	connectionToTelemetryChanged.reset(
			new ScopedConnection(
					sig1.connect(
							boost::bind(&TelemetrySubsystem::TelemetryChangedHandler, this, _1, _2)
							)));

	// load the telemetry configuration from its own XML file
	telemConfigPtr.reset(new TelemetryBuffer::Config(broker, pmClient));
	telemConfigPtr->Load(telemConfigPath);

	// load the telemetry buffer
	telemBufPtr.reset(new TelemetryBuffer(broker, *telemConfigPtr));

	// subscribe to callbacks from TelemetryBuffer
	connectionToTelemetryComplete = telemBufPtr->telemetryReportSubscriber.Subscribe(boost::bind(&TelemetrySubsystem::TelemetryReportComplete, this, _1, _2, _3, _4));

	// start the background thread that waits for telemetry buffer timeouts
	// and publishes them, if so instructed
	if (startTimeoutThread)
		telemBufPtr->InitializeAutoPublishWaitingTelemetry(logger);
}

void TelemetrySubsystem::Shutdown()
{

}

void TelemetrySubsystem::SavePosition(double latitude, double longitude)
{
	 if (telemBufPtr)
		 telemBufPtr->SavePosition(latitude, longitude);
}

void TelemetrySubsystem::EventToTelemetry(LiquidRobotics::EventLib::EventTransceiver& sender, const LiquidRobotics::EventLib::IEventBase& receivedEvent)
{
	{
		Logger::Locked lockedLog(logger);

		if (lockedLog->LogEnabled(Logger::LT_DEBUG) || !IsSpecialPurposeEvent(receivedEvent.GetEventSettings()))
		{
			lockedLog->Info << "EventToTelemetry for ";

			if (lockedLog->LogEnabled(Logger::LT_DEBUG))
				receivedEvent.DescribeGeneric(lockedLog->Info);
			else
				receivedEvent.Describe(lockedLog->Info);

			lockedLog->Info << endl;
		}
	}

	try
	{
		telemBufPtr->ProcessEvent(receivedEvent);
	}
	catch(CORBA::Exception& ex)
	{
		Logger::Locked(logger)->Error << "Caught CORBA exception: " << DecodeCORBAException(ex) << " while processing event telemetry" << endl;
	}
	catch(exception& ex)
	{
		Logger::Locked(logger)->Error << "Caught exception " << typeid(ex).name() << " '" << ex.what() << "' while processing event telemetry" << endl;
	}
}

bool TelemetrySubsystem::IsSpecialPurposeEvent(const EventSettings& settings) const
{
	bool result = false;

	if ((settings == config.GetAddressedMessageEventSettings())
	 || (settings == config.GetFCCIncomingMessageEventSettings())
	 || (settings == config.GetGpsSampleEventSettings())
	 || (settings == config.GetTelemetryChangedEventSettings()))

		result = true;

	return result;
}

void TelemetrySubsystem::TelemetryChangedHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	const EventSettings& telemetryChangedEvent = config.GetTelemetryChangedEventSettings();
	const TelemetrySettingsChangedEvent& evt =
		EventTransceiver::UpcastEventRef<TelemetrySettingsChangedEvent>(receivedEvent, telemetryChangedEvent,
				"TelemetrySubsystem::TelemetryChangedHandler");

	stringstream description;
	evt.Describe(description);
	Logger::Locked(logger)->Info << "Telemetry changed: " << description.str() << endl;
	telemBufPtr->ProcessTelemetryChangedEvent(evt);
}

void TelemetrySubsystem::CheckForTelemetryPublicationTimeout()
{
	telemBufPtr->PublishTimedOut(logger);
}

}
}



