//----------------------------------------------------------------------------------
// <copyright file="RegulusREST.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>
//   RegulusREST provides some methods for implementing REST calls to Regulus from a
//	 payload.
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#ifndef __SV3REMOTINGLIB_REGULUSREST_H__
#define __SV3REMOTINGLIB_REGULUSREST_H__

#include <map>
#include <stdexcept>
#include <string>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
#include "LqrSMACByteArray.h"
#include "LqrSMACDeclareException.h"
#include "LqrSMACDummyLogger.h"
#include "LqrSMACSharedLib.h"

#define REGULUS_REST_TYPEDEFS() \
	LiquidRobotics::SV3RemotingLib::RegulusREST RegulusREST

namespace LiquidRobotics {
namespace SV3RemotingLib {

/*! \class RegulusREST
 *  \brief Methods for implementing REST calls to Regulus from a payload
 */
class SHARED_LIB_PUBLIC RegulusREST
{
public:

	DECLARE_EXCEPTION(EmptyCommandException,std::runtime_error);
	DECLARE_EXCEPTION(JSONParsingError,std::runtime_error);
	DECLARE_EXCEPTION(RemoteCommandFormatError,std::runtime_error);
	DECLARE_EXCEPTION(ServerTimeoutOnRequest,std::runtime_error);

	/*! \var LiquidRobotics::SMACFramework::ByteArray ByteArray
	 *  \brief Provides a definition for ByteArray inside of header
	 */
	typedef LiquidRobotics::SMACFramework::ByteArray ByteArray;

	/*! \var typedef std::string string
	 *  \brief Provides a definition for string inside of header
	 */
	typedef std::string string;

	/*! \var typedef std::map<string,string> DefaultPairMap
	 *  \brief Name/value pair map
	 */
	typedef std::map<string,string> DefaultPairMap;

	/*! \var typedef boost::shared_ptr<string> StringPtr
	 *  \brief Defines shared pointer to a string to allow optional definition
	 */
	typedef boost::shared_ptr<string> StringPtr;

	/*! \var typedef boost::shared_ptr<uint8_t> UInt8Ptr
	 *  \brief Defines shared pointer to a UInt8 to allow optional definition
	 */
	typedef boost::shared_ptr<uint8_t> UInt8Ptr;

	/*! \var typedef boost::shared_ptr<uint16_t> UInt16Ptr
	 *  \brief Defines shared pointer to a UInt16 to allow optional definition
	 */
	typedef boost::shared_ptr<uint16_t> UInt16Ptr;

	/*! \var typedef boost::shared_ptr<RegulusREST> Ptr
	 *  \brief Defines shared pointer to a RegulusREST interface
	 */
	typedef boost::shared_ptr<RegulusREST> Ptr;

	/*! \var LiquidRobotics::SMACFramework::Logger Logger
	 *  \brief Provides a definition for Logger (output display) inside of header
	 */
	typedef LiquidRobotics::SMACFramework::Logger Logger;

	/*! \var struct RemoteCommand
	 *  \brief Data values describing a command received from remote
	 */
	struct RemoteCommand
	{
		//! Default constructor
		RemoteCommand();

		//! Constructor with incoming JSON
		RemoteCommand(const string& json);

		string responseTag;						//!< tag to use when responding to the command
		string commandContent;					//!< content to be executed
		string serverTag;						//!< the tag of the server that should execute the command
		bool returnFile;						//!< true if output should be returned as a file, false if using the ack
	};

	/*! \var struct GPSInfo
	 *  \brief GPS data that may be returned from AwaitCommad
	 */
	struct GPSInfo {

		//! Default constructor
		GPSInfo();

		//! Constructor with incoming JSON
		GPSInfo(const string& json);

		bool valid;								//!< true if this structure contains a response
		bool error;								//!< true if the data contains an error, false if it is good
		string errorMessage;					//!< message explaining any errors
		double latitude;						//!< latitude component of the position
		double longitude;						//!< longitude component of the position
		double timestamp;						//!< timestamp from GPS (seconds since 1/1/1970 GMT)
	};

	/*! \var struct ShutdownInfo
	 *  \brief GPS data that may be returned from AwaitCommad
	 */
	struct ShutdownInfo {

		//! Default constructor
		ShutdownInfo();

		//! Constructor with incoming JSON
		ShutdownInfo(const string& json);

		bool shutdown;							//!< true if the host is shutting down this payload down
	};

	/*! \struct PayloadCommandParameter
	 *  \brief Name and type for exposed command parameters
	 */
	struct PayloadCommandParameter
	{
		string parameterType;					//!< type of parameter
		StringPtr parameterName;				//!< name of parameter (optional if only a single bool is defined)
	};

