//------------------------------------------------------------------------------
// <copyright file="PayloadManagerClient.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>
//   Declaration of the PayloadManagerClient class, which wraps registration and
//   de-registration of objects that implement the IPayloadDevice interface and
//   listens for changes in registration.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef __EVENTLIB_PAYLOADMANAGERCLIENT_H__
#define __EVENTLIB_PAYLOADMANAGERCLIENT_H__

#include "LqrSMACIdl.h"
#include "EventTransceiver.h"
#include "LqrSMACByteArray.h"
#include "LqrSMACPayloadDescription.h"
#include "LqrSMACPayloadDescriptionList.h"
#include "LqrSMACSharedLib.h"
#include "LqrSMACScopedConnection.h"
#include "LqrSMACSignalSubscriber.h"
#include "LqrSMACLockableConcept.h"
#include "PayloadManagerClientSettings.h"
#include "RegulusREST.h"
#include <map>
#include <string>

namespace LiquidRobotics {
namespace EventLib {

namespace PayloadManagerClientDetail {
	const LiquidRobotics::SMACFramework::ByteArray::byte defaultBoardIDProxy = 0xFF;
}

class SHARED_LIB_PUBLIC PayloadManagerClient : public boost::noncopyable
{
public:

	LOCKABLE_CONCEPT_TYPEDEFS();
	typedef LiquidRobotics::SMACFramework::ByteArray::byte byte;
	typedef LiquidRobotics::SMAC::PayloadDescriptionList CORBAList;
	typedef LiquidRobotics::SMAC::PayloadDescriptionList_var CORBAList_var;
	typedef LiquidRobotics::SMAC::IPayloadManager IPayloadManager;
	typedef LiquidRobotics::SMAC::IPayloadManager_var IPayloadManager_var;
	typedef ::CORBA::Object_ptr Object_ptr;
	typedef LiquidRobotics::SMACFramework::PayloadDescription PayloadDescription;
	typedef LiquidRobotics::SMACFramework::PayloadDescriptionList PayloadDescriptionList;
	typedef boost::shared_ptr<PayloadManagerClient> Ptr;
	typedef boost::signals2::scoped_connection ScopedConnection;
	typedef boost::shared_ptr<ScopedConnection> ScopedConnectionPtr;
	typedef boost::signals2::signal<void (PayloadManagerClient& sender, const PayloadDescription& payloadDesc)> PayloadChangeSignalType;
	typedef std::string string;

	PayloadManagerClient(const PayloadManagerClientSettings& _settings, LiquidRobotics::SMACFramework::IObjectBroker& _broker, EventTransceiver& _eventTrans);
	virtual ~PayloadManagerClient();

	// Retrieve the default board ID
	virtual byte GetDefaultBoardID();

	// Retrieve the JSON string
	virtual string GetJSON();

	// Retrieve registered board address
	virtual uint16_t GetMyBoardAddress() const;

	// Retrieve registered board ID
	virtual byte GetMyBoardID() const;

	// Retrieve registered task ID
	virtual byte GetMyTaskID() const;

	// Retrieve the description for this payload
	virtual PayloadDescription::Ptr GetPayloadDescription(bool translateDefaultBoardID = true);

	// Retrieve the list of payload descriptions
	virtual PayloadDescriptionList::Ptr GetPayloadDescriptions(bool translateDefaultBoardID = true);

	// Register with PayloadManager using device data in settings
	virtual void Register(Object_ptr server, bool overwrite = true);

	// Register with PayloadManager using ad hoc created PayloadDescription
	virtual void Register(Object_ptr server, const PayloadDescription& description, bool overwrite = true);

	// Unsubscribe from EventManager's signals (no further events will be fired)
	virtual void RevokeCallbacks();

	// Init class factories and subscribe to EventManager's callbacks (begin firing events from this object's signals)
	virtual void SubscribeCallbacks(EventTransceiver& eventTrans);

