//-------------------------------------------------------------------------------------------------
// <copyright file="SEProgram.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
// 	Implementation of SEProgram, which orchestrates program startup and shutdown
//  and interfaces the Controller class to CORBA
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include "SEProgram.h"
#include "LqrSMACIdl.h"
#include "StringEvent.h"

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

namespace LiquidRobotics {
namespace SensorExample {

SEProgram::SEProgram(IObjectBroker& _broker, const SEProgramSettings& _settings, Logger& _logger)
: Base(_broker, _settings.GetSensorProgramSettings(), _logger)
, broker(_broker)
, settings(_settings)
, logger(_logger)
, latSave(0)
, lonSave(0)
{
}

SEProgram::~SEProgram()
{
}

void SEProgram::Initialize(Object_ptr _pServer)
{
	Base::Initialize(_pServer);

	ctrl.reset(new SEController(settings.GetControllerSettings(), broker, logger, gpsSignalSubscriber, shutdownSignalSubscriber));
	commandCompletedConnection = ctrl->SubscribeCommandComplete(boost::bind(&SEProgram::OnCommandCompleted, this, _1, _2));
	reportedTagConnection = ctrl->SubscribeReportedTag(boost::bind(&SEProgram::OnReportedTag, this, _1, _2, _3, _4));
	ctrl->Initialize();
}

void SEProgram::Shutdown()
{
	//commandCompletedConnection.reset();
	Base::Shutdown();
}

void SEProgram::OnCommandCompleted(const EventLib::SensorControllerBaseCommandID& cmdID, const string& result)
{
	FireCommandCompleteEvent(result, cmdID.GetRequester(), cmdID.GetTransactionID());
}

void SEProgram::OnReportedTag(const string& data, timeval& time, double lat, double lon)
{
	StringEvent evt(settings.GetTagEventSettings(), broker);
	const PayloadDescription& desc = settings.GetSensorProgramSettings().GetPayloadManagerClientSettings().GetPayloadDescription();

	evt.SetGeneratorAddress(desc.GetBoardID(), desc.GetTaskID());
	evt.SetGenerationLocation(lat, lon);
	evt.SetGenerationTime(time);
	evt.SetString(data);

	TransmitEvent(evt);
}

void SEProgram::GPSSampleHandlerAsSample(EventTransceiver& sender, const GPSSampleEvent& receivedEvent)
{
	latSave = receivedEvent.GetLatitude();
	lonSave = receivedEvent.GetLongitude();

	Base::GPSSampleHandlerAsSample(sender, receivedEvent);
}

void SEProgram::SendCommand(const ::CORBA::Any& command, const char* requester, const char* transactionID, ::CORBA::Any_OUT_arg result)
{
	using namespace LiquidRobotics::EventLib;
	using namespace LiquidRobotics::SMAC;

	if (!ctrl)
		ThrowCORBAException<NotReady>("The controller object has not been instantiated");

	const char* cmd0;

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

	typedef SensorControllerExceptions Exceptions;

	SensorControllerBaseCommandID cmdID(requester, transactionID);
	SEController::ParsingContext context(*ctrl, cmd0, cmdID);

	try
	{
		string resultStr = ctrl->SendCommand(context);
		EventPropertyValue epv(broker);

		result.ptr() = new Any;
		epv.SetUInt8Vector((const EventPropertyValue::UInt8*) resultStr.data(), resultStr.length());
		epv.CopyOut(*result.ptr());
	}
	catch(CORBA::Exception& exc)			// this would not be typically returned from the command
	{
		Logger::Locked(logger)->Error << "Caught " << DecodeCORBAException(exc) << " in SendCommand" << endl;
		throw;
	}
	catch(Exceptions::Aborted& exc)
	{
		string whatStr = exc.what();
		ThrowCORBAException<NotReady>(whatStr);
	}
	catch(Exceptions::CommFailure& exc)
	{
		string whatStr = exc.what();
		ThrowCORBAException<CommFailure>(whatStr);
	}
	catch(Exceptions::NotReady& exc)
	{
		string whatStr = exc.what();
		ThrowCORBAException<NotReady>(whatStr);
	}
	catch(Exceptions::ReturnsAsynchronously& exc)
	{
		ThrowCORBAException<WillCompleteAsynchronously>();
	}
	catch(Exceptions::UnavailableInPowerOffState& exc)
	{
		string whatStr = exc.what();
		ThrowCORBAException<UnavailableInPowerOffState>(whatStr);
	}
	catch(invalid_argument& exc)				// this will be thrown if an invalid argument was provided, typically for the command
	{
		string whatStr = exc.what();
		ThrowCORBAException2<BadParameter>("command", whatStr);
	}
	catch(std::exception& exc)
	{
		string whatStr = exc.what();
		stringstream msg;
		msg << "Caught exception " << typeid(exc).name() << " '" << whatStr << "'";
		ThrowCORBAException<NotReady>(msg.str());
	}
	catch(...)
	{
		ThrowCORBAException<NotReady>("Caught unknown exception type");
	}
}

}
}
