//----------------------------------------------------------------------------------
// <copyright file="SensorProgram.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 SensorProgram, a basic implementation of an ICommandable
//	 service with events and stubs for ISensor; use with CORBA tie classes.  Note
//   that GPS and shutdown CORBA events are handled if the corresponding event names
//   are defined; there is also support for firing command completion events.
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#include "SensorProgram.h"
#include "GPSSampleEvent.h"
#include "SendCommandCompleteEvent.h"

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

namespace LiquidRobotics {
namespace EventLib  {

SensorProgram::SensorProgram(IObjectBroker& _broker, const SensorProgramSettings& _settings, Logger& _logger)
: EventTransceiver(_settings.GetEventTransceiverSettings(), _broker, _logger)
, broker(_broker)
, settings(_settings)
, logger(_logger)
{
}

SensorProgram::~SensorProgram()
{
}

void SensorProgram::Initialize(Object_ptr _pServer, PayloadManagerClient::Ptr _pmClient)
{
	// derived class can override to provide application-specific handler and then call base class (this)

	pmClient = _pmClient;

	using namespace LiquidRobotics::SMAC;

	const EventSettings gpsSettings = settings.GetGPSEvent();

	if (gpsSettings.GetName().length())
	{
		RegisterEventClassFactory(gpsSettings.GetType(), new EventTransceiver::TEventClassFactory<GPSSampleEvent>());

		InformSignalType& sig0 = SignalForEventName(gpsSettings.GetName());
		connectionToGPSEvent.reset( new ScopedConnection(sig0.connect(boost::bind(&SensorProgram::GPSSampleHandler, this, _1, _2))) );

		gpsSignalSubscriber.reset(new GPSSignalSubscriber(gpsSampleEventSignal, firingMutex));
	}

	const EventSettings shutdownSettings = settings.GetShutdownEvent();

	if (shutdownSettings.GetName().length())
	{
		RegisterEventClassFactory(shutdownSettings.GetType(), new EventTransceiver::TEventClassFactory<EventBase>());

		InformSignalType& sig0 = SignalForEventName(shutdownSettings.GetName());
		connectionToShutdownEvent.reset( new ScopedConnection(sig0.connect(boost::bind(&SensorProgram::ShutdownHandler, this, _1, _2))) );

		shutdownSignalSubscriber.reset(new ShutdownSignalSubscriber(shutdownSignal, firingMutex));
	}

	EventTransceiver::Initialize(_pServer);
}

void SensorProgram::GPSSampleHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	// derived class can override to provide application-specific handler and then call base class (this)

	const EventSettings gpsSettings = settings.GetGPSEvent();
	const GPSSampleEvent& sample = EventTransceiver::UpcastEventRef<GPSSampleEvent>(receivedEvent, gpsSettings, "SensorProgram::GPSSampleHandler");

	GPSSampleHandlerAsSample(sender, sample);
}

void SensorProgram::GPSSampleHandlerAsSample(EventTransceiver& sender, const GPSSampleEvent& receivedEvent)
{
	ILockableConceptLock lock(firingMutex);
	gpsSampleEventSignal(receivedEvent);
}

void SensorProgram::ShutdownHandler(EventTransceiver& sender, const IEventBase& receivedEvent)
{
	ILockableConceptLock lock(firingMutex);
	shutdownSignal();
	lock.unlock();
	broker.QueueShutdown();
}

// IPingable operations
::CORBA::Double SensorProgram::Ping()
{
	// derived class can override to change ping behavior
	::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
		result = 0;

	return result;
}

// IPayloadDevice
::LiquidRobotics::SMAC::EnumerationData* SensorProgram::GetEnumerationData()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	EnumerationData_var result;
	result = new EnumerationData;
	return result._retn();
}

void SensorProgram::PowerOff(::LiquidRobotics::SMAC::PowerOffTimeType offDelay)
{
	// derived class can override to provide application-specific handler
}

void SensorProgram::PowerOn()
{
	// derived class can override to provide application-specific handler
}

