// vrml2_utf8_exporter.C

#include <stdlib.h>
#include "exporter.H"
#include "nodeInterface.H"
#include "route.H"
#include "List.H"

namespace vrml2_utf8 {

bool exporter::saved_already(class SFNode* node)
{
  return (id == node->export_data.i);
}

void exporter::mark_saved(class SFNode* node)
{
  node->export_data.i = id;
}

char* exporter::unique_nameId(class SFNode* node)
{
  static char* buf = 0; static int bufsize = 0;
  int reqsize = (int)strlen(node->nameId) + 12;
  if (bufsize < reqsize) {
    if (buf) delete buf;
    buf = new char[reqsize];
    bufsize = reqsize;
  }
  sprintf(buf, "(%d)%s", node->nameIndex+1, node->nameId);
  return buf;
}

void exporter::save(const SFBool& val)
{
  fh->printf("%s", (bool)val ? "TRUE" : "FALSE");
}

void exporter::save(const SFString& val)
{
  fh->printf("\"%s\"", (char*)val);
}

void exporter::save(const SFInt32& val)
{
  fh->printf("%d", (int)val);
}

void exporter::save(const SFFloat& val)
{
  fh->printf("%g", (float)val);
}

void exporter::save(const SFTime& val)
{
  fh->printf("%lg", (double)val);
}

void exporter::save(const SFVec2f& val)
{
  fh->printf("%g %g", val.s, val.t);
}

void exporter::save(const SFVec3f& val)
{
  fh->printf("%g %g %g", val.x, val.y, val.z);
}

void exporter::save(const SFColor& val)
{
  fh->printf("%g %g %g", val.r, val.g, val.b);
}

void exporter::save(const SFRotation& val)
{
  fh->printf("%g %g %g %g", val.x, val.y, val.z, val.radians);
}

void exporter::save(const SFImage& val)
{
  fh->printf("%d %d %d ", val.width, val.height, val.num_components);
  for (int i=0; i< val.width * val.height; i++)
    fh->printf("0x%x ", val.pixels[i]);
}

#define array_save(arr) {              \
  fh->printf("[");                     \
  for (int i=0; i<(arr).size; i++) {   \
    fh->printf(i>0 ? ", " : " ");      \
    save((arr)[i]);                    \
  }                                    \
  fh->printf(" ]");                    \
}

void exporter::save(const fieldValue& fvalue)
{
  assert(fvalue.value.any);
  switch (fvalue.type) {
  case tSFBool:		save(*fvalue.value.sfbool); break;
  case tSFString:	save(*fvalue.value.sfstring); break;
  case tSFInt32:	save(*fvalue.value.sfint32); break;
  case tSFFloat:	save(*fvalue.value.sffloat); break;
  case tSFTime:		save(*fvalue.value.sftime); break;
  case tSFVec2f:	save(*fvalue.value.sfvec2f); break;
  case tSFVec3f:	save(*fvalue.value.sfvec3f); break;
  case tSFColor:	save(*fvalue.value.sfcolor); break;
  case tSFRotation:	save(*fvalue.value.sfrotation); break;
  case tSFImage:	save(*fvalue.value.sfimage); break;
  case tSFNode:		save(*fvalue.value.sfnode); break;
  case tMFBool:		array_save(*fvalue.value.mfbool); break;
  case tMFString:	array_save(*fvalue.value.mfstring); break;
  case tMFInt32:	array_save(*fvalue.value.mfint32); break;
  case tMFFloat:	array_save(*fvalue.value.mffloat); break;
  case tMFTime:		array_save(*fvalue.value.mftime); break;
  case tMFVec2f:	array_save(*fvalue.value.mfvec2f); break;
  case tMFVec3f:	array_save(*fvalue.value.mfvec3f); break;
  case tMFColor:	array_save(*fvalue.value.mfcolor); break;
  case tMFRotation:	array_save(*fvalue.value.mfrotation); break;
  case tMFImage:	array_save(*fvalue.value.mfimage); break;
  case tMFNode:		array_save(*fvalue.value.mfnode); break;
  default:		Fatal(-1, "vrml2_utf8::exporter(fieldValue)", "invalid type '%d'", fvalue.type);
  }
}

void exporter::save_link(class link* link)
{
  fh->printf("IS %s", link->ref()->Id);
}

void exporter::save_value(class field* field)
{
  save(field->value);
}

bool exporter::save_value_or_link(class interfaceMember* ifm)
{
  if (is_link(ifm) || is_field(ifm)) {
    fh->newline();
    fh->printf("%s \t", ifm->Id);
    if (is_link(ifm))
      save_link(dynamic_cast<link*>(ifm));
    else if (is_field(ifm))
      save_value(dynamic_cast<field*>(ifm));
    return true;
  }
  return false;
}

