//===========================================================================//
// File:	receiver.hpp                                                        //
// Project: MUNGA           Brick: Dynamic Dispatch                          //
// Contents: Dynamic Dispatch Interface                                      //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 10/19/94  JMA  Initial coding.                                            //
// 10/28/94  JMA  Made compatible with SGI CC                                //
// 11/03/94  ECH  Made compatible with BC4.0                                 //
// 11/04/94  JMA  Made compatible with GNU C++                               //
// 11/04/94  ECH  Added null handler set		                                //
// 11/29/94  JMA  Changed ClassIdentity to ClassID                           //
// 11/30/94  JMA  Adapted to new style conventions                           //
// 12/04/94  JMA  Moved Message under Receiver, merged with message.hpp      //
// 12/06/94  JMA  Made DefaultData publicly visible                          //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#pragma once

#include "Adept.hpp"
#include "Resource.hpp"

namespace Adept {

	class Receiver__MessageEntry;
	class Event;
	class Receiver__ClassData;
	class Receiver__Message;

	//##########################################################################
	//##########################    Receiver    ################################
	//##########################################################################
	//
	// It is suggested that calls to methods meant to be serviced by a receiver
	// be built through inline functions in the derived class, rather than
	// having to build the parameter block in the scope of the caller.  Letting
	// the inline code build the parameter block is much cleaner, and allows the
	// message code numbers to be held inside the derived class, further
	// protecting against calling methods for objects not in a proper derivation
	// chain.
	//
	class Receiver:
		public Stuff::Plug
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef Receiver__ClassData ClassData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction/Destruction
	//
	protected:
		Receiver(ClassData *class_data);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Class data support
	//
	public:
		ClassData*
			GetClassData();

		static ClassData*
			DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Messaging Support
	//
	public:
		typedef Receiver__MessageEntry MessageEntry;
		typedef void (Receiver::*MessageHandler)(const Receiver__Message*);
		typedef int MessageID;
		typedef Receiver__Message Message;

		enum {
			AnyMessageID=0,
			DefaultMessageID,
			NextMessageID
		};

		static const MessageHandler NullMessageHandler;

		void
			Receive(const Message *What);

		virtual void
			Dispatch(const Message *What)
				{Receive(What);}

		void
			DefaultMessageHandler(const Message*);

		const MessageEntry*
			GetMessageEntry(MessageID message);

	protected:
		static const MessageEntry
			MessageEntries[];

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Message Tap Support
	//
	public:
		typedef void (Receiver::*ScanCallback)(
			const Receiver::Message *message,
			Receiver *receiver
		);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Event Support
	//
	public:
		virtual void
			Receive(Event *event);

		void
			FlushEvents(int max_priority=-1);
		void
			FlushMatchingEvents(
				MessageID target_message,
				int max_priority=-1
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		static bool
			TestClass();
		void
			TestInstance();
	};

	//##########################################################################
	//######################    Receiver::Message    ###########################
	//##########################################################################

	class Receiver__Message
	{
	public:
		size_t
			messageLength;
		Receiver::MessageID
			messageID;
		int
			messagePriority;

		enum {
			ReliableBit,
			NextBit
		};

		enum {
			DefaultFlags = 0,
			ReliableFlag = 1 << ReliableBit
		};

		int
			messageFlags;

		Receiver__Message(
			Receiver::MessageID id,
			size_t message_length,
			int priority,
			int flags
		):
			messageID(id),
			messageLength(message_length),
			messagePriority(priority),
			messageFlags(flags)
				{}

		void
			TestInstance() const
				{}
	};

	//##########################################################################
	//####################    ReceiverDataMessageOf    #########################
	//##########################################################################

	template <class T> class ReceiverDataMessageOf:
		public Receiver__Message
	{
	public:
		T
			dataContents;

		ReceiverDataMessageOf(
			Receiver::MessageID id,
			size_t message_length,
			int priority,
			int flags,
			const T& data
		):
			Receiver__Message(id, message_length, priority, flags),
			dataContents(data)
				{}
	};

	//##########################################################################
	//###################    Receiver::MessageEntry    #########################
	//##########################################################################

	class Receiver__MessageEntry
	{
	public:
		int
			messageID;
		const char*
			messageName;
		Receiver::MessageHandler
			messageHandler;

		void
			TestInstance() const
				{}
	};

	#define MESSAGE_ENTRY(c,m)\
		{\
			c::m##MessageID,\
			#m,\
			(Receiver::MessageHandler)\
				(void (c::*)(const Receiver__Message*))(\
					&c::m##MessageHandler\
				)\
		}

	//##########################################################################
	//####################    Receiver::ClassData    ###########################
	//##########################################################################

	class Receiver__ClassData:
		public Stuff::Plug__ClassData
	{
		friend class Receiver;

	public:
		Receiver__ClassData(
			Stuff::RegisteredClass::ClassID class_id,
			const char *class_name,
			Stuff::Plug::ClassData *parent,
			Receiver::MessageID count,
			const Receiver::MessageEntry *handler_table
		);
		~Receiver__ClassData();

		size_t
			WriteClassBlock(
				Stuff::MemoryStream *stream,
				bool actual_write
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Message support
	//
	public:
		const Receiver::MessageEntry*
			FindMessageEntry(const char* name);
		const Receiver::MessageEntry*
			GetMessageEntry(Receiver::MessageID message)
				{
					Check_Object(this); Verify(message > 0);
					return (message<=messageCount)
						? &messageHandlers[message-1]
						: NULL;
				}
		Receiver::MessageHandler
			GetMessageHandler(Receiver::MessageID message)
				{
					Check_Object(this); Verify(message > 0);
					return (message<=messageCount)
						? messageHandlers[message-1].messageHandler
						: Receiver::NullMessageHandler;
				}
		int
			GetMessageCount()
				{Check_Object(this); return messageCount;}

	protected:
		int
			messageCount;
		Receiver__MessageEntry
			*messageHandlers;
		bool
			deleteMessages;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test support
	//
	public:
		void
			TestInstance();
	};

	inline Receiver__ClassData*
		Receiver::GetClassData()
			{return Cast_Pointer(ClassData*, classData);}

	inline const Receiver::MessageEntry*
		Receiver::GetMessageEntry(Receiver::MessageID message)
			{
				Check_Object(this);
				return GetClassData()->GetMessageEntry(message);
			}

}


