    /*

    Copyright (C) 2000 Stefan Westerfeld
                       stefan@space.twc.de

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
  
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.
   
    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

    */

#ifndef OBJECT_H
#define OBJECT_H

#include "buffer.h"
#include "connection.h"
#include "notification.h"

#include <assert.h>
#include <map>
#include <list>

#include "arts_export.h"

/*
 * BC - Status (2002-03-08): Object_base, Object_skel, Object_stub
 *
 * All of them have to be kept binary compatible carefully, due to interaction
 * with generated code. There are d ptrs in _skel and _stub, NOT TO BE USED
 * NORMALLY. Normally, do use _internalData instead, as this is much faster
 * than creating two d objects per MCOP implementation/stub. Handle with care.
 */


namespace Arts {
/* custom dispatching functions */

typedef void (*DispatchFunction)(void *object, Buffer *request, Buffer *result);
typedef void (*OnewayDispatchFunction)(void *object, Buffer *request);
typedef void (*DynamicDispatchFunction)(void *object, long methodID, Buffer *request, Buffer *result);

class ScheduleNode;
class Object_skel;
class Object_stub;
class FlowSystem;
class MethodDef;
class ObjectReference;
class WeakReferenceBase;
class Object;
class ObjectManager;
class DynamicSkeletonData;
class DynamicSkeletonBase;

class ARTS_EXPORT Object_base : public NotificationClient {
private:
	friend class DynamicRequest;
	friend class ObjectManager;
	bool _deleteOk;				// ensure that "delete" is not called manually

protected:
	/**
	 * ObjectInternalData contains private data structures for
	 *  - Object_base
	 *  - Object_stub
	 *  - Object_skel
	 *
	 * This is an optimization over adding each of them private data pointers,
	 * which would lead to some more bloat.
	 */
	class ObjectInternalData *_internalData;

	struct ObjectStreamInfo;

	Object_base();
	virtual ~Object_base();

	/*
	 * internal management for streams
	 */
	ScheduleNode *_scheduleNode;
	std::list<ObjectStreamInfo *> _streamList;

	virtual Object_skel *_skel();
	virtual Object_stub *_stub();

	enum ObjectLocation { objectIsLocal, objectIsRemote };
	virtual ObjectLocation _location() const = 0;

	long _objectID;
	Connection *_connection;
	std::string _internalObjectID;	// two objects are "_isEqual" when these match
	long _nextNotifyID;
	long _refCnt;				// reference count
	static long _staticObjectCount;

	void _destroy();			// use this instead of delete (takes care of
								// properly removing flow system node)
public:
	static unsigned long _IID;	// interface ID
	/**
	 * custom messaging: these can be used to send a custom data to other
	 * objects. Warning: these are *not* usable for local objects. You may
	 * only use these functions if you know that you are talking to a remote
	 * object. Use _allocCustomMessage to allocate a message. Put the data
	 * you want to send in the Buffer. After that, call _sendCustomMessage.
	 * Don't free the buffer - this will happen automatically.
	 */

	virtual Buffer *_allocCustomMessage(long handlerID);
	virtual void _sendCustomMessage(Buffer *data);

	/*
	 * generic capabilities, which allow find out what you can do with an
	 * object even if you don't know it's interface
	 */
	virtual long _lookupMethod(const Arts::MethodDef &) = 0;
	virtual std::string _interfaceName() = 0;
	virtual class InterfaceDef _queryInterface(const std::string& name) = 0;
	virtual class TypeDef _queryType(const std::string& name) = 0;
	virtual class EnumDef _queryEnum(const std::string& name) = 0;
	virtual std::string _toString() = 0;

	/*
	 * stuff for streaming (put in a seperate interface?)
	 */
	virtual void calculateBlock(unsigned long cycles);
	ScheduleNode *_node();
	virtual FlowSystem _flowSystem() = 0;

	/*
	 * reference counting
	 */
	virtual void _release() = 0;
	virtual void _copyRemote() = 0;
	virtual void _useRemote() = 0;
	virtual void _releaseRemote() = 0;

	// BC issue: added _cancelCopyRemote here to avoid virtual function
	void _cancelCopyRemote();

	void _addWeakReference(WeakReferenceBase *reference);
	void _removeWeakReference(WeakReferenceBase *reference);

	inline Object_base *_copy() {
		assert(_refCnt > 0);
		_refCnt++;
		return this;
	}

	// Default I/O info
	virtual std::vector<std::string> _defaultPortsIn() const;
	virtual std::vector<std::string> _defaultPortsOut() const;

	// cast operation
	virtual void *_cast(unsigned long iid);
	void *_cast(const std::string& interface);
	
	// Run-time type compatibility check
	virtual bool _isCompatibleWith(const std::string& interfacename) = 0;

	// Aggregation
	virtual std::string _addChild(Arts::Object child, const std::string& name) = 0;
	virtual bool _removeChild(const std::string& name) = 0;
	virtual Arts::Object _getChild(const std::string& name) = 0;
	virtual std::vector<std::string> * _queryChildren() = 0;

	/*
	 * when this is true, a fatal communication error has occurred (of course
	 * only possible for remote objects) - maybe your returncode is invalid,
	 * maybe your last invocation didn't succeed...
	 */
	virtual bool _error();

	inline static long _objectCount() { return _staticObjectCount; }
	inline long _mkNotifyID() { return _nextNotifyID++; }

	// object creation
	static Object_base *_create(const std::string& subClass = "Object");

