/* interfaceDeclaration.C */

#include "vrml.H"
#include "error.H"

/* ********************* field/event redirections ********************** */

// This struct stores what interface field/exposedField/eventIn/eventOut of what
// node a PROTO scenegraph node interface field/exposedField/.... is a synonym for
// as specified with IS statement in VRML input file.
class redirection {
  SFNode *node;
  interfaceDeclaration *decl;	// member of the interface of the node above

public:
  inline redirection(SFNode *n, interfaceDeclaration *d) { node=n; decl=d; }
  inline operator interfaceDeclaration*() { return decl; }

  // resolves a redirection
  void set_node(SFNode *n)
  {
    int idx = decl - node->interface;
    node = n;
    decl = &(node->interface[idx]);
  }

  friend ostream& operator<<(ostream& s, redirection& v) 
    { 
      return s << " -> " << v.node->name() << " " << v.decl->Id;
    }
};

char* interfaceKindName(const interfaceKind& kind)
{
  return (kind == kdummy ? "dummy" :
	  (kind == keventIn ? "eventIn" :
	   (kind == kexposedField ? "exposedField" :
	    (kind == kfield? "field" :
	     (kind == keventOut ? "eventOut" : "invalid")))));
}

void interfaceDeclaration::construct(interfaceKind k, fieldType t, char *id, fieldValue val, class redirection *r)
{
  kind = k; type = t; Id = id; value = val;
  if (type != fieldType(value))
    Fatal(-1, "interfaceDeclaration::construct", "value type '%s' doesn't match %s type '%s'",
	  fieldTypeName(fieldType(value)), interfaceKindName(kind), fieldTypeName(type));
  checkval = 0;
  clock = -1;
  ref = r ? new redirection(*r) : 0;

  nodeClassId = 0;
  nodeClass = 0;
}

void interfaceDeclaration::destruct(void)
{
  if (ref) delete ref;
}

// value will be new fieldValue object of given type
interfaceDeclaration::interfaceDeclaration(interfaceKind k, fieldType t, char* id)
{
  construct(k, t, id, fieldValue(t), 0);
}

interfaceDeclaration::interfaceDeclaration(interfaceKind k, fieldType t, char* id, fieldValue val)
{
  construct(k, t, id, val, 0);    
}

interfaceDeclaration::interfaceDeclaration(const interfaceDeclaration& src)
{
  construct(src.kind, src.type, src.Id, src.value, src.ref);
  checkval = src.checkval;
  nodeClassId = src.nodeClassId;
  nodeClass = src.nodeClass;
}

interfaceDeclaration& interfaceDeclaration::operator=(const interfaceDeclaration& src)
{
  if (this != &src) {
    destruct();
    construct(src.kind, src.type, src.Id, src.value, src.ref);
    checkval = src.checkval;
    nodeClassId = src.nodeClassId;
    nodeClass = src.nodeClass;
  }
  return *this;
}

interfaceDeclaration::~interfaceDeclaration(void)
{
  destruct();
}

ostream& operator<<(ostream& s, interfaceDeclaration& v)
{
  s << v.kind << '\t' << v.type << '\t' << v.Id << '\t';

  if (v.kind==kfield || v.kind==kexposedField)
    s << v.value;

  if (v.ref) {
    s << *v.ref
      << " clock=" << v.clock
      << ", ref clock=" << ((interfaceDeclaration *)*v.ref)->clock;

    if (v.clock != ((interfaceDeclaration *)*v.ref)->clock)
      s << " (to be updated)";
  }

  return s << '\n';
}

void interfaceDeclaration::redirect(SFNode *node, interfaceDeclaration *decl)
{
  if (ref) 
    ref->set_node(node);
  else
    ref = new redirection(node, decl);

  decl = (interfaceDeclaration *)(*ref);
  value.assign_or_duplicate_if_nil(decl->value);
  clock = decl->clock;
}

void interfaceDeclaration::initialize(fieldValue& val)
{
  value.assign_or_duplicate_if_nil(val);
  clock = 0;

  if (checkval && !checkval(value))
    return;	// value rejected
}

void interfaceDeclaration::instantiate(Proto *master)
{
  switch (kind) {
  case kfield: case kexposedField:
    if (ref)
      redirect(master);
    else if ((void*)value!=0) 		// will be nil for unresolved EXTERNPROTO's
      value.instantiate(master);
    break;

  default:
    break;
  }
}

void interfaceDeclaration::assign(fieldValue& val)
{
  value.assign(val);
  clock++;

  if (checkval && !checkval(value))
    return; 	// value rejected

  if (ref)
    ((interfaceDeclaration *)ref)->assign(val);
}

void interfaceDeclaration::do_synchronize(void)
{
  interfaceDeclaration *refdecl = (interfaceDeclaration *)*ref;
  if (refdecl->clock != clock) {
    value.assign(refdecl->value);
    clock = refdecl->clock;
  }
}

