/* fieldValue.H */

#ifndef _FIELDVALUE_H_
#define _FIELDVALUE_H_

#include "xrml.H"
#include "error.H"

namespace xrml {

/* VRML'97 field Types */
typedef enum fieldType {
  tdummy,
  tSFBool, tSFString, tSFInt32, tSFFloat, tSFTime, tSFVec2f, tSFVec3f, tSFColor, tSFRotation, tSFImage, tSFNode,
  tMFBool, tMFString, tMFInt32, tMFFloat, tMFTime, tMFVec2f, tMFVec3f, tMFColor, tMFRotation, tMFImage, tMFNode
} fieldType;

struct fieldValueBase {
  /* union of pointers to classes that represent XRML field values.*/
  union {
    void *any;

    SFBool *sfbool;
    SFString *sfstring;
    SFInt32 *sfint32;
    SFFloat *sffloat;
    SFTime *sftime;
    SFVec2f *sfvec2f;
    SFVec3f *sfvec3f;
    SFColor *sfcolor;
    SFRotation *sfrotation;
    SFImage *sfimage;
    SFNode **sfnode;	   /* (*sfnode) is a pointer to an SFNode, not an SFNode itself */

    MFBool *mfbool;
    MFString *mfstring;
    MFInt32 *mfint32;
    MFFloat *mffloat;
    MFTime *mftime;
    MFVec2f *mfvec2f;
    MFVec3f *mfvec3f;
    MFColor *mfcolor;
    MFRotation *mfrotation;
    MFImage *mfimage;
    MFNode *mfnode;
  } value;

  /* value type (tSFBool etc...) */
  fieldType type;
};

#ifdef __cplusplus

// for printing field type.names.
extern const char *fieldTypeName(const fieldType& type);
inline ostream& operator<<(ostream& s, const fieldType& v) { return s << fieldTypeName(v); } 
extern ostream& operator<<(ostream&, const fieldValueBase&);

class fieldValue: public fieldValueBase {
public:
  PoolDecl(fieldValue, "fieldValue")

protected:
  // checks whether value is of given type (done before assignements e.g.)
  inline int check(const fieldType typ) const {
    if (typ != type) {
      Error("fieldValueBase::check", "type conflict (%s,%s)",
	    fieldTypeName(typ), fieldTypeName(type));
      return 0;
    } else
      return 1;
  }

public:
  // constructors
  inline fieldValue(void) 		{ type = tdummy; value.any = 0; }
  fieldValue(const fieldType type);		// a new object of given type is created
  inline fieldValue(const fieldType typ, void* val)  { type = typ; value.any = val; }
  inline fieldValue(const fieldValueBase& val) { type = val.type; value.any = val.value.any; }

  // the following constructors construct a fieldValue object from a 
  // SFBool ... pointer.
  inline fieldValue(SFBool *val) 	{ type = tSFBool; value.sfbool = val; }
  inline fieldValue(SFInt32 *val) 	{ type = tSFInt32; value.sfint32 = val; }
  inline fieldValue(SFString *val) 	{ type = tSFString; value.sfstring = val; }
  inline fieldValue(SFFloat *val) 	{ type = tSFFloat; value.sffloat = val; }
  inline fieldValue(SFTime *val) 	{ type = tSFTime; value.sftime = val; }
  inline fieldValue(SFVec2f *val) 	{ type = tSFVec2f; value.sfvec2f = val; }
  inline fieldValue(SFVec3f *val) 	{ type = tSFVec3f; value.sfvec3f = val; }
  inline fieldValue(SFColor *val) 	{ type = tSFColor; value.sfcolor = val; }
  inline fieldValue(SFRotation *val) 	{ type = tSFRotation; value.sfrotation = val; }
  inline fieldValue(SFImage *val) 	{ type = tSFImage; value.sfimage = val; }
  inline fieldValue(SFNode **val) 	{ type = tSFNode; value.sfnode = val; }
  inline fieldValue(MFBool *val) 	{ type = tMFBool; value.mfbool = val; }
  inline fieldValue(MFInt32 *val) 	{ type = tMFInt32; value.mfint32 = val; }
  inline fieldValue(MFString *val) 	{ type = tMFString; value.mfstring = val; }
  inline fieldValue(MFFloat *val) 	{ type = tMFFloat; value.mffloat = val; }
  inline fieldValue(MFTime *val) 	{ type = tMFTime; value.mftime = val; }
  inline fieldValue(MFVec2f *val) 	{ type = tMFVec2f; value.mfvec2f = val; }
  inline fieldValue(MFVec3f *val) 	{ type = tMFVec3f; value.mfvec3f = val; }
  inline fieldValue(MFColor *val) 	{ type = tMFColor; value.mfcolor = val; }
  inline fieldValue(MFRotation *val) 	{ type = tMFRotation; value.mfrotation = val; }
  inline fieldValue(MFImage *val) 	{ type = tMFImage; value.mfimage = val; }
  inline fieldValue(MFNode *val) 	{ type = tMFNode; value.mfnode = val; }

