/* SFNode.C */

#include <string.h>
#include <strstream.h>
#include "xrmlP.H"
#include "file.H"

namespace xrml {

int SFNode::major_version(void) { return -1; }
int SFNode::minor_version(void) { return -1; }
bool SFNode::is_leaf_class(void) { return false; }

bool IsSFNode(SFNode*)
{
  return true;	// all SFNodes are SFNodes
}

void clear(client_data& data)
{
  data.i = 0; data.p = 0;
}

void SFNode::construct(const char *id, int nrInterfaceMembers, interfaceMember** interface)
{
  SFNode::nrInterfaceMembers = 0;
  SFNode::interface = 0;

  typeId = id;
  nameId = 0;
  nameIndex = -1;

  filename = xrml::current_filename;
  line = xrml::current_linenr;
  world = xrml::current_world;

  timestamp = world ? world->time : CONSTRUCT_TIME;
  clear(data); clear(export_data); clear(import_data);

  if (nrInterfaceMembers > 0) {
    SFNode::nrInterfaceMembers = nrInterfaceMembers;
    SFNode::interface = new interfaceMember* [nrInterfaceMembers];
    for (int i=0; i<nrInterfaceMembers; i++) {
      SFNode::interface[i] = interface[i];
    }
  }

  refCount = 0;
  status = 0;	// default values are considered "not updated"
}

void SFNode::destruct(void)
{
  if (interface) {
    for (int i=0; i<nrInterfaceMembers; i++) delete interface[i];
    delete[] interface;
  }
}

SFNode::SFNode(const char *id, int nrInterfaceMembers, interfaceMember** interface)
{
  construct(id, nrInterfaceMembers, interface);
}

void SFNode::clone_interface(const SFNode& src)
{
  nrInterfaceMembers = src.nrInterfaceMembers;
  interface = new interfaceMember* [src.nrInterfaceMembers];
  for (int i=0; i<src.nrInterfaceMembers; i++) {
    interface[i] = src.interface[i]->clone();
    interface[i]->forward_links = 0;	// src links are invalid.
    // routes and backward links are also invalid, but they contain
    // sufficient information for regenerating them efficiently using
    // instantiate() functions. Unlike routes and backward links, forward
    // links are redundant. They are regenerated by the init() and
    // instantiate() member functions of the node interface 'link' class.
  }
}

void SFNode::clone(const SFNode& src)
{
  construct(src.typeId);
  clone_interface(src);
}

SFNode::SFNode(const SFNode& src)
{
  clone(src);
}

SFNode& SFNode::operator=(const SFNode& src)
{
  if (this != &src) {
    destruct();
    clone(src);
  }
  return *this;
}

SFNode::~SFNode()
{
  destruct();
}

void SFNode::instantiate_interface(Proto *enclosing_proto)
{
  for (int i=0; i<nrInterfaceMembers; i++)
    interface[i]->instantiate(this, enclosing_proto);
}

SFNode* SFNode::instantiate(Proto *enclosing_proto)
{
  SFNode *instance = new SFNode(*this);
  instance->instantiate_interface(enclosing_proto);
  return instance;
}

#define AVOID_STREAMS	// unfortunately, the ostrstream stuff
                        // below crashes on certain Linux systems

#ifdef AVOID_STREAMS
#include <stdio.h>
#endif

#define MAXSFNODENAMELEN 1000
char* SFNode::name(char *buf) const
{
  if (!buf) {
    static char sbuf[MAXSFNODENAMELEN];
    buf = sbuf;
  }

#ifdef AVOID_STREAMS
  char *p=buf; int n;
#define checklen(n)				\
  if (p+(n)+1 >= buf+MAXSFNODENAMELEN) { 	\
    Warning("SFNode::name", "buffer overflow, recompile SFNode.C with larger value of MAXSFNODENAMELEN (currently %d)", MAXSFNODENAMELEN); 					\
    return buf; 				\
  }

  checklen(strlen(typeId));
  sprintf(p, "%s%n", typeId, &n); p += n;
  if (nameId) {
    checklen(strlen(nameId)+10);
    sprintf(p, " %s(%d)%n", nameId, nameIndex, &n); p += n;
  }
  if (filename && line>0) {
    checklen(strlen(filename)+10);
    sprintf(p, "@%s:%d%n", filename, line, &n); p += n;
  }
#else
  /* neater, but crashes on Linux and lacks length checking */
  ostrstream ost(buf,MAXSFNODENAMELEN);

  ost << typeId;
  if (nameId) 
    ost << ' ' << nameId << '(' << nameIndex << ')';
  if (filename && line>0)
    ost << '@' << filename << ':' << line;
  ost << '\0';
#endif

  return buf;
}

interfaceMember* SFNode::get(const char *Id)
{
  interfaceMember *ifm = 0;
  for (int i=0; i<nrInterfaceMembers; i++)
    if ((ifm = interface[i]->get(Id)) != 0)
      break;
  return (interfaceMember*)ifm;
}

ostream& SFNode::print(ostream& s)
{
  static int indent_level = 0;

  s << name() 
    //    << "(addr " << (void*)this << ") " 
    << "{ ";
  if (nrInterfaceMembers>0) {
    s << "\n";
    indent_level++;
    for (int i=0; i<nrInterfaceMembers; i++) {
      s.width(2*indent_level);
      s << " ";
      s << interface[i] << '\n';
    }
    indent_level--;
    if (indent_level > 0) {
      s.width(2*indent_level);
      s << " ";
    }
  }
  return s << "}";
}

void SFNode::render(void)
{
  Warning("SFNode::render", "can't yet render %s nodes", typeId);
}

void SFNode::stub(const char *membername) const
{
  Error(NULL, "%s stub called for node %s", membername, name());
}

} // namespace xrml
