#ifndef COIN_SOFIELD_H
#define COIN_SOFIELD_H

/**************************************************************************\
 *
 *  This file is part of the Coin 3D visualization library.
 *  Copyright (C) 1998-2006 by Systems in Motion.  All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  ("GPL") version 2 as published by the Free Software Foundation.
 *  See the file LICENSE.GPL at the root directory of this source
 *  distribution for additional information about the GNU GPL.
 *
 *  For using Coin with software that can not be combined with the GNU
 *  GPL, and for taking advantage of the additional benefits of our
 *  support services, please contact Systems in Motion about acquiring
 *  a Coin Professional Edition License.
 *
 *  See http://www.coin3d.org/ for more information.
 *
 *  Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
 *  http://www.sim.no/  sales@sim.no  coin-support@coin3d.org
 *
\**************************************************************************/

#include <Inventor/SoType.h>
#include <Inventor/misc/SoNotification.h>

class SbString;
class SoEngineOutput;
class SoFieldContainer;
class SoFieldConverter;
class SoFieldList;
class SoInput;
class SoOutput;

class COIN_DLL_API SoField {

public:
  virtual ~SoField();

  static void initClass(void);
  static void initClasses(void);

  void setIgnored(SbBool ignore);
  SbBool isIgnored(void) const;

  void setDefault(SbBool def);
  SbBool isDefault(void) const;

  virtual SoType getTypeId(void) const = 0;

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

  void enableConnection(SbBool flag);
  SbBool isConnectionEnabled(void) const;

  // Field<-Engine connection stuff.
  SbBool connectFrom(SoEngineOutput * master,
                     SbBool notnotify = FALSE, SbBool append = FALSE);
  SbBool appendConnection(SoEngineOutput * master, SbBool notnotify = FALSE);
  void disconnect(SoEngineOutput * engineoutput);
  SbBool isConnectedFromEngine(void) const;
  SbBool getConnectedEngine(SoEngineOutput *& master) const;

  // Field<->Field connection stuff.
  SbBool connectFrom(SoField * master,
                     SbBool notnotify = FALSE, SbBool append = FALSE);
  SbBool appendConnection(SoField * master, SbBool notnotify = FALSE);
  void disconnect(SoField * field);
  SbBool isConnectedFromField(void) const;
  SbBool getConnectedField(SoField *& master) const;
  int getNumConnections(void) const;
  int getForwardConnections(SoFieldList & slavelist) const;
  int getConnections(SoFieldList & masterlist) const;

  void disconnect(void);
  SbBool isConnected(void) const;

  void setContainer(SoFieldContainer * cont);
  SoFieldContainer * getContainer(void) const;

  SbBool set(const char * valuestring);
  void get(SbString & valuestring);

  SbBool shouldWrite(void) const;

  virtual void touch(void);
  virtual void startNotify(void);
  virtual void notify(SoNotList * nlist);
  SbBool enableNotify(SbBool on);
  SbBool isNotifyEnabled(void) const;

  void addAuditor(void * f, SoNotRec::Type type);
  void removeAuditor(void * f, SoNotRec::Type type);

  int operator ==(const SoField & f) const;
  int operator !=(const SoField & f) const;

  virtual void connectionStatusChanged(int numconnections);
  SbBool isReadOnly(void) const;
  virtual SbBool isSame(const SoField & f) const = 0;
  virtual void copyFrom(const SoField & f) = 0;

  virtual void fixCopy(SbBool copyconnections);
  virtual SbBool referencesCopy(void) const;
  void copyConnection(const SoField * fromfield);

  virtual SbBool read(SoInput * in, const SbName & name);
  virtual void write(SoOutput * out, const SbName & name) const;

  virtual void countWriteRefs(SoOutput * out) const;

  // enums for setFieldType()/getFieldType()
  enum FieldType {
    NORMAL_FIELD = 0,
    EVENTIN_FIELD,
    EVENTOUT_FIELD,
    EXPOSED_FIELD
  };

  void setFieldType(int type);
  int getFieldType(void) const;

  SbBool getDirty(void) const;
  void setDirty(SbBool dirty);

  void evaluate(void) const {
    if ((this->statusbits & (FLAG_EXTSTORAGE|FLAG_NEEDEVALUATION)) == 
        (FLAG_EXTSTORAGE|FLAG_NEEDEVALUATION)) this->evaluateField();
  }

#ifdef CVE_ENABLED

