#ifndef SB_TRANSACTION_H_
#define SB_TRANSACTION_H_


/*
types of action:
- READ field (fieldId, timestamp)
- WRITE field (fieldId, newValue)
#- READ mField (fieldId, timestamp, [count], [all|(index|(fromIndex,toIndex))*])
#- WRITE mField (fieldId, newValue, [setNum], [index, newValue]*)

- READ nodeList (nodeId, [count], [all|(indexRec|(fromIndexRec,toIndexRec))*])
- INSERT node (parent, indexRec, child)
- REPLACE node (parent, indexRec, child)
- REMOVE node (parent, indexRec)

- CONNECT - stream data
          - user defined code
          - leave as is
- DISCONNECT - set to commited
             - keep last predicted


  newValue - stream data
           - user defined code

  nodeId - path from connected point (pointId and path within some replication group),
           path can be empty (in the case of "node connection")

  fieldId - nodeId, fieldName
          - nodeId, fieldIndex

  parent - nodeId

  child - nodeId
        - stream data
        - user defined code

  indexRec - index
           - indexOrig - beginning
                       - end
                       - relative position (from node given by an index)
           - canShift - strict position (if need update, abort)
                          - canReplace flag
                      - weak position (if modified, update)

read set
- itemId (connectionId, [nodeId], [fieldId])
- timestamp

write set
- itemid
- newValue
  */

#include <Inventor/lists/SbList.h>
#include "SbTimeStamp.h"

class SoField;
class SoGroup;
class SoInput;
class SbTransaction;
class SoDistributionGroup;
class SbSceneAssert;

#include "SbNetMessage.h"
#include "SbTransactionP.h"



typedef void SbTransactionCB(SbTransaction *tr, const SbName &callbackName, void *buf, size_t bufsize);



class COIN_DLL_API SbTransaction : public SbNetMessage {
public:
  enum State { CONSTRUCTING, SCHEDULED, COMMITTED, ABORTED };

private:
  SoDistributionGroup *distributionGroup;
  SbTimeStamp timeStamp;       // transaction timestamp
  SbBool global;

  SbReadSetItemList readSet;
  SbWriteSetItemList writeSet;
  SbList<SbSceneAssert*> assertList;

  SbName commitCallbackName;
  SbName abortCallbackName;
  void *userData;
  size_t userDataSize;

#if 0
  struct {
    State state;
    SbBool readSetComplete;
    SbBool readSetCommitPredictor;
    SbBool assertCommitPredictor;
    SbBool currentCommitPredictor;
  } objData;
#else
  struct {
    State state : 3; // one additional bit is necessary because of a bug in VC6
    SbBool readSetComplete : 2; // VC6 bug: if only one bite is used, comparison (readSetComplete != TRUE) gives 
    SbBool readSetCommitPredictor : 2; // wrong result when readSetComplete is TRUE (VC6 is mistified considering
    SbBool assertCommitPredictor  : 2; // readSetComplete value as -1 and comparing with TRUE (+1) results in incorrect result)
    SbBool currentCommitPredictor : 2; // PCJohn-2005-09-02
  } objData;
#endif

public:
  inline void notifyReadSetWriteArrived()  { updateReadSetComplete(); }
  void notifyReadSetCommitPredictorChanged(SbBool newValue);

protected:
  void putWriteSetValuesToPublic();
  virtual ~SbTransaction(); // warning: Do not destruct the object explicitly, 
                            // it should be done from commit() and abort() functions only.
public: // should these be exposed in public API?
  void dumpTransactionCPs() const;
  SbBool updateReadSetComplete();
  SbBool updateReadSetCommitPredictor();
  SbBool updateAssertCommitPredictor(SbBool checkForCommit);
  SbBool updateCurrentCommitPredictor();
  void propagateCommitPredictor();
  inline SbBool getReadSetCommitPredictor() const { return objData.readSetCommitPredictor; }

public:

  // Constructors
  SbTransaction();
  SbTransaction(SoDistributionGroup *dg);

  // Transaction state management
  void schedule();  //< Should be used on the sender. It turns the transaction state into the SCHEDULED and sends it to all distribution group members.
  SbBool commit();  //< Commits and then deletes the transaction.
  SbBool abort();   //< Aborts and then deletes the transaction.
  SbBool execute(); //< Checks the commitability of the transaction and based on the result, the transaction is commited or aborted.

  // Scene Assert management
  void appendAssert(SbSceneAssert *sa);
  SbSceneAssert* getSceneAssert(int index) const;
  int getNumSceneAsserts() const;

  SbBool checkAsserts(SbBool checkForCommit) const;

  // global status
  void setGlobal(SbBool value);
  SbBool isGlobal() const;

  // callback management
  void setCommitCallback(const SbName &name);
  SbName getCommitCallback() const;
  void setAbortCallback(const SbName &name);
  SbName getAbortCallback() const;

  static void registerCallback(const SbName &name, SbTransactionCB *cb);
  static SbTransactionCB* getRegisteredCallbackFunc(const SbName &name);

  // data
  void setUserData(const void *data, size_t datasize);
  void getUserData(void *&data, size_t &datasize);

  // commit predictor
  SbBool getCommitPredictor() const;
  //void setCommitPredictor(SbBool value);


  // Stream functions
  virtual SbBool read(SoInput *in);  //< Reads the transaction from the stream. Internally, it registers all reads and writes in their data items. In the case of error, it returns FALSE.
  virtual void write(SoOutput *out) const; //< Writes the transaction to the stream.


  // State retrieval
  SoDistributionGroup* getDistributionGroup() const;
  const SbTimeStamp& getTimeStamp() const;
  State getState() const;
  const SbReadSetItemList& getReadSetItems() const  { return readSet; }
  const SbWriteSetItemList& getWriteSetItems() const  { return writeSet; }
  SoComputer* getSender() const;
  int getSenderIndex() const;
  SbBool isSenderLocalhost() const;
  virtual SbBool isTransaction() const { return TRUE; }

  // Internal functions
  void appendReadSetItem(SbReadSetItem *ri);
  void appendWriteSetItem(SbWriteSetItem *wi);

  // debugging functions
  void assertAlive();
  void assertState(State s, const char *msg = NULL);
};


#endif /* SB_TRANSACTION_H_ */