	/*! \typedef std::vector<PayloadCommandParameter> PayloadCommandParameterArray
	 *  \brief An array of command parameters to accompany a command
	 */
	typedef std::vector<PayloadCommandParameter> PayloadCommandParameterArray;

	/*! \struct PayloadCommand
	 *  \brief Defines the variables for an exposed command
	 */
	struct PayloadCommand
	{
		string label;							//!< label to show on WGMS
		string tag;								//!< the tag to get back from Regulus for invocation
		string toolTip;							//!< the tooltip to show on WGMS when hovering

		/*! \var enum ReturnFormat
		 *  \brief Defines the allowed values for the format of the return message
		 */
		enum ReturnFormat
		{
			CMDLINE,							//!< indicates a command line style return
			JSON								//!< return format is JSON encoded
		};

		ReturnFormat returnFormat;					//!< the format of the return value from the invocation
		PayloadCommandParameterArray parameters;	//!< the parameters accepted by the invocation
	};

	/*! \var typedef std::vector<PayloadCommand> PayloadCommandArray
	 *  \brief An array of commands to be included in the registration message
	 */
	typedef std::vector<PayloadCommand> PayloadCommandArray;

	/*! \var struct PRHardware
	 *  \brief Data values related to hardware used by the interface
	 */
	struct PRHardware
	{
		/*! \var typedef std::vector<PRHardware> Vector
		 *  \brief Defines an array of hardware associated with the sensor
		 */
		typedef std::vector<PRHardware> Vector;

		//! Default constructor
		PRHardware();

		//! Initializes from a single element
		void InitFromJSON(
				const string& json						/**< [in] json string for this entry */);

		//! Reads from registration JSON and initializes PRHardware
		static void InitFromRegJSON(
				const string& json,						/**< [in] registration json string */
				Vector& hardware						/**< [in,out] array of hardware values to fill out */);

		//! Updates a JSON string with an array of hardware values; any of the string that are blank are
		//! not included in the output
		static string UpdateJSON(
				const string& json   					/**< [in] original json string */,
				const Vector& hardware					/**< [in] array of hardware values to merge in */);

		string manufacturer;							//!< Name of the manufacturer
		string model;									//!< Model number of the device
		string pn;										//!< Part number of the hardware (LRI's part number)
		string serialNumber;							//!< Serial number of the device
		string swver;									//!< Software version installed on the device
		string tty;										//!< Serial port on which the hardware is connected
		bool powerSpecified;							//!< True if a power switch was specified
		int powerSwitch;								//!< The specified power switch
		string ampsPowerSwitch;							//!< AMPS power switch if any
	};

	/*! \var struct PRInterface
	 *  \brief Data values related to the software interface
	 */
	struct PRInterface
	{
		//! Default constructor
		PRInterface();

		//! Reads from JSON for just this item
		void InitFromJSON(
				const string& json						/**< [in] json string for this item */);

		//! Reads from registration JSON and initializes PRInterface
		void InitFromRegJSON(
				const string& json						/**< [in] registration json string */);

		//! Updates a JSON string with data from this
		string UpdateJSON(
				const string& json   					/**< [in] original json string */) const;

		string pn;										//!< Part number of the software (LRI part number)
		string swver;									//!< Software version installed on the device
	};

	/*! \var typedef std::vector<PRHardware> HardwareVector
	 *  \brief Defines an array of hardware associated with the sensor
	 */
	typedef std::vector<PRHardware> HardwareVector;

	/*! \var struct PayloadRegistration
	 *  \brief Data values to pass to the payload registration function on RegulusREST
	 */
	struct PayloadRegistration
	{
		//! Default constructor
		PayloadRegistration();

		//! Constructor from file
		PayloadRegistration(
				const string& fileName   /**< [in] file name from which to load the instance */);

		//! Returns the JSON form of the data
		string ToJSON(const string& fileName) const;

		//! Write to a file
		void WriteFile(
				const string& fileName   /**< [in] file name towhich to save the instance */);

		//bool enabled;							//!< Is the payload enabled or disabled?
		string tag;								//!< Name of the service in Regulus
		string description;						//!< Description of the service in the manifest
		//bool generateAlarms;					//!< Does the payload generate alarms?
		uint16_t boardAddress;					//!< The address where commands must be directed (write console script)
		uint16_t reportingAddress;				//!< The address from which telemetry is fired
		string corbaName;						//!< CORBA name for the service
		string hostID;							//!< an ID string that represents the host on which the software is running
	};

	//! Constructor providing the address field of the URL (should include port if needed) including logger
	RegulusREST(
			const string& _address /**< [in] Address field of the URL (should include port if needed) */,
			Logger& _logger /**< [in] instance of Logger to use for output */);


