/* 
 * PVM++ - A C++ library for easy use of PVM
 * Copyright (C) 1997-2001 Sebastian Wilhelmi; University of Karlsruhe
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __PVM_STRUCT_HH__
#define __PVM_STRUCT_HH__

#include <pvm++/types.hh>

#define PvmSetStructId(n)						\
virtual ::Pvm::StructId GetStructId () const { return n; }		\
virtual ::Pvm::Struct* GetNewInstance() const				\
{									\
  return ::Pvm::GetNewInstance (*this);					\
}
#define PvmRegistration() virtual void PackOrUnPack ()

namespace Pvm
{

  template< class Type > Type * GetNewInstance (const Type &);
  
  /** Base class for all classes to be transmitted by PVM++.
      
      Every struct (or class, for that matter) that should be sent
      between tasks must be publically derived from Struct. And it
      has to register all its members, that should be transmitted, to
      PVM++. Finally it has to provide a tag, which is used internally
      in PVM++ to determine the type of a message upon arrival, just
      like in PVM. {\bf It is very important not to use one number for
      different classes and to use the same number for the same type in
      the different communicating programs!} For example a definition
      could look like this:
      
      \begin{verbatim}
      struct Test : public Pvm::Struct
      {
        PvmSetStructId (42); // Setting the Tag for this struct. 
                              // Don't use twice!!
        PvmRegistration ()
        {
          Pvm::Register (Data);
          Pvm::Register (Character);
          Pvm::Register (DoubleArray, 187);
          Pvm::Register (Host);
          Pvm::Register (IntSet);
        }
        int Data;
        char Character;
        double DoubleArray[187];
        Pvm::Host Host;
        std::set< int > IntSet;
      };
      \end{verbatim}

      As you can see, STL types can be used and will be transmitted
      correctly, if they are registered and if the template arguments
      (i.e. int in set< int >) can be registered. The
      following list shows all types, that can be registered (and
      therefore transmitted):
      
      \begin{itemize}
      \item bool.
      \item char.
      \item unsigned char.
      \item short int.
      \item int.
      \item float.
      \item double.
      \item long int.
      \item unsigned short.
      \item unsigned int.
      \item unsigned long int.
      \item \Ref{Pvm::Host}.
      \item \Ref{Pvm::HostSet}.
      \item \Ref{Pvm::Task}.
      \item \Ref{Pvm::TaskSet}.
      \item std::string.
      \item std::complex< Type >, if Type can be registered.
      \item std::pair< First, Second >, if First and Second can be registered.
      \item std::vector< Key >, if Key can be registered.
      \item std::list< Key >, if Key can be registered.
      \item std::deque< Key >, if Key can be registered.
      \item std::set< Key >, if Key can be registered.
      \item std::multiset< Key >, if Key can be registered.
      \item std::map< Key >, if Key can be registered.
      \item std::multimap< Key >, if Key can be registered.
      \item Classes derived from \Ref{Pvm::Struct}.
      \item Classes derived from \Ref{Pvm::Custom}.
      \end{itemize}
      
      Arrays for all of the above types (except \Ref{Struct} and
      \Ref{Custom}, where it isn't possible due to inheritance) are
      supported as well. The corresponding syntax is Register (type
      *PointerToArray, int Size). */

  class Struct
  {
  public:
    static bool CurrentlyPacking;
    Struct ();
    Struct (const Struct &What);
    Struct & operator = (const Struct &What);
    virtual ~Struct ();
    virtual Struct *GetNewInstance () const = 0;
    virtual StructId GetStructId () const = 0;
    
    /** sends the message to task To. */
    void Send (Task To) const;
    
    /** sends the message to all tasks in the set To. */
    void Send (const TaskSet &To) const;
    
    /** receives a message of the given type and returns the id of the
	sender in From. The parameter can be omitted, if you're
	not interested in who was sending. The call blocks until a
	message is received. */
    void Receive (Task &From = IgnoreTask);
    
    /** receives a message of the given type, but only from one of the
	tasks given by FromSet, and returns the id of the sender in
	From. This parameter can be omitted. The call blocks until a
	message is received. */
    void ReceiveFrom (const TaskSet &FromSet, Task &From = IgnoreTask);
    
    /** receives a message of the given type, but only from the task
	From. The call blocks until a message is received. */
    void ReceiveFrom (Task From);
    
    /** receives a message of the given type and returns the id of the
	sender in From. This parameter can be omitted. The parameter
	Time specifies the maximal blocking time in microseconds. If
	after this time no message is received, than the function
	returns false, otherwise it returns true and the remaining time
	in Time immediately after receiving the message. */
    bool TimedReceive (unsigned long int &Time, Task &From = IgnoreTask);
    
    /** receives a message of the given type, but only from one of the
	tasks given by FromSet, and returns the id of the sender in
	From. This parameter can be omitted. The parameter Time
	specifies the maximal blocking time in microseconds. If after
	this time no message is received, than the function returns
	false, otherwise it returns true and the remaining time in Time
	immediately after receiving the message. */
    bool TimedReceiveFrom (const TaskSet &FromSet,
			   unsigned long int &Time,
			   Task &From = IgnoreTask);
    
    /** receives a message of the given type, but only from the task
	From. The parameter Time specifies the maximal blocking time in
	microseconds. If after this time no message is received, than
	the function returns false, otherwise it returns true and the
	remaining time in Time immediately after receiving the
	message. */
    bool TimedReceiveFrom (Task From, unsigned long int &Time);
    
    /** sets the new action on receive for the current message type to
	What.  The old action is returned for later use with this
	function call. */
    ::Pvm::ReceiveAction ReceiveAction (const ::Pvm::ReceiveAction &What);
    
    /** installs the handler Func as a message handler for all messages
	of the current message type. Such messages can't be received
	after a call to that function, as every time, such a message
	arrives, the installed handler will be called. The old action is
	returned for later use with the method ReceiveAction().  The
	parameter Func is of type void (*HandlerFunc)( const class
	\Ref{Struct}&, const class \Ref{Task}& ). It gets the
	received message and the sender of it as parameters. A message
	handler might change global variables and send messages to other
	tasks, but it should {\bf never} try to receive messages!
	Message handlers are not called asynchronous, i.e. they are
	called, whenever the program calls a Send() or
	Receive*()-function or the Update()-function of
	\Ref{Class}. */
    ::Pvm::ReceiveAction InstallHandler (HandlerFunc Func);
    
    /** installs the "unpack on receive" action, i.e. every subsequent
	arriving message of this type will automatically be unpacked
	into this instance. If this instance is deleted, NormalReceive
	is enabled again. The old action is returned for later use with
	the method ReceiveAction(). */
    ::Pvm::ReceiveAction AutomaticUnPack ();
    
    /** installs the "swallow on receive" action, i.e. every subsequent
	arriving message of this type will be ignored. The old action is
	returned for later use with the method ReceiveAction(). */
    ::Pvm::ReceiveAction SwallowOnReceive ();
    
    /** switches back to normal mode of operation, i.e. you have to
	receive messages yourself. The old action is returned for later
	use with the method ReceiveAction(). */
    ::Pvm::ReceiveAction NormalReceive ();
    
  protected:
    virtual void PackOrUnPack () = 0;
  private:
    static Task IgnoreTask;
    int StructIdForAutomaticUnpack;
    friend class AccessPrivate;
    friend class HandlerTableType;
    friend void Pack (const Struct &What);
    friend void UnPack (Struct &What);
    void Pack () const;
    void UnPack ();
  };

} // namespace Pvm

#endif /* __PVM_STRUCT_HH__ */
