//----------------------------------------------------------------------------------
// <copyright file="SBGPCTDProgram.h" 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>
// 	Declarations for SBGPCTDProgram, which orchestrates program startup and shutdown
//  and interfaces the Controller class to CORBA
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#ifndef __SBGPCTDProgram_h__
#define __SBGPCTDProgram_h__

#include "EventTransceiver.h"
#include "LqrSMACIdl.h"
#include "LqrSMACSharedLib.h"
#include "LqrSMACScopedConnection.h"
#include "SBGPCTDController.h"
#include "SBGPCTDProgramSettings.h"
#include "SensorProgram.h"
#include <boost/signals2.hpp>
#include <boost/shared_ptr.hpp>

namespace LiquidRobotics {

namespace EventLib {
	class GPSSampleEvent;
}

//! Classes deployed in the SeabirdGPCTD program
namespace SeabirdGPCTD {

/*! \brief Program role implementation class for SeabirdGPCTD program
 *  \sa SBGPCTDController, SBGPCTDSensor, LiquidRobotics::EventLib::SensorProgram, LiquidRobotics::SMAC::ISensorWithEvents
 *
 *  The Program role is to:
 *
 *  - handle any CORBA related activity
 *  - implement the CORBA interfaces for whatever kind of server is exposed (ISensorWithEvents; largely handled by base class)
 *  - handle invocations on the server (SendCommand)
 *  - derive from EventTransceiver to send and receive events
 *  - instantiate the Controller role class and subscribe to its Boost events, sending out CORBA events for whatever would be appopriate
 */
class SHARED_LIB_PUBLIC SBGPCTDProgram
: public LiquidRobotics::EventLib::SensorProgram
{
public:

	//! Defines the typedefs for ScopedConnection classes (used for holding event subscriptions)
	SCOPED_CONNECTION_TYPEDEFS();

	//! Define a short cut for the base class
	typedef LiquidRobotics::EventLib::SensorProgram Base;

	//! Define a shortcut for EventLib's GPSSampleEvent (don't want "using namespace" in header files)
	typedef LiquidRobotics::EventLib::GPSSampleEvent GPSSampleEvent;

	//! Define a shortcut for SMACFramework's PayloadDescription (don't want "using namespace" in header files)
	typedef LiquidRobotics::SMACFramework::PayloadDescription Desc;

	/*! \brief Class constructor
	 *  \sa Initialize()
	 *
	 *  Performs basic construction, but doesn't allocate any resources.
	 *  Call Initialize() before attempting to use the other methods.
	 */
	SBGPCTDProgram(
			IObjectBroker& _broker,						/**< [in] Reference to the CORBA object broker wrapper class (used to access remote resources and publish local ones) */
			const SBGPCTDProgramSettings& _settings,	/**< [in] Settings for the program and other objects */
			Logger& _logger								/**< [in] Logging object used to to incrementally describe operations */
			);

	/*! \brief Class destructor
	 *
	 *  Call Shutdown() member to ensure that any resources are released
	 *  before allowing the destructor to execute.
	 */
	virtual ~SBGPCTDProgram();

	/*! \brief Initializes the Program and allocates initial resources
	 *  \sa Shutdown()
	 *
	 *  Call Shutdown() to release resources once Initialize() has successfully
	 *  executed.
	 */
	virtual void Initialize(
			Object_ptr _pServer,						/**< [in] CORBA reference to the tie class that was advertized (allows other servers to invoke on this server) */
			PayloadManagerClient::Ptr _pmClient			/**< [in] Interface to PayloadManager, allows the instance to be registered (not fully initialized when called) */
			);

	/*! \brief Release resources before exiting
	 *  \sa Initialize()
	 */
	virtual void Shutdown();

	/*! \brief Handle incoming invocations
	 *  \sa CommandCompleted()
	 *
	 *  Invocations from PayloadInterface and ServerExec arrive at this
	 *  method.  In this case, execution of commands is delegated to the
	 *  Controller role object.
	 *
	 *  Note that this method throw CORBA exceptions; if the command cannot
	 *  be immediately executed and completed, it throw
	 *  WillCompleteAsynchronously and fires a CORBA event to signal
	 *  completion later.
	 */
	virtual void SendCommand(
			const ::CORBA::Any& command,				/**< [in] The command to be executed */
			const char* requester,						/**< [in] A string indicating the requester (used in CORBA completion event if completing asynchronously) */
			const char* transactionID,					/**< [in] A string indicating the transaction ID (used in CORBA completion event if completing asynchronously) */
			::CORBA::Any_OUT_arg result					/**< [out] The result of the invocation if the command is completing immediately */
			 );

protected:

	//! Define a short cut for EventLib's IEventBase (don't want "using namespace" in header files)
	typedef LiquidRobotics::EventLib::IEventBase IEventBase;

	/*! \brief Handler for incoming GPS samples
	 *
	 *  The handler saves the latitude and longitude to location stamp
	 *  outgoing samples.
	 */
	void GPSSampleHandlerAsSample(
			EventTransceiver& sender,					/**< [in] Reference of sending object */
			const GPSSampleEvent& receivedEvent			/**< [in] Reference of received GPS sample */
			);

	/*! \brief Handler for Command Complete events coming from the controller via Boost event
	 *  \sa SendCommand()
	 *
	 *  When a command is completed, the Program sends a Command Complete
	 *  CORBA event to notify the requester and pass back resulting data.
	 */
	void OnCommandCompleted(
			const EventLib::SensorControllerBaseCommandID& cmdID,	/**< [in] Information about the requester that was saved from the SendCommand() invocation */
			const string& result,									/**< [in] Resulting data from the command */
			bool success											/**< [in] true if the command was successful, false if it failed */
			);

private:

	/*! \brief GPCTD asynchronous sample handler coming from the controller via Boost event
	 *  \sa SBGPCTDController::sampleReport, SBGPCTDProgramSettings::GetSampleEventSettings(), SBGPCTDProgramSettings::GetO2SampleEventSettings()
	 *
	 *  The program packages the sample up as a CORBA event and transmits
	 *  it. The sample already comes with latitude, longitude and time from
	 *  the controller since the controller subscribes directly to the GPS
	 *  event.
	 */
	void OnSample(
			SBGPCTDController& sender,					/**< [in] Reference on the originating object */
			const SBGPCTDSensor::Sample& sample,		/**< [in] Sample data (includes date/time and geolocation) */
			bool o2Installed							/**< [in] true if dissolved oxygen sensor is installed, false if not (determines the name of the event being sent) */
			);

	/*! \brief GPCTD serial number information handler coming from the controller via Boost event
	 *  \sa SBGPCTDController::serialNumberReport, SBGPCTDSensor::GetHardwareData()
	 *
	 *  The program uses this information to update the JSON fragment that it
	 *  advertises through PayloadManager; WGMS and shore-side applications
	 *  can use the serial number correlate received telemetry with a specific
	 *  sensor and its calibration history.
	 */
	void OnSerialNumber(
			SBGPCTDController& sender,					/**< [in] Reference on the originating object */
			const string& serialNumber,					/**< [in] Combination of serial numbers from the sensor's GetHardwareData() method */
			const string& softwareVersion				/**< [in] Combination of firmware version and firmware date from the sensor's GetHardwareData() method */
			);

	/*! \brief Handler for changes in the number of samples per report or the sample interval
	 *  \sa SBGPCTDController::sampleTelemetryChange, SBGPCTDStartCmd, SBGPCTDProgramSettings::GetTelemetryChangedEventSettings()
	 *
	 *  The program uses these parameters to send a telemetry changed
	 *  event, which results the sample packing process to be altered.
	 */
	void OnTelemetryChanged(
			SBGPCTDController& sender,					/**< [in] Reference on the originating object */
			unsigned reportSize,						/**< [in] New number of samples per report */
			unsigned reportIntervalSec					/**< [in] New number of seconds between samples */
			);

	IObjectBroker& broker;								//!< Reference to the CORBA object broker wrapper class (used to access remote resources and publish local ones)
	const SBGPCTDProgramSettings& settings;				//!< Settings for the program and other objects
	Logger& logger;										//!< Logging object used to to incrementally describe operations
	double latSave;										//!< Latitude from last valid GPS reading (0 by default)
	double lonSave;										//!< Longitude from last valid GPS reading (0 by default)
	SBGPCTDControllerPtr ctrl;							//!< Smart pointer to the controller
	ScopedConnectionPtr commandCompletedConnection;		//!< Subscription to the controller's command complete Boost event
	ScopedConnectionPtr sampleConnection;				//!< Subscription to the controller's GPCTD sample Boost event
	ScopedConnectionPtr serialNumberConnection;			//!< Subscription to the controller's GPCTD serial number Boost event
	ScopedConnectionPtr telemetryChangedConnection;		//!< Subscription to the controller's telemetry changed event
	PayloadManagerClient::Ptr pmClient;					//!< Smart pointer to PayloadManagerClient
};

}
}

#endif