  // assginement operator: no type checking and only the value pointer is 
  // copied. See assign(), duplicate() and instantiate() below if the 
  // object pointed to needs to be copied or duplicated.
  inline fieldValue& operator=(const fieldValue& val)
  { value.any = val.value.any; type = val.type; return *this; }

  // type convertors: retrieve type and value pointer.
  inline operator fieldType() const { return type; }
  inline operator void*() const     { return value.any; }

  // type convertors (cast a fieldValue to a SFBool pointer etc...)
  inline operator SFBool*() 	{ check(tSFBool); return value.sfbool; }
  inline operator SFString*() 	{ check(tSFString); return value.sfstring; }
  inline operator SFInt32*() 	{ check(tSFInt32); return value.sfint32; }
  inline operator SFFloat*() 	{ check(tSFFloat); return value.sffloat; }
  inline operator SFTime*() 	{ check(tSFTime); return value.sftime; }
  inline operator SFVec2f*() 	{ check(tSFVec2f); return value.sfvec2f; }
  inline operator SFVec3f*() 	{ check(tSFVec3f); return value.sfvec3f; }
  inline operator SFColor*() 	{ check(tSFColor); return value.sfcolor; }
  inline operator SFRotation*(){ check(tSFRotation); return value.sfrotation; }
  inline operator SFImage*() 	{ check(tSFImage); return value.sfimage; }
  inline operator SFNode**() 	{ check(tSFNode); return value.sfnode; }
  inline operator MFBool*() 	{ check(tMFBool); return value.mfbool; }
  inline operator MFString*() 	{ check(tMFString); return value.mfstring; }
  inline operator MFInt32*() 	{ check(tMFInt32); return value.mfint32; }
  inline operator MFFloat*() 	{ check(tMFFloat); return value.mffloat; }
  inline operator MFTime*() 	{ check(tMFTime); return value.mftime; }
  inline operator MFVec2f*() 	{ check(tMFVec2f); return value.mfvec2f; }
  inline operator MFVec3f*() 	{ check(tMFVec3f); return value.mfvec3f; }
  inline operator MFColor*() 	{ check(tMFColor); return value.mfcolor; }
  inline operator MFRotation*(){ check(tMFRotation); return value.mfrotation; }
  inline operator MFImage*() 	{ check(tMFImage); return value.mfimage; }
  inline operator MFNode*() 	{ check(tMFNode); return value.mfnode; }

  // value retrieval: return (a reference to) the SFBool ... value pointed to
  inline operator const SFBool&() const	{ check(tSFBool); return *value.sfbool; }
  inline operator const SFString&() const	{ check(tSFString); return *value.sfstring; }
  inline operator const SFInt32&() const	{ check(tSFInt32); return *value.sfint32; }
  inline operator const SFFloat&() const	{ check(tSFFloat); return *value.sffloat; }
  inline operator const SFTime&() const	{ check(tSFTime); return *value.sftime; }
  inline operator const SFVec2f&() const	{ check(tSFVec2f); return *value.sfvec2f; }
  inline operator const SFVec3f&() const	{ check(tSFVec3f); return *value.sfvec3f; }
  inline operator const SFColor&() const	{ check(tSFColor); return *value.sfcolor; }
  inline operator const SFRotation&() const	{ check(tSFRotation); return *value.sfrotation; }
  inline operator const SFImage&() const	{ check(tSFImage); return *value.sfimage; }
  inline operator SFNode*&() const		{ check(tSFNode); return *value.sfnode; }
  inline operator const MFBool&() const	{ check(tMFBool); return *value.mfbool; }
  inline operator const MFString&() const	{ check(tMFString); return *value.mfstring; }
  inline operator const MFInt32&() const	{ check(tMFInt32); return *value.mfint32; }
  inline operator const MFFloat&() const	{ check(tMFFloat); return *value.mffloat; }
  inline operator const MFTime&() const	{ check(tMFTime); return *value.mftime; }
  inline operator const MFVec2f&() const	{ check(tMFVec2f); return *value.mfvec2f; }
  inline operator const MFVec3f&() const	{ check(tMFVec3f); return *value.mfvec3f; }
  inline operator const MFColor&() const	{ check(tMFColor); return *value.mfcolor; }
  inline operator const MFRotation&() const { check(tMFRotation); return *value.mfrotation; }
  inline operator const MFImage&() const	{ check(tMFImage); return *value.mfimage; }
  inline operator const MFNode&() const	{ check(tMFNode); return *value.mfnode; }