  void extendCveStorageIfNecessary();
  void releaseCveStorage();
  struct SoCveStorage* getCveStorage();
  const struct SoCveStorage* getCveStorage() const;
  inline SbBool myReadValue(SoInput * in)  { return readValue(in); }
  inline void myWriteValue(SoOutput * out) const  { writeValue(out); }

  virtual void cveSetFieldValue(class SbWriteSetItem *wsi)  {}
  virtual void cveGetFieldValue(class SbWriteSetItem *wsi) const {}

  virtual SbWriteSetItem* cveCreateWriteSetItem() const { return NULL; }
  virtual SbBool cveReadValue(SoInput *in, class SbWriteSetItem *wsi)  { return FALSE; }
  virtual void cveWriteValue(SoOutput *out, class SbWriteSetItem *wsi)  {}
  SbWriteSetItem* getCveWriteItem(int index, SbBool predictedOnly = TRUE) const;
  
  // functions intended to be used by a user
  enum CveValueIndex { COMMITTED = -1, LATEST = -2, USER_VIEW = -3 };
  const class SbTimeStamp& getCveTimeStamp(int index, SbBool predictedOnly = TRUE) const;
  void setCommitTimeStamp(const SbTimeStamp& ts);
  int getCveNumValues(SbBool predictedOnly = TRUE) const;
  SbBool cveIsPredicted(int index) const;

  class SbTransaction* getTransaction(int index, SbBool predictedOnly = TRUE) const;
  int getNumTransactions(SbBool predictedOnly = TRUE) const;

  void transactionNotifyRead(int index, class SbTransaction *t,
                             SoFieldContainer *root, const class SbIntList &path,
                             SbBool predictedOnly = TRUE);
  virtual void transactionNotifyWrite(class SbTransaction *t,
                                      SoFieldContainer *root, const class SbIntList &path)  {}

#endif // CVE_ENABLED

protected:
  SoField(void);

  void valueChanged(SbBool resetdefault = TRUE);
  virtual void evaluateConnection(void) const;
  virtual SbBool readValue(SoInput * in) = 0;
  virtual void writeValue(SoOutput * out) const = 0;
  virtual SbBool readConnection(SoInput * in);
  virtual void writeConnection(SoOutput * out) const;

  SbBool isDestructing(void) const;

private:

  enum FieldFlags {
    FLAG_TYPEMASK = 0x0007,  // need 3 bits for values [0-5]
    FLAG_ISDEFAULT = 0x0008,
    FLAG_IGNORE = 0x0010,
    FLAG_EXTSTORAGE = 0x0020,
    FLAG_ENABLECONNECTS = 0x0040,
    FLAG_NEEDEVALUATION = 0x0080,
    FLAG_READONLY = 0x0100,
    FLAG_DONOTIFY = 0x0200,
    FLAG_ISDESTRUCTING = 0x0400,
    FLAG_ISEVALUATING = 0x0800,
    FLAG_ISNOTIFIED = 0x1000
  };

  void evaluateField(void) const;
  void extendStorageIfNecessary(void);
  SoFieldConverter * createConverter(SoType from) const;
  SoFieldContainer * resolveWriteConnection(SbName & mastername) const;

  void notifyAuditors(SoNotList * l);

  static SoType classTypeId;

  // These are bit flags.
  enum FileFormatFlags {
    IGNORED = 0x01,
    CONNECTED = 0x02,
    DEFAULT = 0x04,
    ALLFILEFLAGS = IGNORED|CONNECTED|DEFAULT
  };

  SbBool changeStatusBits(const unsigned int bits, const SbBool onoff);
  void clearStatusBits(const unsigned int bits);
  void setStatusBits(const unsigned int bits);
  SbBool getStatus(const unsigned int bits) const;
  unsigned int statusbits;
  union {
    SoFieldContainer * container;
    class SoConnectStorage * storage;
  };

  SbBool hasExtendedStorage(void) const;
};


#ifndef COIN_INTERNAL
// Added to be Inventor compliant.
#include <Inventor/fields/SoSField.h>
#include <Inventor/fields/SoMField.h>
#endif // !COIN_INTERNAL

#endif // !COIN_SOFIELD_H
