/* nodeInterface.H: fields, exposedFields, eventIns and eventOuts */

#ifndef _NODEINTERFACE_H_
#define _NODEINTERFACE_H_

#include "xrml.H"
#include "fieldValue.H"
#include "List.H"

namespace xrml {
/////////////////////////////////////////////////////////////////////////

class interfaceMemberRef {
public:
  PoolDecl(interfaceMemberRef, "interfaceMemberRef")

  // Stores a reference to an interface member of another node.
  // Used for links (IS statements inside PROTO body) and
  // routes (ROUTE statements).
  // We don't store a pointer to a interfaceMember directly, but
  // rather the referenced node and the index of the interface member
  // in the referenced nodes interface. This makes instantiation
  // quite a bit easier while the cost of following the reference
  // remains very low.
  class SFNode *refnode;
  int refidx;

protected:
  void _init(class SFNode *refnode =0, class interfaceMember *refifm =0);
  inline void init(class SFNode *refnode, class interfaceMember* refifm) { _init(refnode, refifm); }

public:
  inline interfaceMemberRef(void) { _init(); }
  inline interfaceMemberRef(class SFNode* refnode, class interfaceMember* refifm) { _init(refnode, refifm); }

  inline interfaceMember *ref(void) { return refnode->interface[refidx]; }
  ostream& print(ostream&);
};

class link: public interfaceMemberRef {
public:
  PoolDecl(link, "link")

  // references an interface member of the inenrmost enclosing PROTO
  // definition node.
protected:
  void init(class SFNode* refnode, class interfaceMember* refifm, class SFNode* srcnode, class interfaceMember* srcifm);

public:
  link(void) { interfaceMemberRef::_init(); }
  void instantiate(class Proto* master, class SFNode* srcnode, class interfaceMember* srcifm);
};

class routing_table {
  // Linear list of routes. Implemented in route.C.
  typedef class list<route> route_list;

public:
  PoolDecl(routing_table, "routing_table")
  route_list *routes;

protected:
  inline void _init(void) { routes = 0; }
  inline void init(void) { _init(); }

public:
  inline routing_table(void) { _init(); }

  // Duplicates the routes and stores them in 'dst'.
  void clone(class routing_table* dst) const;

  void instantiate(class Proto* master =0);
  void dispatch(const fieldValue& value) const;
  void add(class SFNode *dstnode, class interfaceMember *dstifm);

  ostream& print(ostream& s);
};

/////////////////////////////////////////////////////////////////////////

#define CONSTRUCT_TIME -1e76	// special "just constructed" time stamp

class interfaceMember {
public:
  PoolDecl(interfaceMember, "interfaceMember")

  const char* Id;		// Id name
  class fieldValue value;	// type is encoded in value.type
  double timestamp;		// time of last update

  // Forward links: added to a PROTO interface member for each
  // IS statement that refers to it.
  typedef class list<class interfaceMemberRef> forward_link_list;
  forward_link_list *forward_links;

  // casts to value field type
  inline operator fieldType()  { return fieldType(value); }

protected:
  // checks whether value type of 'this' is the same as for 'refvalue'
  // and issues an error message if not the same. Returns true if same
  // and false if not the same.
  bool check_type(const fieldValue& refvalue);

  // initialisations for values introduces in this particular
  // interfaceMember subclass
  inline void _init(const char *Id, const fieldValue& value, const double timestamp =CONSTRUCT_TIME)
    {
      interfaceMember::Id = Id;
      interfaceMember::value = value;
      interfaceMember::timestamp = timestamp;
      interfaceMember::forward_links = 0;
    }

  // initialisations for the whole subclass (the proper sublass and all
  // parent classes.
  inline void init(const char *Id, const fieldValue& value, const double timestamp =CONSTRUCT_TIME)
    {
      _init(Id, value, timestamp);
    }

  ostream& print_Id_value(ostream& s);

  virtual interfaceMember* link_eventIn(class SFNode* thisnode, class SFNode* refnode, class eventIn* refifm)
    { return 0; /* don't allow linking with eventIns */ }
  virtual interfaceMember* link_exposedField(class SFNode* thisnode, class SFNode* refnode, class exposedField* refifm)
    { return 0; /* don't allow linking with exposedFields */ }
  virtual interfaceMember* link_field(class SFNode* thisnode, class SFNode* refnode, class field* refifm)
    { return 0; /* don't allow linking with fields */ }
  virtual interfaceMember* link_eventOut(class SFNode* thisnode, class SFNode* refnode, class eventOut* refifm)
    { return 0; /* don't allow linking with eventOuts */ }

public:
  inline interfaceMember(void) {  init(0, fieldValue(tdummy, 0), CONSTRUCT_TIME); }

  // returns an exact copy of 'this'
  virtual interfaceMember* clone(void);