  // The following methods assign or duplicate the values pointed to
  // by sf*Value. Watch out: especially for SFImage values, assignment or
  // duplication may require copying of large amounts of data.

  // Checks the type and assigns the object pointed to by src.
  // - this->*Value (pointer) remains the same. The object pointed to by this->*Value
  //   is assigned the value of the object pointed to by src.*Value.
  // - if the type is tSFNode, only the pointer to the node is assigned:
  //   *(src.sfnode) and *(this->sfnode) will be SFNode*'s pointing to the same SFNode,
  //   which can be NULL.
  // - this->*Value and src.*Value must not be nil pointers.
  void assign(const fieldValue& src);

  // Duplicates the object pointed to by src.*Value ands let this->*Value 
  // point to the duplicate:
  // - this->*Value (pointer) gets a new value
  // - If the type is tSFNode, only the pointer to the node is duplicated.
  //   that means: a new SFNode* is created and initialized with
  //   *src.value.sfnode (which can be NULL).
  // - this->*Value can be nil, but src.*Value must not be nil
  void duplicate(const fieldValue& src);
  void duplicate(void) { duplicate(*this); }

  // Creates a new instance of the object pointed to by 'this' and lets
  // this->*Value point to the new instance. Only affects fieldValues
  // of type SFNode or MFNode (see SFNode::instantiate()).
  void instantiate(class Proto *master =0);

  // deletes the object pointed to
  void destroy(void);

  // saves the value to the specified file handle, with proper
  // indentation etc...
  void save(class file* file_handle);
};

///////////////////////////////////////////////////////////////////////////////
// [S|M]FNode value checking

extern bool checkValue(MFNode& nodes,
		       bool (*checkType)(SFNode*),
		       const char *fieldname, const char *typeclassname);

extern bool checkValue(SFNode*& node,
		       bool (*checkType)(SFNode*),
		       const char *fieldname, const char *typeclassname);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FInt32 value checking

inline bool equal(const SFInt32& val1, const SFInt32& val2)	{ return (int)val1 == (int)val2; }
inline bool greater(const SFInt32& val1, const SFInt32& val2) 	{ return (int)val1 > (int)val2; }
inline bool grequal(const SFInt32& val1, const SFInt32& val2) 	{ return (int)val1 >= (int)val2; }
inline bool less(const SFInt32& val1, const SFInt32& val2)	{ return (int)val1 < (int)val2; }
inline bool lequal(const SFInt32& val1, const SFInt32& val2)	{ return (int)val1 <= (int)val2; }

extern bool checkValue(const SFInt32& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFInt32&, const SFInt32&), const char *opname, const SFInt32& threshold);
extern bool checkValue(const SFInt32& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFInt32&, const SFInt32&), const char *op1name, const SFInt32& threshold1,
		       bool (*op2)(const SFInt32&, const SFInt32&), const char *op2name, const SFInt32& threshold2);
extern bool checkValue(const MFInt32& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFInt32&, const SFInt32&), const char *opname, const SFInt32& threshold);
extern 	bool checkValue(const MFInt32& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFInt32&, const SFInt32&), const char *op1name, const SFInt32& threshold1,
			bool (*op2)(const SFInt32&, const SFInt32&), const char *op2name, const SFInt32& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FFloat value checking

inline bool equal(const SFFloat& val1, const SFFloat& val2)	{ return (float)val1 == (float)val2; }
inline bool greater(const SFFloat& val1, const SFFloat& val2) 	{ return (float)val1 > (float)val2; }
inline bool grequal(const SFFloat& val1, const SFFloat& val2) 	{ return (float)val1 >= (float)val2; }
inline bool less(const SFFloat& val1, const SFFloat& val2)	{ return (float)val1 < (float)val2; }
inline bool lequal(const SFFloat& val1, const SFFloat& val2)	{ return (float)val1 <= (float)val2; }

