// exposedField.C:

#include <string.h>
#include "nodeInterface.H"
#include "world.H"

namespace xrml {

void exposedField::init(exposedField *ref)
{
  init((char *)ref->Id, (fieldValue&)ref->value, ref->do_assign);
  timestamp = ref->timestamp;
}

interfaceMember* exposedField::clone(void)
{
  exposedField *e = new exposedField(*this);
  routing_table::clone(e);
  return e;
}

// An exposedField may be referred to by its name, e.g. 'translation', but
// also by its name with prefix set_ or suffix _changed, e.g.
// 'set_translation' or' translation_changed'.
interfaceMember* exposedField::get(const char *Id) 
{
  int n=(int)strlen(this->Id);
  if ((memcmp(Id, "set_", 4) == 0 && strcmp(this->Id, Id+4) == 0) ||
      (memcmp(this->Id, Id, n) == 0 &&
       (Id[n] == '\0' || strncmp(Id+n, "_changed", 8) == 0)))
    return this;
  else
    return 0;
}

void exposedField::instantiate(SFNode* thisnode, Proto *master)
{
  field::instantiate(thisnode, master);
  routing_table::instantiate(master);
}

inline bool exposedField::assign_here(SFNode *thisnode, const fieldValue& value)
{
  double time_before = timestamp;

  bool succes = field::assign_here(thisnode, value);	// effectively assign

  if (succes && time_before < timestamp)
    // route only once at each instance and if assignement is succesful
    thisnode->world->postEvent(thisnode, this);

  return succes;
}

bool exposedField::assign(SFNode *thisnode, const fieldValue& value)
{
  if (timestamp > thisnode->world->time || busy_forwarding)	// don't assign old values
    return false;

  bool succes = assign_here(thisnode, value);
  assign_forward(value);
  return succes;
}

void exposedField::process(SFNode *thisnode, const fieldValue& value)
{
  assign(thisnode, value);
}

void exposedField::post(SFNode *thisnode, const fieldValue& value)
{
  assign(thisnode, value);
}

void exposedField::dispatch(void) const
{
  routing_table::dispatch(value);
}

void exposedField::add_route(class SFNode *dstnode, class interfaceMember *dstifm)
{
  routing_table::add(dstnode, dstifm);
}

ostream& exposedField::print(ostream& s) 
{
  print_Id_value(s << "exposedField");
  routing_table::print(s);
  return s;
}


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

// common stuff for all exposedField_xxxx links
class exposedFieldLink: public exposedField, public link {
protected:
  inline void init(SFNode *thisnode, exposedField *thisifm, SFNode *srcnode, interfaceMember *srcifm)
  {
    exposedField::init(thisifm);
    // Indeed: thisifm and not 'this': 'this' is not a member yet of the interface 
    // of 'thisnode'.
    link::init(srcnode, srcifm, thisnode, thisifm);
  }

public:
  PoolDecl(exposedFieldLink, "exposedFieldLink")

  exposedFieldLink(void) {};

  virtual void instantiate(SFNode* thisnode, Proto *master =0)
  {
    exposedField::instantiate(thisnode, master);
    link::instantiate(master, thisnode, this);
  }

  virtual ostream& print(ostream& s)
  {
    exposedField::print(s);
    link::print(s);
    return s
      << ' ' 
      << ref()->value 
      << (ref()->timestamp > timestamp ? " (to be updated)" : " (up to date)");
  }
};

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

class exposedField_eventInLink: public exposedFieldLink {
public:
  PoolDecl(exposedField_eventInLink, "exposedField_eventInLink")

  exposedField_eventInLink(void) {}

  inline exposedField_eventInLink(SFNode* thisnode, exposedField *dest, SFNode *srcnode, eventIn *srcifm) 
  { 
    exposedFieldLink::init(thisnode, dest, srcnode, srcifm);
  }

  virtual interfaceMember* clone(void)
  {
    exposedField_eventInLink *e = new exposedField_eventInLink(*this);
    routing_table::clone(e);
    return e;
  }
};

interfaceMember* exposedField::link_eventIn(SFNode* thisnode, SFNode* node, eventIn* ifm) 
{
  return new exposedField_eventInLink(thisnode, this, node, ifm);
}

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

class exposedField_fieldLink: public exposedFieldLink {
public:
  PoolDecl(exposedField_fieldLink, "exposedField_fieldLink")

  exposedField_fieldLink(void) {};

  inline exposedField_fieldLink(SFNode* thisnode, exposedField *dest, SFNode *srcnode, field *srcifm)
  {
    exposedFieldLink::init(thisnode, dest, srcnode, srcifm);
    assign_here(thisnode, ref()->value);
  }

  virtual void instantiate(SFNode* thisnode, Proto *master =0)
  {
    exposedFieldLink::instantiate(thisnode, master);
    assign_here(thisnode, ref()->value);
  }

  virtual bool assign(SFNode *thisnode, const fieldValue& value)
  {
    // Forward assignement to linked PROTO interface field, which will
    // assign to all linked fields in PROTO implementation.
    // i.o.w.: assignement to any linked field in the PROTO implementation
    // is exactly the same as assignement to the PROTO interface field.
    return (!(dynamic_cast<field*>(ref()))->busy_forwarding) ?
      ref()->assign(refnode, value) : exposedField::assign(thisnode, value);
  }

  virtual interfaceMember* clone(void)
  {
    exposedField_fieldLink *e = new exposedField_fieldLink(*this);
    routing_table::clone(e);
    return e;
  }
};

interfaceMember* exposedField::link_field(SFNode* thisnode, SFNode* node, field* ifm) 
{
  return new exposedField_fieldLink(thisnode, this, node, ifm);
}

interfaceMember* exposedField::link_exposedField(SFNode* thisnode, SFNode* node, exposedField* ifm)
{
  return new exposedField_fieldLink(thisnode, this, node, ifm);
}

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

class exposedField_eventOutLink: public exposedFieldLink {
public:
  PoolDecl(exposedField_eventOutLink, "exposedField_eventOutLink")

  exposedField_eventOutLink(void) {};

  inline exposedField_eventOutLink(SFNode* thisnode, exposedField *dest, SFNode *srcnode, eventOut *srcifm)
  {
    exposedFieldLink::init(thisnode, dest, srcnode, srcifm); 
  }

  virtual void instantiate(SFNode* thisnode, Proto *master =0)
  {
    exposedFieldLink::instantiate(thisnode, master);
  }

  virtual interfaceMember* clone(void)
  {
    exposedField_eventOutLink *e = new exposedField_eventOutLink(*this);
    routing_table::clone(e);
    return e;
  }

  virtual bool assign(class SFNode *thisnode, const fieldValue& value)
  {
    // assign to 'this' and post to linked eventOut
    bool succes = exposedField::assign(thisnode, value); 
    ref()->post(refnode, value);
    return succes;
  }

  virtual ostream& print(ostream& s)
  {
    exposedField::print(s);
    link::print(s);
    return s
      << ' ' 
      << ref()->value;
  }
};

interfaceMember* exposedField::link_eventOut(SFNode* thisnode, SFNode* node, eventOut* ifm)
{
  return new exposedField_eventOutLink(thisnode, this, node, ifm);
}

PoolImpl(exposedField)
PoolImpl(exposedFieldLink)
PoolImpl(exposedField_eventInLink)
PoolImpl(exposedField_fieldLink)
PoolImpl(exposedField_eventOutLink)

} // namespace xrml
