#ifndef SB_TRANSACTION_P_H_
#define SB_TRANSACTION_P_H_


#include <Inventor/lists/SbList.h>
#include <Inventor/lists/SbIntList.h>
#include <Inventor/fields/SoSubField.h> // for SbItemIdentification, SbWriteSetItem, and SbWriteSetItemValue
                                        // that was need to move into the Coin sources
#include "SbTimeStamp.h"


class SoInput;
class SoOutput;
class SoField;
class SoFieldContainer;
class SbTransaction;



int cve_debugTransactions();
void encodeBinaryData(SoOutput *out, void *data, size_t size);
SbBool decodeBinaryData(SoInput  *in,  void *data, size_t size);



struct COIN_DLL_API SbItemIdentification {
  int32_t connectionId;
  SbIntList path;
  int fieldIndex;

  SbBool pointsDirectlyToAField() const { return fieldIndex == -1; }
  SoFieldContainer* getContainer(SoDistributionGroup *dg) const;
  SoField* getField(SoDistributionGroup *dg) const;

  SbItemIdentification()  {}
  SbItemIdentification(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f)
      { set(t, root, path, f); }
  
  void set(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f);

  SbBool read(SoInput *in);
  void write(SoOutput *out);
/*
 *  file format (BIG endian):
 *
 *   int32_t connectionId
 *   int32_t pathLen        (zero means "direct node" or field target
 * [ int32_t index ]*       present "pathLen" times
 *   int32_t fieldIndex     (-1 means connectionId identifies the field directly)
 */
private:
  SoFieldContainer* getContainer(void *root) const;
};


class COIN_DLL_API SbReadSetItem {
public:
  SoField *field;
  SbTransaction *transaction;

  SbItemIdentification itemId;
  SbTimeStamp timeStamp;

  struct {
    SbBool writeArrived : 2; // two bites are neccessary due to bug in MSVC compiler (see the comments in SbTransaction.h for SbTransaction::objData).
    SbBool commitPredictor : 2;
      // writeExist and writeCommitPredictor are initialized in evaluateCommitPredictor
  } flags;

  SbBool read(SoInput *in, SoDistributionGroup *dg, SbTransaction *t);
  void write(SoOutput *out, SoDistributionGroup *dg);

  SbBool updateCommitPredictor();
  void propagateCommitPredictor();
};


class COIN_DLL_API SbWriteSetItem {
private:
  SoField *field;
  SbTransaction *transaction;

  SbItemIdentification itemId; // FIXME: probably can be removed?

public:
  inline SoField* getField() const  { return field; }
  // FIXME: functions for int getIndex for total list and predicted list
  inline SbTransaction* getTransaction() const  { return transaction; }
  inline const SbItemIdentification* getItemId() const  { return &itemId; }
  inline SbBool isCommitted() const  { return transaction == NULL; }
  inline void makeCommitted()  { transaction = NULL; }

  SbWriteSetItem()  {}
  SbWriteSetItem(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f)
      { set(t,root,path,f); }

  void set(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f);
        
  static SbWriteSetItem* read(SoInput *in, SoDistributionGroup *dg, SbTransaction *t);
  void write(SoOutput *out);

  static void propagateCommitPredictor(SbTransaction *t);

private:
  struct SbPropagRec* startPropagateCommitPredictor(SbBool commitPredictor, SbTransaction *t);
  static void doPropagateCommitPredictor(struct SbPropagRec* rec, SbBool commitPredictor, SbTransaction *t);
};


template <class Type>
class COIN_DLL_API SbWriteSetItemSValue : public SbWriteSetItem {
public:
  Type value;
  
  SbWriteSetItemSValue()  {}
  SbWriteSetItemSValue(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f)
      { set(t, root, path, f); }
};


template <class Type, class ConstArrayType>
class COIN_DLL_API SbWriteSetItemMValue : public SbWriteSetItem {
private:
  Type *values;
  int num;
public:

  inline ConstArrayType getValues()  { return (ConstArrayType)values; }

  void setValues(int start, int num, ConstArrayType newValues) {
    int newNum = start+num;
    if (newNum > this->num)
      setNum(newNum);

    int i;
    for (i=0; i<num; i++)
      values[start++] = (Type)newValues[i]; // do not use memcpy here since it does not invoke copy functions
  }

  inline int getNum()  { return num; }

  void setNum(int newNum) {
    Type *newValues = new Type[newNum];

    int i,c = SbMin(num, newNum);
    for (i=0; i<c; i++)
      newValues[i] = values[i]; // do not use memcpy or realloc since it does not invoke copy functions
    num = newNum;

    delete[] values;
    values = newValues;
    assert(values);
  }
  
  SbWriteSetItemMValue() : values(NULL), num(0)  {}
  SbWriteSetItemMValue(SbTransaction *t, SoFieldContainer *root, const SbIntList &path, SoField *f)
      : values(NULL), num(0)  { set(t, root, path, f); }
  
  ~SbWriteSetItemMValue() {
    free(values);
  }
};



typedef SbList<SbReadSetItem*> SbReadSetItemList;
typedef SbList<SbWriteSetItem*> SbWriteSetItemList;


struct COIN_DLL_API SoCveStorage {
  int32_t connectionId;
  SoDistributionGroup *dg;
  SbWriteSetItem *committedValue;
  SbTimeStamp commitTime; // this copy of timestamp must be placed here, since original timestamp disappear with transaction commit/abort that will release it from memory
  SbReadSetItemList  readList;  // ordered list by timestamps
  SbWriteSetItemList writeList; // ordered list by timestamps
  SbReadSetItemList  unscheduledReads;  // unordered list; Here SbReadSetItems stays until their transaction is scheduled.
  SbWriteSetItemList unscheduledWrites; // unordered list; Here SbWriteSetItems stays until their transaction is scheduled.

  SoCveStorage();
  ~SoCveStorage();

  void orderlyAppendRead(SbReadSetItem *ri);
  void orderlyAppendWrite(SbWriteSetItem *wi);
  void abortAllTransactions();

  enum SearchResult { COMMITTED = -1, OVERWRITTEN = -2, ABORTED = -3, NOT_ARRIVED = -4 };
  static const char* searchResult2string(const int sr);
  int getWriteSetItemIndex(const SbTimeStamp &ts);

  int getFirstLargerReadSetItemIndex(const SbTimeStamp &ts);
  int getSmallerWriteSetItemIndex(const SbTimeStamp &ts);
};


#endif /* SB_TRANSACTION_P_H_ */
