//------------------------------------------------------------------------------
// <copyright file="DemoSensor.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>
// 	Example declaration of a sensor that logs method calls
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef DEMOSENSOR_H_
#define DEMOSENSOR_H_

#include "DemoSensorSettings.h"
#include "IEventBase.h"
#include "EventTransceiver.h"
#include "GPSSampleEvent.h"
#include "PayloadManagerClient.h"
#include "LqrSMACLogger.h"
#include "LqrSMACIdl.h"
#include "LqrSMACIObjectBroker.h"
#include "LqrSMACSerialListener.h"
#include "LqrSMACSerialPort.h"

/*! \class DemoSensor
    \brief Program/server class for the DemoSensor project

	DemoSensor implements the basic CORBA interface for ISensorWithEvents.
	It doesn't have any actual functionality, but it can be compiled and
	there are TODO items for the important next steps.
*/
class DemoSensor
: public LiquidRobotics::EventLib::EventTransceiver
, public LiquidRobotics::SMACFramework::ICommandableBase
{
public:

	typedef LiquidRobotics::SMACFramework::ByteArray ByteArray;
	typedef LiquidRobotics::EventLib::IEventBase IEventBase;
	typedef LiquidRobotics::EventLib::EventTransceiver EventTransceiver;
	typedef LiquidRobotics::EventLib::EventType EventType;
	typedef LiquidRobotics::EventLib::GPSSampleEvent GPSSampleEvent;
	typedef EventTransceiver::Settings EventTransceiverSettings;
	typedef LiquidRobotics::SMACFramework::IObjectBroker IObjectBroker;
	typedef LiquidRobotics::SMACFramework::Logger Logger;
	typedef LiquidRobotics::SMACFramework::PayloadDescription Desc;
	typedef LiquidRobotics::EventLib::PayloadManagerClient PayloadManagerClient;
	typedef LiquidRobotics::SMACFramework::SerialListener SerialListener;
	typedef boost::shared_ptr<SerialListener> SerialListenerPtr;
	typedef LiquidRobotics::SMACFramework::SerialPort SerialPort;

	//! Class constructor for the DemoSensor class: only saves parameters.  Call Initialize before attempting to use it
	DemoSensor(
			IObjectBroker& _broker,					/**< [in] Interface to the CORBA object broker (needed to send or receive events or perform remote invocation) */
			const DemoSensorSettings& _settings,	/**< [in] The settings from the XML file  */
			Logger& _logger							/**< [in] A logger to be used during operation */
			);

	//! Class destructor for the DemoSensor class
	virtual ~DemoSensor();

	//! Second stage initialization, which must be called before the class can be used
	virtual void Initialize(
			Object_ptr _pServer,					/**< [in] CORBA pointer to the tie class for this server (used by EventTransceiver)  */
			PayloadManagerClient::Ptr _pmClient		/**< [in] Pointer to the PayloadManager interface (not registered yet when this function is called) */
			);

	//! Shut down any resources opened by the server
	virtual void Shutdown();

	/////////////////////////////////
	// CORBA Proxy Implementations //
	/////////////////////////////////

	////// IPingable operations /////

	//! Return time of day (seconds since 1-Jan 1970 plus fractional seconds), used to check latency
    virtual ::CORBA::Double Ping();

	//// IPayloadDevice operations //

    //! Return enumeration data (board ID, description, enable state, request on/off time, software version, task ID and other non-standard values) as a set of name/value pairs (not currently implemented)
    virtual ::LiquidRobotics::SMAC::EnumerationData* GetEnumerationData();

    //! Request the payload to power off (not currently implemented)
    virtual void PowerOff(
    		::LiquidRobotics::SMAC::PowerOffTimeType offDelay	/**< [in] The number of seconds before power off is required */
    		 );

    //! Request the payload to power on (not currently implemented)
    virtual void PowerOn();

    //! Return the payload's board ID (upper 8 bits of board address)
    virtual ::LiquidRobotics::SMAC::BoardIDType boardID();

    //! Return a PayloadDescription for the payload
    virtual ::LiquidRobotics::SMAC::PayloadDescription* description();

    //! Return the enable state of the pyaload (not currently implemented)
    virtual ::LiquidRobotics::SMAC::EPayloadEnableState enableState();

    //! Return the number of seconds the payload requires to turn off (not currently implemented)
    virtual ::LiquidRobotics::SMAC::PowerOffTimeType requestOffTime();

    //! Return the number of seconds the payload requires to be off before turning on (not currently implemented)
    virtual ::LiquidRobotics::SMAC::PowerOnTimeType requestOnTime();

    //! Return the software revision components
    virtual ::LiquidRobotics::SMAC::ComponentVersion* softwareVersion();

    //! Return the payload's task ID (lower 8 bits of board address)
    virtual ::LiquidRobotics::SMAC::TaskIDType taskID();

	//// ICommandable operations ////

    //! Invoke a command on the server and return result, throw WillCompleteAsynchronously and send a "command complete" event later for commands that take time to complete
    virtual void SendCommand(
    		const ::CORBA::Any& command,		/**< [in] Command to be invoked (can be any type of value, but usually a string) */
    		const char* requester,				/**< [in] Reference name to send back with "command complete" event */
    		const char* transactionID,			/**< [in] Reference transaction ID to send back with "command complete" event */
    		::CORBA::Any_OUT_arg result			/**< [out] Output from the command (typically a string) */
    		 );

	/////// ISensor operations //////

    //! Take a sensor sample now (not implemented)
    virtual ::LiquidRobotics::SMAC::SensorSample* SampleNow(
    		const ::CORBA::Any& sampleParameter	  /**< [in] Free-form parameter that can be passed in for the sample cycle */
    		);

    //! Push back a sample to be returned as the next reading (not implemented)
    virtual void SetSamplePending(
    		const ::CORBA::Any& sampleParameter  /**< [in] Free-form parameter that can include sensor data */
    		);

    //! Return version information about the hardware attached (not implemented)
    virtual ::LiquidRobotics::SMAC::ComponentVersion* hardwareVersion();

    //! Read the sensor sampling interval (seconds) (not implemented)
    virtual ::LiquidRobotics::SMAC::SensorSampleIntervalType sensorSampleInterval();

    //! Set the sensor sampling interval (seconds) (not implemented)
    virtual void sensorSampleInterval(
    		::LiquidRobotics::SMAC::SensorSampleIntervalType _v  /**< [in] New sampling interval (seconds) */
    		 );

    //! Returns TRUE if there is a sample pending or FALSE if no sample pending report (not implemented)
    virtual ::CORBA::Boolean samplePending();

private:

	typedef boost::signals2::scoped_connection ScopedConnection;
	typedef boost::shared_ptr<ScopedConnection> ScopedConnectionPtr;

    //! Example thread function that runs a command and returns its output asynchronously
	void AsynchCmd(
			const string requester,			/**< [in] Reference requester ID to be included in completion event (from SendCommand parameter) */
			const string transID,			/**< [in] Reference transaction ID to be included in completion event (from SendCommand parameter) */
			unsigned timeoutSec			/**< [in] The number of seconds to wait before responding */
			);

    //! Callback function that receives GPS samples from a CORBA event
	void GPSSampleHandler(
			EventTransceiver& sender,		 /**< [in] Reference of EventTransceiver sending the event */
			const IEventBase& receivedEvent /**< [in] Wrapper for the received event */
			);

	//! Turns power on or off to the sensor, returns TRUE if successful and FALSE if failed
	bool PowerSensor(
				bool powerOn = true,		/**< [in] Turn sensor power on (true) or off (false) */
				bool throwException = true /**< [in] TRUE if throwing an exception on error, FALSE if returning FALSE */
			);

	//! Callback for received data from the serial port
	void OnSerialData(
			SerialListener& sender,			/**< [in] Reference to instance reporting data */
			const ByteArray& data			/**< [in] Data received (0 bytes means end of received data) */
			);

	//! Callback for system shutdown notification
	void OnSystemShutdown(
			EventTransceiver& sender,		 /**< [in] Reference of EventTransceiver sending the event */
			const IEventBase& receivedEvent /**< [in] Wrapper for the received event */
			);

	//! Reports some telemetry as a CORBA event (configure PayloadInterface-telemetry-config.xml to convert this to a binary package)
	void ReportTelemetryExample(
			float data1,					/**< [in] Sensor data item 1 to report */
			uint16_t data2					/**< [in] Sensor data item 2 to report */
			);

	IObjectBroker& broker;									//!< Reference to CORBA object broker interface (from constructor)
	Logger& logger;											//!< Reference to logger interface (from constructor)
	DemoSensorSettings settings;							//!< Copy of settings from XML (from constructor)
	const string gpsEventName;								//!< Copy of GPS event name (from settings)
	EventType gpsEventType;									//!< Copy of GPS event type (from settings)
	ScopedConnectionPtr connectionToGPSEvent;				//!< subscription to GPS event call-back
	double lat;												//!< copy of the latest latitude from GPS
	double lon;												//!< copy of the latest longitude from GPS
	PayloadManagerClient::Ptr pmClient;						//!< reference on PayloadManagerClient, used for addressing and descriptions
	SerialPort::Ptr serialPort;								//!< SerialPort instance (to talk to the sensor)
	SerialListenerPtr serialListener;						//!< SerialListener instance (to get outputs back from the sensor)
	ScopedConnectionPtr connectionToSerialListener;			//!< subscription to SerialListener callback
	ScopedConnectionPtr connectionToSystemShutdownEvent; 	//!< subscription to system shutdown event call-back

	///! \todo add more application-specific items
	// TODO: add more application-specific items
};

#endif /* DEMOSENSOR_H_ */