	// comparison
	bool _isEqual(Object_base *object) const;

	// static converter (from reference)
	static Object_base *_fromString(const std::string& objectref);
	static Object_base *_fromReference(class ObjectReference ref, bool needcopy);
};

/*
 * Dispatching
 */

class Buffer;
class MethodDef;


class Object_skel_private;
class AnyConstRef;
class AttributeDef;

class ARTS_EXPORT Object_skel : virtual public Object_base {
private:
	friend class Object_base;
	friend class DynamicSkeletonData;
	friend class DynamicSkeletonBase;

	Object_skel_private *_d_skel;// do not use until there is a very big problem

	// reference counting - remote object watching
	
	long _remoteSendCount;		// don't kill objects just sent to other server
	bool _remoteSendUpdated;	// timeout if they don't want the object
	std::list<class Connection *> _remoteUsers;	// who is using it?

protected:
	void _addMethod(DispatchFunction disp, void *object, const MethodDef& md);
	void _addMethod(OnewayDispatchFunction disp, void *object,
														const MethodDef& md);
	void _addMethod(DynamicDispatchFunction disp, void *object,
														const MethodDef& md);
	void _initStream(const std::string& name, void *ptr, long flags);

	/** stuff relative to attribute notifications **/
	bool _initAttribute(const Arts::AttributeDef& attribute);
	static bool _QueryInitStreamFunc(Object_skel *skel,const std::string& name);
	bool _generateSlots(const std::string& name, const std::string& interface);

	/** for DynamicSkeleton: **/
	const MethodDef& _dsGetMethodDef(long methodID);

protected:
	void _defaultNotify(const Notification& notification);
	void notify(const Notification& notification);
	void _emit_changed(const char *stream, const AnyConstRef& value);

	/**
	 * custom messaging: this is used to install a custom data handler that
	 * can be used to receive non-standard messages
	 */
	long _addCustomMessageHandler(OnewayDispatchFunction handler, void *object);

	Object_skel *_skel();
	ObjectLocation _location() const;

public:
	Object_skel();
	virtual ~Object_skel();

	// reference counting connection drop
	void _disconnectRemote(class Connection *connection);
	void _referenceClean();

	// synchronous & asynchronous dispatching
	void _dispatch(Buffer *request, Buffer *result,long methodID);
	void _dispatch(Buffer *request, long methodID);
	long _lookupMethod(const MethodDef &);

	/*
	 * standard interface for every object skeleton
	 */
	static std::string _interfaceNameSkel();
	virtual void _buildMethodTable();

	/*
	 * reference counting
	 */
	virtual void _release();
	virtual void _copyRemote();
	virtual void _useRemote();
	virtual void _releaseRemote();

	/*
	 * streaming
	 */
	FlowSystem _flowSystem();

	/*
	 * to inspect the (remote) object interface
	 */
	virtual std::string _interfaceName();
	InterfaceDef _queryInterface(const std::string& name);
	TypeDef _queryType(const std::string& name);
	EnumDef _queryEnum(const std::string& name);
	virtual std::string _toString();
	
	// Run-time type compatibility check
	bool _isCompatibleWith(const std::string& interfacename);

	// Aggregation
	std::string _addChild(Arts::Object child, const std::string& name);
	bool _removeChild(const std::string& name);
	Arts::Object _getChild(const std::string& name);
	std::vector<std::string> * _queryChildren();
};

class Object_stub_private;

class ARTS_EXPORT Object_stub : virtual public Object_base {
private:
	friend class Object_base;

	Object_stub_private *_d_stub;// do not use until there is a very big problem

protected:
	long _lookupCacheRandom;

	Object_stub();
	Object_stub(Connection *connection, long objectID);
	virtual ~Object_stub();

	virtual Object_stub *_stub();
	ObjectLocation _location() const;

	enum { _lookupMethodCacheSize = 337 };
	static struct methodCacheEntry {
		methodCacheEntry() : obj(NULL),method(NULL),ID(0) {} ;
		Object_stub *obj;
		const char *method;
		long ID;
	} *_lookupMethodCache;

	long _lookupMethodFast(const char *method);
	long _lookupMethod(const MethodDef &);

public:
	/*
	 * custom messaging
	 */

	Buffer *_allocCustomMessage(long handlerID);
	void _sendCustomMessage(Buffer *data);

	/*
	 * to inspect the (remote) object interface
	 */
	std::string _interfaceName();
	InterfaceDef _queryInterface(const std::string& name);
	TypeDef _queryType(const std::string& name);
	EnumDef _queryEnum(const std::string& name);
	std::string _toString();

	/*
	 * streaming
	 */
	FlowSystem _flowSystem();

	/*
	 * reference counting
	 */
	virtual void _release();
	virtual void _copyRemote();
	virtual void _useRemote();
	virtual void _releaseRemote();
	
	// Run-time type compatibility check
	bool _isCompatibleWith(const std::string& interfacename);

	// Aggregation
	std::string _addChild(Arts::Object child, const std::string& name);
	bool _removeChild(const std::string& name);
	Arts::Object _getChild(const std::string& name);
	std::vector<std::string> * _queryChildren();

	/*
	 * communication error? this is true when your connection to the remote
	 * object is lost (e.g. when the remote server crashed) - your return
	 * values are then undefined, so check this before relying too much
	 * on some invocation
	 */

	bool _error();

	/*
	 * global cleanup
	 */
	static void _cleanupMethodCache();
};

}
#endif
