//-------------------------------------------------------------------------------------
// <copyright file="LqrSMACObjectBroker.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 ObjectBroker class, which wraps common CORBA object broker
//   sequences
//
//   Typical startup case:
//
//		int main(int argc, char* argv[])
//		{
//			int retcode = 0;
//
//			try
//			{
//				ObjectBroker broker(true);				// true if acting as server
//				broker.Initialize(argc, argv);			// note that args are modified
//
//				MyServant serv(broker);					// implementation class
//
//				// tie class receives CORBA events, subclassing my implementation class,
//				// pass the instance as a reference so that the tie class does not
//				// delete it when it goes out of scope
//				PortableServer::ServantBase_var theServer = 0;
//				theServer = new ::POA_LiquidRobotics::SMAC::ICommandable_tie<MyServant>(serv);
//
//				// advertise the instance with a name via omniORB's name server; alternatively,
//				// call ActivateInstance if the name server is not used (e.g. your object
//				// is referenced by IOR)
//				Object_var serverRef = broker.AdvertizeInstance(theServer.in(), "MyServantName");
//
//				// ... set up other items ...
//
//				serv.Initialize();						// launch background threads
//
//				// listen for incoming requests (blocks here until the ORB is shut down); all
//				// program actions must occur by CORBA event or by executing threads
//				broker.ListenForRequests();
//
//				// at this point, someone called Shutdown on the broker (ORB), but the ORB has
//				// not been destroyed
//
//				// exiting this scope deletes objects in reverse order, with the broker being
//				// deleted last, which calls the destroy method on the ORB;  at that point, no
//				// CORBA actions can occur without a program crash
//			}
//			catch(CORBA::Exception& ex)
//			{
//				cerr << "Caught CORBA exception: " << ex._name() << endl;
//				retcode = 1;
//			}
//			catch(exception& ex)
//			{
//				cerr << "Caught " << typeid(ex).name() << ": " << ex.what() << endl;
//				retcode = 1;
//			}
//
//			// cannot do any CORBA actions here
//
//			return retcode;
//		}
//
//   Invoking on another server:
//
//		void MyServant::PingServerByName(const string& serverName)
//		{
//			using namespace LiquidRobotics::SMAC;
//			IPingable_var pingV;
//			pingV = brokerPtr->GetNamedInstance<IPingable_var,IPingable>(serverName.c_str());
//			::CORBA::Double timeAtObject = pingV->Ping();
//		}
//
//   Receiving a ping:
//
//		::CORBA::Double MyServant::Ping()
//		{
//			using namespace LiquidRobotics::SMACFramework;
//			::CORBA::Double result = GetTimeAsDouble();
//			return result;
//		}
//
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------

#ifndef __OBJECTBROKER_H__
#define __OBJECTBROKER_H__

#include "LqrSMACSharedLib.h"
#include "LqrSMACIObjectBroker.h"
#include "LqrSMACLockableConcept.h"
#include <map>
#include <set>
#include <boost/thread/locks.hpp>
#include <boost/thread/recursive_mutex.hpp>

namespace LiquidRobotics {
namespace SMACFramework {

class SHARED_LIB_PUBLIC ObjectBroker
: public IObjectBroker
, public LockableConceptMutex
{
public:

	typedef CORBA::Any Any;
	typedef CORBA::Any_var Any_var;
	typedef CORBA::Object_ptr Object_ptr;
	typedef CORBA::Object_var Object_var;
	typedef std::string string;

	ObjectBroker(bool _server = true);
	~ObjectBroker();

	virtual Object_var ActivateInstance(PortableServer::Servant pServant);
	virtual Object_var AdvertizeInstance(PortableServer::Servant pServant, const char* instanceName, const char* instanceKind = NULL);
	virtual void DeactivateAll();
	virtual void DeactivateInstance(PortableServer::Servant pServant);
	virtual bool GetAdvertizedInstanceNameFromStringizedName(const char* instanceStringized, string& advertizedInstanceName, string& advertizedInstanceKind) const;
	virtual Object_var GetRawDynamicObjectFromTypeCode(TypeCode_ptr typeCode);
	virtual Object_var GetRawObjectFromStringizedName(const char* instanceStringized);
	virtual Object_var GetRawRootInstance(const char* instanceName);
	virtual Object_var GetRawNamedInstance(const char* instanceName, const char* instanceKind = NULL, bool validateName = true, StringPairVectorSet* cachePtr = NULL);
	virtual void GetServers(StringPairVectorSet& servers);
	virtual string GetStringizedReference(Object_ptr pRef);
	virtual void Initialize(int& argc, char**& argv);
	virtual bool IsShutdownQueued();
	virtual bool IsShutDown();
	void ListenForRequests();
	virtual void QueueShutdown();
	void Shutdown(bool waitForCompletion = true);
	virtual void UnadvertiseAll();
	virtual void UnadvertiseInstance(const char* instanceName, const char* instanceKind = NULL);
	virtual bool ValidateNamedInstance(const char* instanceName, const char* instanceKind, StringPairVectorSet* cachePtr = NULL, bool throwOnFailure = true);

	void AnyToStringVector(StringVector& to, const Any& from);
	Any_var StringVectorToAny(const StringVector& from);
	Any_var StringVectorToAny(const string* pStart, size_t numElems);

protected:

	virtual ORB_var ORB();

private:

	typedef std::pair<string,string> NameItem;
	typedef std::set<NameItem> NameItemSet;
	typedef std::set<PortableServer::Servant> ServantSet;
	typedef std::map<string,NameItem> StringizedNameToNameItemMap;

	template<typename RETURN_TYPE, typename NARROWING_TYPE>
	RETURN_TYPE LocalGetRootInstance(const char* instanceName);

	void CheckState(const char* caller, bool initCheck = true, bool shutdownCheck = true, bool checkServer = false) const;
	virtual CORBA::Object_var LocalGetRawRootInstance(const char* instanceName);
	void QueuedShutdownCallback();

	const bool server;
	bool initialized;
	bool listening;
	bool shutdownQueued;
	bool shuttingDown;
	bool shutDown;
	CORBA::ORB_var globalORB;
	CosNaming::NamingContext_var rootContextV;
	PortableServer::POA_var rootPOAV;
	PortableServer::POAManager_var rootPOAMgrV;
	ServantSet activations;
	NameItemSet names;
	StringizedNameToNameItemMap stringNameToItem;
};

}
}

#endif