  // the argument 'thisnode' of the following member functions
  // is the node containing the interface member 'this'. We need to pass
  // this "owner" node explicitly because we didn't want to store it in
  // the interface members.

  // returns a link from 'this' to the interface memeber 'ifm' of the
  // enclosing PROTO definition node 'refnode' (corresponds to 'IS'
  // statement in PROTO body). This member function checks the value 
  // type of 'this' and 'ifm', and calls link_eventIn if 'ifm' is
  // and eventIn, link_exposedField if 'ifm' is an exposedField, etc...
  virtual interfaceMember* link(class SFNode* thisnode, SFNode* refnode, interfaceMember* refifm);

  // returns 'this' if Id matches and null if no match, used for
  // finding the interface member with given Id in a node.
  virtual interfaceMember *get(const char *Id);

  // - for links, replaces the referenced interface member by a reference
  // to the corresponding interface member of 'enclosing_proto'.
  // - for eventOuts and exposedFields, replaces routing table by new
  // routing table routing to the corresponding instances of named nodes
  // in 'enclosing_proto'.
  // - for fields with [S|M]FNode value: replaces [S|M}Fnode by a new
  // instance w.r.t. 'enclosing_proto' by calling the instantiate()
  // member function of these nodes.
  // In all cases, 'enclosing_proto' shall be a new instance of the 
  // innermost enclosing prototype definition node referenced before.
  virtual void instantiate(class SFNode* thisnode, class Proto* enclosing_proto =0);

  // eventIns/exposedFields.
  virtual void process(class SFNode *thisnode, const fieldValue& value);

  // fields/exposedFields: assigns initial value (while parsing a VRML file)
  virtual void initialize(class SFNode *thisnode, const fieldValue& value);

  // fields/exposedFields:
  // returns true if assignment was succesfull (i.e. value was valid etc...)
  // and false if no assignement was done (because value was invalid e.g.).
  virtual bool assign(class SFNode *thisnode, const fieldValue& value);

  // eventOuts:
  virtual void post(class SFNode *thisnode, const fieldValue& value);

  // eventOuts/exposedFields: adds a route from 'this' to the 
  // interface member 'dstifm' of the named node 'dstnode'.
  virtual void add_route(class SFNode *dstnode, class interfaceMember *dstifm);

  // eventOuts/exposedFields: propagates the current value of 'this'
  // to all eventIns/exposedFields in the routing table of 'this'.
  // (The routing table contains also the node to which the destination
  // eventIn/exposedField belongs.)
  virtual void dispatch(void) const;

  virtual ostream& print(ostream&);
  friend ostream& operator<<(ostream& s, interfaceMember& ifm) { return ifm.print(s); }
  friend ostream& operator<<(ostream& s, interfaceMember* ifm) { return ifm->print(s); }
};

#define is_field(member)	(dynamic_cast<field*>(member) != 0)
#define is_exposedField(member)	(dynamic_cast<exposedField*>(member) != 0)
#define is_eventIn(member)	(dynamic_cast<eventIn*>(member) != 0)
#define is_eventOut(member)	(dynamic_cast<eventOut*>(member) != 0)
#define is_link(member)         (dynamic_cast<link*>(member) != 0)
#define is_routable(member)     (dynamic_cast<routing_table*>(member) != 0)

/////////////////////////////////////////////////////////////////////////

class eventIn: public interfaceMember {
public:
  PoolDecl(eventIn, "eventIn")

protected:
  void (*do_process)(SFNode* dst, const fieldValue& value);

  inline void _init(void (*do_process)(SFNode*, const fieldValue&) =0)
    {
      eventIn::do_process = do_process;
    }

  inline void init(const char *Id, fieldType type, void (*do_process)(SFNode*, const fieldValue&) =0)
    {
      interfaceMember::init(Id, fieldValue(type, 0));
      _init(do_process);
    }

  void init(eventIn *ref);	// for creating links

  virtual interfaceMember* link_eventIn(class SFNode* thisnode, class SFNode* refnode, class eventIn* ifm);

  void process_forward(const fieldValue& value) const;
  void process_here(class SFNode* thisnode, const fieldValue& value);

public:
  inline eventIn(void) { _init(); }
  inline eventIn(const char *Id, fieldType type, void (*do_process)(SFNode*, const fieldValue&) =0) { init(Id, type, do_process); }

  virtual interfaceMember* clone(void);

  virtual void process(class SFNode *thisnode, const fieldValue& value);

  virtual ostream& print(ostream& s);
};

/////////////////////////////////////////////////////////////////////////

class eventOut: public interfaceMember, public routing_table {
public:
  PoolDecl(eventOut, "eventOut")

protected:
  // routing table for the eventOut: array of destination eventIns

