/* ExternProto.C */

#include "ExternProto.H"
#include "world.H"
#include "file.H"

namespace xrml {

PoolImpl(ExternProto)

bool IsUnresolvedExternProto(SFNode *node) 
{
  ExternProto *extproto = dynamic_cast<ExternProto*>(node);
  return (extproto && !extproto->sceneGraph);
}

bool IsExternProto(SFNode *node)
{
  return (dynamic_cast<ExternProto*>(node) != (ExternProto*)0);
}

ExternProto::ExternProto(SFNode *master)
{
  SFNode::construct(master->typeId);
  Proto_construct(master, (MFNode*)0, namedNodeTable(0), protoTable(0));
  url = MFString(0);
}

void ExternProto::clone_here(const ExternProto& src)
{
  Proto::clone(src);	// clones the interface, duplicating field values
  url = src.url;
}

void ExternProto::destruct(void)
{
  master->destruct();
}

ExternProto::ExternProto(const ExternProto& src)
{
  clone_here(src);
}

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

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

void ExternProto::fetch(const MFString *url)
{
  ExternProto::url = *url;
  // TODO: start fetching the urls already in the background
}

static bool check_interfaces(const SFNode *oldmaster, const SFNode *newmaster)
{
  // TODO
  return true;
}

static Proto* lookup_toplevel_local_proto(const world* world, const char *typeId)
{
  for (int i=0; i<world->protos.size; i++) {
    if (IsExternProto(world->protos[i]))
      continue;		// skip extern protos

    if (!typeId || strcmp(typeId, world->protos[i]->typeId)==0)
      return world->protos[i];
  }
  return (Proto *)0;
}

void ExternProto::resolve(void)
{
  static bool dont_resolve = false;
  static char* base_url = (char*)0;

  if (sceneGraph || dont_resolve) {	// already resolved or unwanted recursion
    return;
  }

  Info(NULL, "Fetching implementation for %s EXTERNPROTO", name());
  Proto *newmaster = 0;	// PROTO containing the true interface
                        // and scene graph

  dont_resolve = true;	// avoid automatic recursive EXTERNPROTO resolution
  file *ep_file = new file;
  for (int i=0; i<url.size; i++) {
    // TODO: fetch the implementation in the background in FetchExternProto
    ep_file->openFromURL(url[i], base_url ? base_url : world->file->url);
    if (!ep_file->fp)
      continue;

    // TODO: caching: check URL and return previously parsed world if present
    // TODO: check for recursion
    class world *ep_world = new class world;
    ep_world->parse(ep_file, world->time);
    ep_file->close();
    // TODO: store ep_world in cache somewhere

    char *nodename = ep_file->target;
    newmaster = lookup_toplevel_local_proto(ep_world, nodename);
    if (!newmaster) {
      Warning(NULL, "File '%s' does not contain a PROTO node%s%s",
	    ep_world->file->url,
	    nodename ? " named " : "",
	    nodename ? nodename : "");
      continue;
    }
    break;
  }
  dont_resolve = false;	// allow recursive resolution (for newmaster only)

  if (newmaster) {
    check_interfaces(master, newmaster);

    // TODO:    delete master;		// destroys old interface
    master = this;

    clone_interface(*newmaster);	// copies interface of newmaster, duplicating fieldValues
    instantiate_interface(0);	// instantiates new copies of [M|S]FNode interface field
                                // values. Note that newmaster is a top level PROTO node, 
                                // so there is no enclosing proto to take into account.
    char *old_base_url = base_url;
    base_url = world->file->url;
    instantiate_implementation(newmaster);	// copies scene graph from newmaster
    base_url = old_base_url;
  } else {
    Error(NULL, "Can't find implementation of %s EXTERNPROTO", name());
    sceneGraph = new MFNode(0);		// empty scene graph
  }
}

SFNode *ExternProto::instantiate(Proto *enclosing_proto)
{
  if (!sceneGraph) resolve();

  // can't use Proto::instantiate here because the result shall be
  // an EXTERNPROTO node and not a PROTO node.
  ExternProto *instance = new ExternProto(*this);
  instance->instantiate_interface(enclosing_proto);
  instance->instantiate_implementation(this);

  return instance;
}

} // namespace xrml