extern bool checkValue(const SFFloat& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFFloat&, const SFFloat&), const char *opname, const SFFloat& threshold);
extern bool checkValue(const SFFloat& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFFloat&, const SFFloat&), const char *op1name, const SFFloat& threshold1,
		       bool (*op2)(const SFFloat&, const SFFloat&), const char *op2name, const SFFloat& threshold2);
extern bool checkValue(const MFFloat& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFFloat&, const SFFloat&), const char *opname, const SFFloat& threshold);
extern 	bool checkValue(const MFFloat& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFFloat&, const SFFloat&), const char *op1name, const SFFloat& threshold1,
			bool (*op2)(const SFFloat&, const SFFloat&), const char *op2name, const SFFloat& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FTime value checking

inline bool equal(const SFTime& val1, const SFTime& val2)	{ return (double)val1 == (double)val2; }
inline bool greater(const SFTime& val1, const SFTime& val2) 	{ return (double)val1 > (double)val2; }
inline bool grequal(const SFTime& val1, const SFTime& val2) 	{ return (double)val1 >= (double)val2; }
inline bool less(const SFTime& val1, const SFTime& val2)	{ return (double)val1 < (double)val2; }
inline bool lequal(const SFTime& val1, const SFTime& val2)	{ return (double)val1 <= (double)val2; }

extern bool checkValue(const SFTime& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFTime&, const SFTime&), const char *opname, const SFTime& threshold);
extern bool checkValue(const SFTime& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFTime&, const SFTime&), const char *op1name, const SFTime& threshold1,
		       bool (*op2)(const SFTime&, const SFTime&), const char *op2name, const SFTime& threshold2);
extern bool checkValue(const MFTime& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFTime&, const SFTime&), const char *opname, const SFTime& threshold);
extern 	bool checkValue(const MFTime& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFTime&, const SFTime&), const char *op1name, const SFTime& threshold1,
			bool (*op2)(const SFTime&, const SFTime&), const char *op2name, const SFTime& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FVec2f value checking

inline bool equal(const SFVec2f& val1, const SFVec2f& val2)	{ return val1.s == val2.s && val1.t == val2.t; }
inline bool greater(const SFVec2f& val1, const SFVec2f& val2) 	{ return val1.s > val2.s && val1.t > val2.t; }
inline bool grequal(const SFVec2f& val1, const SFVec2f& val2) 	{ return val1.s >= val2.s && val1.t >= val2.t; }
inline bool less(const SFVec2f& val1, const SFVec2f& val2)	{ return val1.s < val2.s && val1.t < val2.t; }
inline bool lequal(const SFVec2f& val1, const SFVec2f& val2)	{ return val1.s <= val2.s && val1.t <= val2.t; }

extern bool checkValue(const SFVec2f& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFVec2f&, const SFVec2f&), const char *opname, const SFVec2f& threshold);
extern bool checkValue(const SFVec2f& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFVec2f&, const SFVec2f&), const char *op1name, const SFVec2f& threshold1,
		       bool (*op2)(const SFVec2f&, const SFVec2f&), const char *op2name, const SFVec2f& threshold2);
extern bool checkValue(const MFVec2f& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFVec2f&, const SFVec2f&), const char *opname, const SFVec2f& threshold);
extern 	bool checkValue(const MFVec2f& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFVec2f&, const SFVec2f&), const char *op1name, const SFVec2f& threshold1,
			bool (*op2)(const SFVec2f&, const SFVec2f&), const char *op2name, const SFVec2f& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FVec3f value checking

inline bool equal(const SFVec3f& val1, const SFVec3f& val2)	{ return val1.x == val2.x && val1.y == val2.y && val1.z == val2.z; }
inline bool greater(const SFVec3f& val1, const SFVec3f& val2) 	{ return val1.x > val2.x && val1.y > val2.y && val1.z > val2.z; }
inline bool grequal(const SFVec3f& val1, const SFVec3f& val2) 	{ return val1.x >= val2.x && val1.y >= val2.y && val1.z >= val2.z; }
inline bool less(const SFVec3f& val1, const SFVec3f& val2)	{ return val1.x < val2.x && val1.y < val2.y && val1.z < val2.z; }
inline bool lequal(const SFVec3f& val1, const SFVec3f& val2)	{ return val1.x <= val2.x && val1.y <= val2.y && val1.z <= val2.z; }