	void DelayShutdown(
			const string& id,						/**< [in] Interface ID */
			long seconds							/**< [in] Number of seconds to delay (allows payload to shut down) */);

	//! Returns the payload registration content given a file and a registration structure
	string GetPayloadRegistrationContent(
			const string& fileName,				    /**< [in] Name of file with default values */
			const PayloadRegistration& payloadReg  	/**< [in] Registration data */);

	//! Installs the given JAR file on Regulus
	void InstallJar(
			const string& id,						/**< [in] Interface ID */
			const string& fileName 					/**< [in] Name and path of JAR file to send */);

	//! Waits for the next command from Regulus from any of an array of targets or (optionally) GPS or (optionally) a shutdown notification
	typedef std::vector<RemoteCommand> RemoteCommandVector;
	typedef std::vector<string> IDVector;
	void Poll(
			IDVector& ids,							/**< [in] list of ids to reference (must be at least one) */
			RemoteCommandVector* commands = NULL,	/**< [in] include if there is a desire to receive commands */
			GPSInfo* gpsInfo = NULL,				/**< [in] include if there is a desire to receive GPS information */
			ShutdownInfo* shutdownInfo = NULL,		/**< [in] include if there is a desire to receive information about a shutdown */
			long maxWait = -1,						/**< [in] maximum milliseconds to wait (-1=no limit) */
			long maxCommands = 1					/**< [in] maximum number of commands to accept */);

	//! Registers the payload with Regulus
	void RegisterPayload(
			const string& id,						/**< [in] Interface ID */
			const string& fileName,				    /**< [in] Name of file with default values */
			const PayloadRegistration& payloadReg, 	/**< [in] Registration data */
			const string& hostIDPurge				/**< [in] Host ID of this payload, purges all others with the same host ID if provided */);

	//! Registers the payload with Regulus
	void RegisterPayloadWithContent(
			const string& id,						/**< [in] Interface ID */
			const string& content,					/**< [in] Content of the registration file */
			const string& hostIDPurge				/**< [in] Host ID of this payload, purges all others with the same host ID if provided */);

	//! Registration is complete (OK to send file to shore)
	void RegistrationComplete(
			const string& id						/**< [in] Interface ID */);

	//! Provides a way to respond to a previously-received command
	void RespondToCommand(
			const string& id,						/**< [in] Interface ID */
			const string& responseTag,				/**< [in] The tag that was passed in the originating RemoteCommand */
			const string& response,					/**< [in] The response string to be returned */
			bool complete = true,					/**< [in] Is this the last response to be received? */
			bool succeeded = true					/**< [in] Was the command completed successfully? */);

	//! Executes the given script string on Regulus and returns the result
	string RunScript(
			const string& id,						/**< [in] Interface ID */
			const string& script 					/**< [in] Script to be executed */);

	//! Sends the given file to shore (via the bulk file service)
	void SendFileToShore(
			const string& id,						/**< [in] Interface ID */
			const string& fileName,					/**< [in] Name and path of file to be sent */
			const string& remoteName				/**< [in] The name of the file on the remote system */);

	//! Sends the given file to shore (via the legacy WGMS file transfer protocol)
	void SendFileToShoreLegacy(
			const string& id,						/**< [in] Interface ID */
			const string& fileName,					/**< [in] Name and path of file to be sent */
			const string& remoteName				/**< [in] The name of the file on the remote system */);

	//! Sends a raw packet to shore (byte array parameter)
	void SendRawPacketToShore(
			const string& id,						/**< [in] Interface ID */
			const ByteArray& rawPacket 				/**< [in] the data to be sent in a byte array */);

	//! Sends a raw packet to shore (string parameter)
	void SendRawPacketToShore(
			const string& id,						/**< [in] Interface ID */
			const string& rawPacket 				/**< [in] the data to be sent in a string */);

	//! Turns power switch associated with payload on or off
	void SetPayloadAMPSSwitch(
			const string& id,						/**< [in] Interface ID */
			bool on = true							/**< [in] true to turn the switch on, false to turn the switch off */);

	//! Turns power switch associated with payload on or off
	void SetPayloadAMPSSwitch(
			const string& id,						/**< [in] Interface ID */
			const string& switchName,				/**< [in] Name of the switch to affect */
			bool on = true							/**< [in] true to turn the switch on, false to turn the switch off */);


	//! Requests to turn this payload off
	void TurnPayloadOff(
			const string& id						/**< [in] Interface ID */);

	//! Encodes the given URL so that disallowed characters use percent hex notation
	static string EncodeURL(const string& url);

private:

	const string address;			//!< Copy of the construct parameter, the address and optional port specification
	Logger& logger;					//!< Reference to caller's logger
};

}
}

#endif
