#ifndef SB_NETMESSAGE_H_
#define SB_NETMESSAGE_H_


typedef void SbNetMessageFailureCB(class SbNetMessage *message);


class COIN_DLL_API SbNetMessage {
public:

  SbNetMessage();
  virtual ~SbNetMessage();

  SoComputer* getSender() const  { return sender; }

  // local data (not sent through the network)
  void setLocalData(void *data);
  void* getLocalData();

  virtual void sendTo(SoComputer *comp, SbNetMessageFailureCB *cb = NULL);
  virtual void sendToComputers(const SoComputerList &list, SbNetMessageFailureCB *cb = NULL);
  virtual void sendToNonActiveComputers(SoDistributionGroup *dg, SbNetMessageFailureCB *cb = NULL);
  // FIXME: It is probably really bad idea to send messages to non-active computers because
  // they may not have established their network connections. PCJohn-2007-07-09
  virtual void sendToAnyActiveComputers(SoDistributionGroup *dg, SbNetMessageFailureCB *cb = NULL);
  virtual void sendToAllActiveComputers(SoDistributionGroup *dg, SbNetMessageFailureCB *cb = NULL);
  virtual void sendToAllComputers(SoDistributionGroup *dg, SbNetMessageFailureCB *cb = NULL);

  static SbNetMessage* readMessage(const char *type, SoInput *in, SoComputer *sender);  //< Reads the message from the stream. In the case of error, it returns NULL.
  virtual SbBool read(SoInput *in);  //< Reads the message from the stream. In the case of error, it returns FALSE.
  virtual void write(SoOutput *out) const; //< Writes the message to the stream.

  virtual void process();


  virtual SbBool isTransaction() const { return FALSE; }

  static void initClass();

  virtual SoType getTypeId(void) const;
  SbBool isOfType(SoType type) const;
  static SoType getClassTypeId(void);

protected:
  virtual void createStream(void *&buffer, size_t &size);
  virtual const char* getFileFormatName() const;
private:
  SoComputer* sender;
  void *localData;
  static void* createInstance();
  static SoType classTypeId;
};



#define SB_NETMESSAGE_HEADER() \
public: \
  static void initClass(); \
  virtual SoType getTypeId() const; \
  static SoType getClassTypeId(); \
protected: \
  virtual const char* getFileFormatName() const; \
private: \
  static void* createInstance(); \
  static SoType classTypeId


#define SB_NETMESSAGE_INIT_CLASS(_class_, _parent_) \
do { \
  assert(_class_::classTypeId == SoType::badType() && "The class was initialized two times."); \
  SoType parentType = SoType::fromName(SO__QUOTE(_parent_)); \
  assert(parentType != SoType::badType() && "Parent class was still not initialized."); \
  _class_::classTypeId = SoType::createType(parentType, SO__QUOTE(_class_), &_class_::createInstance); \
} while(0)


#define SB_NETMESSAGE_SRC(_class_, _parent_) \
void _class_::initClass() \
{ \
  SB_NETMESSAGE_INIT_CLASS(_class_, _parent_); \
} \
  \
SoType _class_::getTypeId() const \
{ \
  return _class_::classTypeId; \
} \
  \
const char* _class_::getFileFormatName() const \
{ \
  return SO__QUOTE(_class_); \
} \
  \
void* _class_::createInstance() \
{ \
  return new _class_; \
} \
  \
SoType _class_::classTypeId


class SbNMUserMessage : public SbNetMessage {
  SB_NETMESSAGE_HEADER();

public:
  enum CopyPolicy { COPY, NO_COPY, NO_COPY_AND_DELETE, NO_COPY_AND_FREE };

  SbNMUserMessage();
  SbNMUserMessage(const SoOutput *out);
  SbNMUserMessage(void *buf, size_t size);
  virtual ~SbNMUserMessage();

  // message data
  void setData(void *msg, size_t size, CopyPolicy policy = COPY);
  void getData(void *&msg, size_t &size);

  virtual SbBool read(SoInput *in);  //< Reads the message from the stream. In the case of error, it returns FALSE.
  virtual void write(SoOutput *out) const; //< Writes the message to the stream.

protected:
  void *msg;
  size_t size;
  CopyPolicy copyPolicy;
};


class SbNMNullMessage : public SbNetMessage {
  SB_NETMESSAGE_HEADER();

public:

  SbTimeStamp timeStamp;

  virtual SbBool read(SoInput *in);
  virtual void write(SoOutput *out) const;
  virtual void process();
};

  
#endif /* SB_NETMESSAGE_H_ */
