/* 
 * 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_STRUCTSET_HH__
#define __PVM_STRUCTSET_HH__

#include <pvm++/types.hh>

#include <map>
#include <set>

namespace Pvm
{

  /** A set of structs to be received.
      
      A StructSet is of course a set of \Ref{Struct}
      derivations. But in contrast to the classes \Ref{TaskSet} and
      \Ref{HostSet} it is not derived from the STL-class set. Yet
      some of the methods are available with the same name as in the
      STL. You can add a \Ref{Struct} to the StructSet and can
      remove one from there. If you want to receive messages of, let's
      say, three different types, then you simply add instances of all
      those three \Ref{Struct}-derivations to a StructSet and do a
      Receive*() (syntax and semantic are analogous to those in
      Struct.Receive*()). Then you get a return value with the id of
      the received message (as given with SetStructId()) or 0, in
      case no message has been received (e.g. if it is a timed
      receive). Now you find the received information in the previously
      added instance of the corresponding type. So a code-fragment looks
      like this:
      
      \begin{verbatim}
      StructA A; // derived from Pvm::Struct; Id = A_Id;
      StructB B; // derived from Pvm::Struct; Id = B_Id;
      Pvm::StructSet Awaited;
      Awaited.insert (A);
      Awaited.insert (B);
      while (1)
        {
          Pvm::StructId Id = Awaited.ReceiveFrom (SendingTask);
          if (Id == A_Id)
          {
            cout << A.what_ever_data_is_in_A << endl;
          }
          else // now Id must be equal B_Id
          {
            cout << B.what_ever_data_is_in_B << endl;
          }
        }
	\end{verbatim} */
  class StructSet
  {
  public:
    StructSet ();
    
    /** inserts a reference to the instance What into the set. There can
	inly be one instance of every type (determined by the
	corresponding \Ref{StructId}) inside such a set. So an
	instance overrides potentially contained ones of the same
	type. */
    void insert (Struct &What);
    
    /** erases the potentially contained instance with the same
	\Ref{StructId} as What. */
    void erase (const Struct &What);
    
    /** erases the potentially contained instance with the
	\Ref{StructId} What. */
    void erase (StructId What);
    
    /** returns 1, if What (not just any instance with the same
	\Ref{StructId} as What) is in the set and 0 otherwise. */
    int count (const Struct &What) const;
    
    /** returns 1, if an instance with the \Ref{StructId}` is in the
	set and 0 otherwise. */
    int count (StructId What) const;
    
    /** receives a message of one of the types in the set and returns
	the id of that message and the id of the sender in From. The
	parameter can be omitted, if you're not interested in who was
	sending. The message is unpacked into the instance that was
	added to set at. The call blocks until a message is received. If
	one of the file descriptor sets is not empty, the function
	returns immediatly 0, if the corresponding action on the file
	desriptor is possible. Subsequent calls to the
	Ready*FDs()-methods return the ready file descriptors. The input
	sets are not changed. */
    StructId Receive (Task &From = IgnoreTask);
    
    /** receives a message of one of the types in the set, but only from
	one of the tasks given by FromSet, and returns the id of that
	message and the id of the sender in From. This parameter can be
	omitted. The message is unpacked into the instance that was
	added to set at.The call blocks until a message is received. If
	one of the file descriptor sets is not empty, the function
	returns immediatly 0, if the corresponding action on the file
	desriptor is possible. Subsequent calls to the
	Ready*FDs()-methods return the ready file descriptors. The input
	sets are not changed. */
    StructId ReceiveFrom (const TaskSet &FromSet,
			  Task &From = IgnoreTask);
    
    /** receives a message of one of the types in the set, but only from
	the task From. It returns the id of the message. The message is
	unpacked into the instance that was added to set at.The call
	blocks until a message is received. If one of the file
	descriptor sets is not empty, the function returns immediatly 0,
	if the corresponding action on the file desriptor is
	possible. Subsequent calls to the Ready*FDs()-methods return the
	ready file descriptors. The input sets are not changed. */
    StructId ReceiveFrom (Task From);
    
    /** receives a message of one of the types in the set and returns
	the 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 0, otherwise it returns the id of the
	message and the remaining time in Time immediately after
	receiving the message. If received, the message is unpacked into
	the instance that was added to set at. If one of the file
	descriptor sets is not empty, the function returns immediatly 0,
	if the corresponding action on the file desriptor is
	possible. Subsequent calls to the Ready*FDs()-methods return the
	ready file descriptors. The input sets are not changed. */
    StructId TimedReceive (unsigned long int &Time,
			   Task &From = IgnoreTask);
    
    /** receives a message of one of the types in the set, 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 0, otherwise it returns the id of the message and the
	remaining time in Time immediately after receiving the
	message. If received, the message is unpacked into the instance
	that was added to set at. If one of the file descriptor sets is
	not empty, the function returns immediatly 0, if the
	corresponding action on the file desriptor is
	possible. Subsequent calls to the Ready*FDs()-methods return the
	ready file descriptors. The input sets are not changed. */
    StructId TimedReceiveFrom (const TaskSet &FromSet,
			       unsigned long int &Time,
			       Task &From = IgnoreTask);
    
    /** receives a message of one of the types in the set, 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 0,otherwise it returns the id of the
	message and the remaining time in Time immediately after
	receiving the message. If received, the message is unpacked into
	the instance that was added to set at. If one of the file
	descriptor sets is not empty, the function returns immediatly 0,
	if the corresponding action on the file desriptor is
	possible. Subsequent calls to the Ready*FDs()-methods return the
	ready file descriptors. The input sets are not changed. */
    StructId TimedReceiveFrom (Task From, unsigned long int &Time);
    
    /** the type representing a set of file descriptors. */
    typedef std::set< int > FDSet;
    
    /** returns a reference to the set of file descriptors, you want to
	read from. */
    FDSet &ReadFDs ();
    
    /** returns the set of file descriptors, you want to read from. */
    const FDSet &ReadFDs () const;
    
    /** returns the set of file descriptors, that are ready to read
	from. */
    const FDSet &ReadyReadFDs () const;
    
    /** returns a reference to the set of file descriptors, you want to
	write to. */
    FDSet &WriteFDs ();
    
    /** returns the set of file descriptors, you want to write to. */
    const FDSet &WriteFDs () const;
    
    /** returns the set of file descriptors, that are ready to write
	to. */
    const FDSet &ReadyWriteFDs () const;
    
    /** returns a reference to the set of file descriptors, that you're
	expecting to get into an exceptional condition. */
    FDSet &ExceptFDs ();
    
    /** returns the set of file descriptors, that you're expecting to
	get into an exceptional condition. */
    const FDSet &ExceptFDs () const;
    
    /** returns the set of file descriptors, that have an exceptional
	condition pending. */
    const FDSet &ReadyExceptFDs () const;
    
    /** returns true if any file descriptor is ready. */
    bool FDsReady () const;
    
  private:
    friend class AccessPrivate;
    static Task IgnoreTask;
    typedef std::map< StructId, Struct * > MapType;
    MapType Map;
    bool UseSelect;
    bool UseSelectUnknown;
    bool IntFDsReady;
    FDSet IntReadFDs;
    FDSet IntWriteFDs;
    FDSet IntExceptFDs;
    FDSet IntReadyReadFDs;
    FDSet IntReadyWriteFDs;
    FDSet IntReadyExceptFDs;
  };
  
} // namespace Pvm

#endif /* __PVM_STRUCTSET_HH__ */
