/* world.C */

#include <string.h>

#include "xrmlP.H"
#include "renderer.H"
#include "file.H"
#include "ExternProto.H"
#include "EventManager.H"
#include "nodeCatalog.H"
#include "vrml2_utf8/exporter.H"
#include "vrml2_utf8/parser.H"

namespace xrml {

extern void rusage(char *filename, int linenr);

static renderer default_renderer;

PoolImpl(world)

world::world()
{
  file = 0;
  time = 0.;
  sceneGraph = 0;
  renderer = &default_renderer;
  stacks = 0;
  filename = (char*)0;
  strpool = 0;
  event_m = 0;
  inited = false;
}

void world::init(const double time)
{
  if (inited) return;

  world::time = time;
  if (!stacks) {
    stacks = new StackManager;
    stacks->init(this);
  }
  if (!event_m) {
    event_m = new EventManager;
    event_m->init(this);
  }
  sceneGraph = new MFNode;

  if (!builtin_nodes)
    builtin_nodes = new node_catalog();
  inited = true;
}

// parses the given VRML file and returns the constructed scene graph.
// The parseable file format is determined by the 'importer' object.
MFNode* world::parse(class file *input, const double start_time, class importer *importer)
{
  init(start_time);

  if (!file) file = input;
  if (!filename) {
    char *slash = strrchr(file->url, '/');
    filename = strdup(slash ? slash+1 : file->url);
  }

  class world* previous_world = make_current();
  char *prev_filename = current_filename;
  int prev_linenr = current_linenr;
  current_filename = filename;
  current_linenr = 0;

  // Create a parser if none was given and parse the file.
  // The parser fills in sceneGraph, namedNodes and protos.
  bool succes = false;
  if (!importer) {
    vrml2_utf8::parser *parser = new vrml2_utf8::parser;
    succes = parser->parse(this, &strpool);
    delete parser;
  } else
    succes = importer->parse(this, &strpool);

  // restore previously saved
  if (previous_world) previous_world->make_current();  
  current_linenr = prev_linenr;
  current_filename = prev_filename;

  return succes ? sceneGraph : (MFNode*)0;
}

MFNode* world::parse(const char *filename, const double start_time, class importer *importer)
{
  init(start_time);
  
  //  open the file
  file = new class file;
  if (filename) {
    file->openFromURL(filename, (char*)0);
    char *slash = strrchr(filename, '/');
    world::filename = strdup(slash ? slash+1 : filename);
  } else {
    file->open("<stdin>", "r", stdin, true);
    world::filename = strdup("<stdin>");
  }
  if (!file->fp) {
    if (world::filename) delete world::filename;
    world::filename = (char*)0;
    return 0;
  }

  // TODO: should inspect file extension and/or first lines 
  // and call an appropriate importer accordingly.

  // parse it
  parse(file, start_time, importer);

  // close the file
  if (filename)
    file->close();

  return sceneGraph;
}

void world::newframe(void)
{
  event_m->dispatch_all();	// dispatch all queued events
}

void world::set_time(const double newtime)
{
  if (newtime < time)
    Warning(NULL, "time of world '%s' is set before current time (new time = %g, current time = %g",
	  filename, newtime, time);
  time = newtime;
}

class renderer* world::set_renderer(class renderer* newrenderer)
{
  class renderer* oldrenderer = renderer;
  renderer = newrenderer;
  return oldrenderer;
}

void world::render(void)
{
  renderer->begin_frame(this);
  if (sceneGraph) sceneGraph->render();
  renderer->end_frame(this);
}

void world::postEvent(const class SFNode *srcnode, const class interfaceMember* event) const
{
  event_m->post(srcnode, event);
}

ostream& operator<<(ostream& s, const world* w)
{
  if (w && w->sceneGraph) {
    s << "World at time " << w->time << ":\n";
    return s << w->sceneGraph;
  } else if (w) {
    return s << "empty world\n";
  } else
    return s << "(null)\n";
}

class file *world::openFileFromURLs(const MFString& url, const char* base_url) const
{
  class file *newfile = new class file;
  for (int i=0; i<url.size; i++) {
    newfile->openFromURL((char*)url[i], base_url ? base_url : file->url);
    if (newfile->fp) // open succeeded.
      return newfile;
  }
  return 0;
}

int world::addNamedNode(const char *name, class SFNode* node)
{
  node->nameId = strdup(name);
  return node->nameIndex = namedNodes.add((char*)name, node);
}

int world::addProto(class Proto* proto)
{
  return protos.add((char*)proto->typeId, proto);
}

class SFNode* world::lookupNamedNode(const char* nameId) const
{
  int i = namedNodes((char*)nameId);
  return (i>=0) ? namedNodes[i] : (SFNode*)0;
}

class Proto* world::lookupProto(const char* typeId) const
{
  int i = protos((char*)typeId);
  return (i>=0) ? protos[i] : (Proto*)0;
}

class world* world::make_current(void)
{
  class world *prev_world = current_world;
  current_world = this;
  return prev_world;
}

void world::save(class file* fh, class exporter* exporter)
{
  if (!exporter) {
    exporter = new vrml2_utf8::exporter;
    exporter->init(fh);
    exporter->save(this);
    delete exporter;
  } else {
    exporter->init(fh);
    exporter->save(this);
  }
}

void world::save(const char* filename, class exporter* exporter)
{
  // open the file for writing into
  class file fh;
  if (filename)
    fh.open(filename, "w");
  else
    fh.open("<stdout>", "w", stdout, true);
  if (!fh.fp)
    return;

  // TODO: should inspect file extension in order to call
  // an appropriate exporter accordingly

  // export
  save(&fh, exporter);

  // close the file
  fh.close();
}

}  // namespace xrml