  inline void _init(void)
    {
      // no additional data on top of interfaceMember and routine_table
    }

  inline void init(const char *Id, const fieldValue& value)
    {
      interfaceMember::init(Id, value);
      routing_table::init();
      _init();
    }

  void init(class eventOut *ref);

  virtual interfaceMember* link_eventOut(class SFNode* thisnode, SFNode* refnode, class eventOut* ifm);

public:
  inline eventOut(void) { _init(); }
  inline eventOut(const char *Id, const fieldValue& value) { init(Id, value); }

  virtual interfaceMember* clone(void);

  virtual void instantiate(class SFNode* thisnode, class Proto* master =0);
  virtual void post(class SFNode *thisnode, const fieldValue& value);
  virtual void dispatch(void) const;
  virtual void add_route(class SFNode *dstnode, class interfaceMember *dstifm);

  virtual ostream& print(ostream& s);
};

/////////////////////////////////////////////////////////////////////////

class field: public interfaceMember {
  friend class fieldLink;
  friend class exposedField_fieldLink;

protected:
  // The do_assign function, if non-nil:
  // . checks whether the value being passed is appropriate for
  // assignement to the field
  // . if so, assigns it to the node
  // . synchronizes redundant data derived from the field value
  // . sets node status bits indicating that the field value has changed
  // . sets node and interface member time stamp to the current
  // world time if succesful.
  // It returns 'true' if succesful and 'false' is no assignement has
  // been done. In the latter case, the fields value should not have changed
  // and an exposedField will not generate eventOuts for the assignement.
  bool (*do_assign)(SFNode *node, const fieldValue& value);

  // will prevent assignement loops in links (master -> link -> master -> ...)
  bool busy_forwarding;

  inline void _init(bool (*do_assign)(SFNode*, const fieldValue&) =0)
    {
      field::do_assign = do_assign;
      busy_forwarding = false;
    }

  inline void init(const char *Id, const fieldValue& value, bool (*do_assign)(SFNode*, const fieldValue&) =0)
    {
      interfaceMember::init(Id, value);
      _init(do_assign);
    }

  void init(field *ref);

  virtual interfaceMember* link_field(class SFNode* thisnode, SFNode* refnode, class field* ifm);
  void assign_forward(const fieldValue& value);
  bool assign_here(class SFNode* thisnode, const fieldValue& value);

public:
  PoolDecl(field, "field")

  inline field(void) { _init(); }
  inline field(const char *Id, const fieldValue& value, bool (*do_assign)(SFNode*, const fieldValue&) =0) { init(Id, value, do_assign); }

  virtual interfaceMember* clone(void);

  virtual void instantiate(class SFNode* thisnode, Proto *master =0);

  virtual bool assign(class SFNode *thisnode, const fieldValue& value);
  virtual void initialize(class SFNode *thisnode, const fieldValue& value);

  virtual ostream& print(ostream& s);
};

/////////////////////////////////////////////////////////////////////////

class exposedField: public field, public routing_table {
public:
  PoolDecl(exposedField, "exposedField")

protected:
  inline void _init(void)
    {
      // no additional data on top of field and routine_table
    }

  inline void init(const char *Id, const fieldValue& value, bool (*do_assign)(SFNode*, const fieldValue&) =0)
    {
      field::init(Id, value, do_assign);
      routing_table::init();
      _init();
    }

  void init(exposedField *ref);

  virtual interfaceMember* link_eventIn(class SFNode* thisnode, SFNode* refnode, class eventIn* ifm);
  virtual interfaceMember* link_exposedField(class SFNode* thisnode, SFNode* refnode, class exposedField* ifm);
  virtual interfaceMember* link_field(class SFNode* thisnode, SFNode* refnode, class field* ifm);
  virtual interfaceMember* link_eventOut(class SFNode* thisnode, SFNode* refnode, class eventOut* ifm);

  virtual bool assign_here(class SFNode *thisnode, const fieldValue& value);

public:
  inline exposedField(void) { _init(); }
  inline exposedField(const char *Id, const fieldValue& value, bool (*do_assign)(SFNode*, const fieldValue&) =0) { init(Id, value, do_assign); }

  virtual interfaceMember* clone(void);
  virtual interfaceMember* get(const char *Id);

  virtual void instantiate(class SFNode* thisnode, class Proto *master =0);

  virtual void process(class SFNode *thisnode, const fieldValue& value);
  virtual bool assign(class SFNode *thisnode, const fieldValue& value);
  virtual void post(class SFNode *thisnode, const fieldValue& value);

  virtual void dispatch(void) const;
  virtual void add_route(class SFNode *dstnode, class interfaceMember *dstifm);

  virtual ostream& print(ostream& s);
};

} // namespace xrml

#endif /*_NODEINTERFACE_H_*/