extern bool checkValue(const SFVec3f& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFVec3f&, const SFVec3f&), const char *opname, const SFVec3f& threshold);
extern bool checkValue(const SFVec3f& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFVec3f&, const SFVec3f&), const char *op1name, const SFVec3f& threshold1,
		       bool (*op2)(const SFVec3f&, const SFVec3f&), const char *op2name, const SFVec3f& threshold2);
extern bool checkValue(const MFVec3f& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFVec3f&, const SFVec3f&), const char *opname, const SFVec3f& threshold);
extern 	bool checkValue(const MFVec3f& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFVec3f&, const SFVec3f&), const char *op1name, const SFVec3f& threshold1,
			bool (*op2)(const SFVec3f&, const SFVec3f&), const char *op2name, const SFVec3f& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FColor value checking

inline bool equal(const SFColor& val1, const SFColor& val2)	{ return val1.r == val2.r && val1.g == val2.g && val1.b == val2.b; }
inline bool greater(const SFColor& val1, const SFColor& val2) 	{ return val1.r > val2.r && val1.g > val2.g && val1.b > val2.b; }
inline bool grequal(const SFColor& val1, const SFColor& val2) 	{ return val1.r >= val2.r && val1.g >= val2.g && val1.b >= val2.b; }
inline bool less(const SFColor& val1, const SFColor& val2)	{ return val1.r < val2.r && val1.g < val2.g && val1.b < val2.b; }
inline bool lequal(const SFColor& val1, const SFColor& val2)	{ return val1.r <= val2.r && val1.g <= val2.g && val1.b <= val2.b; }

extern bool checkValue(const SFColor& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFColor&, const SFColor&), const char *opname, const SFColor& threshold);
extern bool checkValue(const SFColor& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFColor&, const SFColor&), const char *op1name, const SFColor& threshold1,
		       bool (*op2)(const SFColor&, const SFColor&), const char *op2name, const SFColor& threshold2);
extern bool checkValue(const MFColor& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFColor&, const SFColor&), const char *opname, const SFColor& threshold);
extern 	bool checkValue(const MFColor& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFColor&, const SFColor&), const char *op1name, const SFColor& threshold1,
			bool (*op2)(const SFColor&, const SFColor&), const char *op2name, const SFColor& threshold2);

///////////////////////////////////////////////////////////////////////////////
// [S|M]FRotation value checking

inline bool equal(const SFRotation& val1, const SFRotation& val2)	{ return val1.x == val2.x && val1.y == val2.y && val1.z == val2.z && val1.radians == val2.radians; }
inline bool greater(const SFRotation& val1, const SFRotation& val2) 	{ return val1.x > val2.x && val1.y > val2.y && val1.z > val2.z && val1.radians > val2.radians; }
inline bool grequal(const SFRotation& val1, const SFRotation& val2) 	{ return val1.x >= val2.x && val1.y >= val2.y && val1.z >= val2.z && val1.radians >= val2.radians; }
inline bool less(const SFRotation& val1, const SFRotation& val2)	{ return val1.x < val2.x && val1.y < val2.y && val1.z < val2.z && val1.radians < val2.radians; }
inline bool lequal(const SFRotation& val1, const SFRotation& val2)	{ return val1.x <= val2.x && val1.y <= val2.y && val1.z <= val2.z && val1.radians <= val2.radians; }

extern bool checkValue(const SFRotation& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFRotation&, const SFRotation&), const char *opname, const SFRotation& threshold);
extern bool checkValue(const SFRotation& value, class SFNode* node, const char *fieldId,
		       bool (*op1)(const SFRotation&, const SFRotation&), const char *op1name, const SFRotation& threshold1,
		       bool (*op2)(const SFRotation&, const SFRotation&), const char *op2name, const SFRotation& threshold2);
extern bool checkValue(const MFRotation& value, class SFNode* node, const char *fieldId,
		       bool (*op)(const SFRotation&, const SFRotation&), const char *opname, const SFRotation& threshold);
extern 	bool checkValue(const MFRotation& value, class SFNode* node, const char *fieldId,
			bool (*op1)(const SFRotation&, const SFRotation&), const char *op1name, const SFRotation& threshold1,
			bool (*op2)(const SFRotation&, const SFRotation&), const char *op2name, const SFRotation& threshold2);

} // namespace xrml

#endif /*__cplusplus*/

#endif /*_FIELDVALUE_H_*/