::LiquidRobotics::SMAC::BoardIDType SensorProgram::boardID()
{
	// derived class can override to change implementation
	return pmClient->GetMyBoardID();
}

::LiquidRobotics::SMAC::PayloadDescription* SensorProgram::description()
{
	// derived class can override to change implementation
	Desc::CORBAEntity_var result = settings.GetPayloadManagerClientSettings().GetPayloadDescription().GetEntity();
	return result._retn();
}

::LiquidRobotics::SMAC::EPayloadEnableState SensorProgram::enableState()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	return EPES_ENABLED;
}

::LiquidRobotics::SMAC::PowerOffTimeType SensorProgram::requestOffTime()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	return (PowerOffTimeType) 0;
}

::LiquidRobotics::SMAC::PowerOnTimeType SensorProgram::requestOnTime()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	return (PowerOnTimeType) 0;
}

::LiquidRobotics::SMAC::ComponentVersion* SensorProgram::softwareVersion()
{
	// derived class can override to change implementation

	using namespace LiquidRobotics::SMAC;

	ComponentVersion_var result;
	result = new ComponentVersion;

	const SensorProgramSettings::Version& softVer = settings.GetSoftwareVersion();
	string softVerStr = softVer.ToString();

	result->versionString = CORBA::string_dup(softVerStr.c_str());
	result->majorVersion = softVer.GetMajor();
	result->minorVersion = softVer.GetMinor();
	result->subminorVersion = softVer.GetSubMinor();

	return result._retn();
}

::LiquidRobotics::SMAC::TaskIDType SensorProgram::taskID()
{
	// derived class can override to change implementation
	return pmClient->GetMyTaskID();
}

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

	// derived class should override to handle commands

	stringstream msg;
	msg << typeid(*this).name() << " has no implementation for SendCommand";
	ThrowCORBAException2<BadParameter>("command", msg.str());
}

// ISensor
::LiquidRobotics::SMAC::SensorSample* SensorProgram::SampleNow(const ::CORBA::Any& sampleParameter)
{
	// derived class can override to provide application-specific handler
	return NULL;
}

void SensorProgram::SetSamplePending(const ::CORBA::Any& sampleParameter)
{
	// derived class can override to provide application-specific handler
}

::LiquidRobotics::SMAC::ComponentVersion* SensorProgram::hardwareVersion()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	ComponentVersion_var result;
	result = new ComponentVersion;

	SensorProgramSettings::Version hwVersion;
	string hwVerStr = hwVersion.ToString();

	result->versionString = CORBA::string_dup(hwVerStr.c_str());
	result->majorVersion = hwVersion.GetMajor();
	result->minorVersion = hwVersion.GetMinor();
	result->subminorVersion = hwVersion.GetSubMinor();

	return result._retn();
}

::LiquidRobotics::SMAC::SensorSampleIntervalType SensorProgram::sensorSampleInterval()
{
	// derived class can override to provide application-specific handler

	using namespace LiquidRobotics::SMAC;

	return (SensorSampleIntervalType) 0;
}

void SensorProgram::sensorSampleInterval(::LiquidRobotics::SMAC::SensorSampleIntervalType _v)
{
	// derived class can override to provide application-specific handler
}

::CORBA::Boolean SensorProgram::samplePending()
{
	// derived class can override to provide application-specific handler
	return (::CORBA::Boolean) 0;
}

void SensorProgram::FireCommandCompleteEvent(const string& result, const string& requester, const string& transactionID, bool complete, bool success)
{
	SendCommandCompleteEvent evt(settings.GetCommandCompleteEvent(), broker);
	evt.Complete(complete);
	evt.Requester(requester);
	evt.Successful(success);
	evt.TransactionID(transactionID);
	evt.ResultVariantString(result);

	{
		Logger::Lock logLock(logger);
		logger.Info << "Firing CommandComplete event: ";
		evt.Describe(logger.Info);
		logger.Info << endl;
	}

	TransmitEvent(evt);
}

}
}