	// Translate a board adddress that contain a default board ID to the actual board address
	virtual uint16_t TranslateBoardAddress(uint16_t boardAddress, byte defaultBoardID = PayloadManagerClientDetail::defaultBoardIDProxy);

	// Translate a board ID that may point to the default to its actual board ID
	virtual byte TranslateBoardID(byte boardID, byte defaultBoardID = PayloadManagerClientDetail::defaultBoardIDProxy);

	// Unregister from PayloadManager and remove all servers
	virtual void Unregister();

	// Unregister from PayloadManager and delist objects for given server
	virtual void Unregister(Object_ptr server);

	// Update the descriptive JSON for this instance
	virtual bool UpdateJSON(const string& newJSON);
	virtual bool UpdateJSON(
			const LiquidRobotics::SV3RemotingLib::RegulusREST::PRHardware::Vector& hardwareInfo);
	virtual bool UpdateJSON(
			const LiquidRobotics::SV3RemotingLib::RegulusREST::PRInterface& interfaceInfo);
	virtual bool UpdateJSON(
			const LiquidRobotics::SV3RemotingLib::RegulusREST::PRHardware::Vector& hardwareInfo,
			const LiquidRobotics::SV3RemotingLib::RegulusREST::PRInterface& interfaceInfo);

	// signals

private:

	LockableConceptMutex firingMutex;
	PayloadChangeSignalType payloadAddedSignal;
	PayloadChangeSignalType payloadChangedSignal;
	PayloadChangeSignalType payloadRemovedSignal;

public:

	LiquidRobotics::SMACFramework::SignalSubscriber<PayloadChangeSignalType> payloadAddedSubscriber;
	LiquidRobotics::SMACFramework::SignalSubscriber<PayloadChangeSignalType> payloadChangeSubscriber;
	LiquidRobotics::SMACFramework::SignalSubscriber<PayloadChangeSignalType> payloadRemovedSubscriber;

protected:

	virtual void DeviceAddedHandler(const PayloadDescription& desc);
	virtual void DeviceChangedHandler(const PayloadDescription& desc);
	virtual void DeviceRemovedHandler(const PayloadDescription& desc);

private:

	typedef std::map<Object_ptr, string> PtrToIORMap;
	typedef void (PayloadManagerClient::* HandlerType)(const PayloadDescription& desc);

	class CallbackHandler
	{
	public:

		CallbackHandler(PayloadManagerClient& _client, HandlerType _handler, const EventType& _evtType);

		void init(EventTransceiver::InformSignalType& sig);
		void operator()(EventTransceiver& sender, const IEventBase& receivedEvent);

		PayloadManagerClient& client;
		HandlerType handler;
		const EventType& evtType;
		ScopedConnectionPtr scopedConnection;
	};

	class ShutdownTask
	{
	public:

		ShutdownTask(PayloadManagerClient& _client);
		virtual ~ShutdownTask();

		void connect(LiquidRobotics::SMACFramework::IObjectBroker& broker);
		void operator()(LiquidRobotics::SMACFramework::IObjectBroker& sender);

	private:

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

		PayloadManagerClient& client;
		ScopedConnectionPtr connection;
	};

	typedef boost::shared_ptr<ShutdownTask> ShutdownTaskPtr;

	friend class CallbackHandler;
	typedef boost::shared_ptr<CallbackHandler> CallbackHandlerPtr;

	string DetermineRegFilename() const;
	IPayloadManager_var GetPM();

	LiquidRobotics::SMACFramework::IObjectBroker& broker;
	EventTransceiver& eventTrans;
	PayloadManagerClientSettings settings;
	byte myBoardID;
	byte myTaskID;
	EventType commonEventType;
	PtrToIORMap registeredPtrs;
	EventTransceiver::EventClassFactory::Ptr classFact;
	CallbackHandlerPtr addCallback;
	CallbackHandlerPtr changeCallback;
	CallbackHandlerPtr removeCallback;
	ShutdownTaskPtr shutdownTask;
};

}
}

#endif
