#ifndef SO_DISTRIBUTION_GROUP_H_INCLUDED_
#define SO_DISTRIBUTION_GROUP_H_INCLUDED_


#include <Inventor/nodes/SoSubNode.h>
#include <Inventor/fields/SoMFBool.h>
#include <Inventor/lists/SbList.h>
#define COIN_ALLOW_SBDICT
#include <Inventor/SbDict.h>
#undef COIN_ALLOW_SBDICT
#include <Inventor/SbTime.h>
#include "SbNetBasic.h"
#include "SbNetListener.h"
#include "SbRefList.h"
#include "SoMFComputer.h"
#include "SoMFNetListener.h"
class SoComputer;
class SoGroup;
class SbTransaction;
class SbSceneAssert;
class SoField;
class SoFieldContainer;



class COIN_DLL_API SoDistributionGroup : public SoNode { // FIXME: name ?SoDistribGroup SoReplGroup SoReplicationGroup
                                                         // FIXME: shall it be derived from SoNode or SoFieldContainer? PCJohn 2005-09-03
  typedef SoNode inherited;

  SO_NODE_HEADER(SoDistributionGroup);

private:
  int32_t conIdGen;
  SbDict connection2pointer;
  SbDict pointer2connection;
  int lastRecvTransactionComputerIndex;

  class SoDistributionGroupP *pimpl;
  friend class SoDistributionGroupP;

  void registerComputer(SoComputer *comp);
  static void transactionRegisterComputerCB(SbTransaction *tr, const SbName &callbackName, 
                                            void *buf, size_t bufsize);
  static void transactionChangeActivationCB(SbTransaction *tr, const SbName &callbackName,
                                            void *buf, size_t bufsize);

public:
  enum State { DISCONNECTED, CONNECT_ATTEMPT, CONNECTING, RUNNING };

protected:
  virtual ~SoDistributionGroup();

public:
  
  SoMFComputer computer;
  SoMFBool active;
  SoMFNetListener listener;
  
  SbList<SbTransaction*> transactionList;
  SbTimeStamp commitTime;
  SbList<SbSceneAssert*> assertList;

  enum { LATEST_VALUE, COMMITTED_VALUE, USER_HANDLED } updateGraphModel; // FIXME: the same customization
    // should be possible on "field" level (it should be possible for each field to set its value separately)

  const SbTransaction* getTransaction(int index) const;
  SbTransaction* getTransaction(int index);
  int getNumTransactions() const;

  int registerAssert(SbSceneAssert *sa);
  void unregisterAssert(SbSceneAssert *sa);

  // connectionId converting functions
  void* con2pointer(int con) const;
  int pointer2con(void *p) const;

  int appendToDict(SoField *f);
  int appendToDict(SoFieldContainer *c);
  void removeFromDict(SoField *f);
  void removeFromDict(SoFieldContainer *c);

  // send and receive data within the replication group,
  // direct sending function should be internal
  void sendRawData(const void *buf, int size, SbBool sendToNotActive = FALSE);
  void sendRawData(const void *buf, int size, const class SoMFComputer &list);
  //int  recvData(void *buf, int size);

  // sending and receiving within the replication group
  void sendTransaction(SbTransaction *tr); //< The function puts the transaction into the transactionList and then broadcast it to the all distribution group members.
  SbBool processTransactions();           //< Perform commits and aborts of the "old enough" transactions from the end of the transactionList (higher indexes). FALSE is returned on data consistency problem.
  void onTransactionDelete(SbTransaction *tr);
  void onTransactionArrival(SbTransaction *tr);

  const SbTimeStamp& getLowestLargestTimeStamp() const;
  SoComputer* getLowestLargestTimeStampComputer() const;

  // computer identity & this computer
  SbComputerIdentity getComputerIdentity(const SoComputer *comp);
  SbComputerIdentity getThisComputerIdentity();
  int getComputerIndex(const SoComputer *comp) const;
  int getThisComputerIndex() const;
  static SoComputer* getThisComputer();

  // deactivating
  void deactivate();
  void dataIntegrityFailure();

  // listening
  SbBool startListening(SbInetPort port);
  void stopListening(SbInetPort port);
  SbInetPort getListeningPort();
  void getListeningPorts(SbInetPortList &list);

  SbBool startListening(const SbInetAddr &addr);
  void stopListening(const SbInetAddr &addr);
  SbInetAddr getListeningAddress();
  void getListeningAddresses(SbInetAddrList &list);

  SbBool isListening();

  // Accepting
  SoComputer* accept(SbInetPort port);
  SoComputer* accept(const SbInetAddr &addr);

  // Connecting
  SoComputer* connect(const SbInetAddr &ia);
  void accept();
  void sendRegisterComputerTransaction(SoComputer *comp, SbInetAddr &listeningAddr);
  void sendActivationChangeTransaction(int32_t index, SbBool value);
  void updateComputersStates();

  State process();
  State getState() const;

  // Disconnecting
  // FIXME: what about removing computers from computerList? (consistency reasons) and deriving from SoBase (to safely handle removes)
  void disconnectAll();
  void dropAllConnections();
  
  // Quickly designed API
  // makes some compilation troubles. enum { DISCONNECTED, CONNECTING, CONNECTED, REPLICATION, DISCONNECTING, ERROR };
  SoDistributionGroup();

  void sendComputerList(SoComputer *comp);
  static void recvComputerList(void *buf, int size, SbInetAddrList &addrList);

  char* getLastNetworkError();

  static SbName getNullCallbackName();
  static SbName getVerifyExecutionCallbackName();

  static void initClass(void);
};


#endif