void exporter::save(SFNode* node)
{
  if (!node) {
    fh->printf("NULL");
    return;
  }

  if (node->nameId) {
    if (saved_already(node)) {
      fh->printf("USE %s", unique_nameId(node));
      return;
    } else
      fh->printf("DEF %s ", unique_nameId(node));
  }
  fh->printf("%s { ", node->typeId);

  // save field values and links
  bool something_written;
  fh->indent();
  something_written = false;
  for (int i=0; i<node->nrInterfaceMembers; i++) {
    if (node->interface[i]->timestamp != CONSTRUCT_TIME || is_link(node->interface[i]))
      something_written |= save_value_or_link(node->interface[i]);
  }
  fh->outdent();
  if (something_written) fh->newline();

  fh->printf("}");
  mark_saved(node);
}

void exporter::save_memberKind(class interfaceMember* ifm)
{
  if      (is_exposedField(ifm)) fh->printf("exposedField");
  else if (is_field(ifm))        fh->printf("field");
  else if (is_eventIn(ifm))      fh->printf("eventIn");
  else if (is_eventOut(ifm))     fh->printf("eventOut");
  else Error("vrml2_utf8::exporter::save_memberKind", "Unknown interface member kind!!");
}

void exporter::save_memberType(class interfaceMember* ifm)
{
  fh->printf("%s", fieldTypeName(fieldType(ifm->value)));
}

void exporter::save_proto_interfaceMember(class interfaceMember* ifm)
{
  save_memberKind(ifm);
  fh->printf(" ");
  save_memberType(ifm);
  fh->printf(" ");
  fh->printf("%s \t", ifm->Id);
  if (is_field(ifm))
    save(ifm->value);
}

void exporter::save_proto_interface(class Proto* proto)
{
  for (int i=0; i<proto->nrInterfaceMembers; i++) {
    fh->newline();
    save_proto_interfaceMember(proto->interface[i]);
  }
}

void exporter::save_proto_implementation(class Proto* proto)
{
  save_context(proto->sceneGraph, proto->protos);
}

void exporter::save_protodef(class Proto* proto)
{
  fh->printf("PROTO %s [", proto->typeId);
  fh->indent();
  save_proto_interface(proto);
  fh->outdent();
  fh->newline();
  fh->printf("] {");
  fh->indent();
  save_proto_implementation(proto);
  fh->outdent();
  fh->newline();
  fh->printf("}");
}

void exporter::save_route(class SFNode* srcNode, const char* srcId, class route* route)
{
  fh->newline();
  // unique_name() returns pointer to static buffer!! */
  fh->printf("ROUTE %s.%s ", unique_nameId(srcNode), srcId);
  fh->printf("TO %s.%s", unique_nameId(route->refnode), route->ref()->Id);
}

void exporter::save_routes(class SFNode* srcNode, const char* srcId, class routing_table* rtable)
{
  if (!rtable->routes)
    return;

  for (class list<route> *r=rtable->routes; r; r=r->next) {
    class route* route = (class route*)(*r);
    save_route(srcNode, srcId, route);
  }
}

void exporter::save_children_routes(class  field* field)
{
  switch (field->value.type) {
  case tSFNode: save_routes(*field->value.value.sfnode); break;
  case tMFNode: save_routes(*field->value.value.mfnode); break;
  default: /* skip */ break;
  }
}

void exporter::save_routes(class SFNode* node)
{
  if (!node || !node->nameId || saved_already(node))
    return;   // not a named node -> no routes, or already saved

  for (int i=0; i<node->nrInterfaceMembers; i++) {
    if (is_routable(node->interface[i]))
      save_routes(node, node->interface[i]->Id, dynamic_cast<routing_table*>(node->interface[i]));
    else if (is_field(node->interface[i]))
      save_children_routes(dynamic_cast<field*>(node->interface[i]));
  }

  mark_saved(node);
}

void exporter::save_routes(const MFNode& nodes)
{
  for (int i=0; i<nodes.size; i++)
    save_routes(nodes[i]);
}

void exporter::save_context(const MFNode* sceneGraph, const protoTable& protos)
{
  int i;

  for (i=0; i<protos.size; i++) {
    fh->newline();
    save_protodef(protos[i]);
  }

  if (sceneGraph) {
    id = (int)lrand48();
    for (i=0; i<sceneGraph->size; i++) {
      fh->newline();
      save((*sceneGraph)[i]);
    }

    // TODO: iterate over named nodes table here, instead of full
    // second sweep through scene graph.
    id = (int)lrand48();  // instantly marks all nodes as not previouisly written
    save_routes(*sceneGraph);
  }
}

void exporter::save(class world* world)
{
  fh->printf("#VRML V2.0 utf8\n");
  save_context(world->sceneGraph, world->protos);
  fh->printf("\n");
}

}  // namespace vrml2_utf8
