// vrmlconv: convert various 3D file formats to, from
// and across VRML'97
//
// Philippe Bekaert - February 22, 2001
// Philippe.Bekaert@mpi-sb.mpg.de

#include <stdlib.h>
#include <stdio.h>
#include "world.H"
#include "vrml2_utf8/parser.H"
#include "vrml2_utf8/exporter.H"
#ifdef PLY
#include "ply/importer.H"
#endif
#ifdef MGF
#include "mgf/exporter.H"
#endif
#ifdef NFF
#include "nff/exporter.H"
#endif
#ifdef CSO
#include "cso/importer.H"
#include "cso/exporter.H"
#endif
#include "error.H"

#include "options.h"

static char* progname =0;

typedef enum IN_FILTER { IN_VRML, IN_PLY, IN_CSO } IN_FILTER;
static ENUMDESC inFiltVals[] = {
  { IN_VRML, "vrml", 4 },
  { IN_PLY,  "ply" , 3 },
  { IN_CSO, "so", 3 },
  { 0, NULL, 0 }     // sentinel
};
MakeEnumOptTypeStruct(inFiltTypeStruct, inFiltVals);
#define TInFilt (&inFiltTypeStruct)

typedef enum OUT_FILTER { OUT_VRML, OUT_MGF, OUT_NFF, OUT_CSO } OUT_FILTER;
static ENUMDESC outFiltVals[] = {
  { OUT_VRML, "vrml", 4 },
  { OUT_MGF,  "mgf",  3 },
  { OUT_NFF,  "nff",  3 },
  { OUT_CSO, "cxx", 3 },
  { 0, NULL, 0 }     // sentinel
};
MakeEnumOptTypeStruct(outFiltTypeStruct, outFiltVals);
#define TOutFilt (&outFiltTypeStruct)

static xrml::importer* the_importer = 0;
static xrml::exporter* the_exporter = 0;

static void no_support(char *what)
{
  xrml::Fatal(-1, NULL, "%s has been compiled without %s support", progname, what);
}

static void SetImporter(void *value)
{
  IN_FILTER which = *(IN_FILTER*)value;
  switch (which) {
  case IN_VRML: 
    the_importer = new vrml2_utf8::parser;  
    break;
  case IN_PLY: 
#ifdef PLY
    the_importer = new ply::importer; 
#else
    no_support("ply input");
#endif
    break;
  case IN_CSO: 
#ifdef CSO
    the_importer = new cso::importer; 
#else
    no_support("C++/so input");
#endif
    break;
  default:
    cerr << "Unrecognized import filter identification number " << which << "\n";
    exit(-1);
  }
}

static void SetExporter(void *value)
{
  OUT_FILTER which = *(OUT_FILTER*)value;
  switch (which) {
  case OUT_VRML: 
    the_exporter = new vrml2_utf8::exporter;
    break;
  case OUT_MGF: 
#ifdef MGF
    the_exporter = new mgf::exporter(progname);
#else
    no_support("mgf output");
#endif
    break;
  case OUT_NFF: 
#ifdef NFF
    the_exporter = new nff::exporter(progname);
#else
    no_support("nff output");
#endif
    break;
  case OUT_CSO: 
#ifdef CSO
    the_exporter = new cso::exporter;
#else
    no_support("C++/so output");
#endif
    break;
  default:
    cerr << "Unrecognized export filter identification number " << which << "\n";
    exit(-1);
  }
}

// forward declaration
static void ShowUsage(void*);

static CMDLINEOPTDESC options[] = {
  {"-input-file-format", 2, TInFilt, NULL, SetImporter,
   "-input-file-format  vrml|ply     : input file format"},
  {"-output-file-format", 2, TOutFilt, NULL, SetExporter,
   "-output-file-format vrml|mgf|nff : output file format"},
  {"-help", 2, TYPELESS, NULL, ShowUsage,
   "-help                            : explain usage of this program"},
  {NULL	, 	0,	TYPELESS, 	NULL, 	DEFAULT_ACTION,
   NULL} // sentinel
};

static void ShowUsage(void*)
{
  fprintf(stdout, "%s: convert 3D file formats to, from or across VRML'97.\n", progname);
  fprintf(stdout, "\n");
  fprintf(stdout, "Usage:\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "\t%s [options] [input-file-names]\n", progname);
  fprintf(stdout, "\n");
  fprintf(stdout, "Options:\n");
  fprintf(stdout, "\n");
  PrintOptions(stdout, options);
  fprintf(stdout, "\n");
  fprintf(stdout, "%s reads the input files in order and converts them to\n", progname);
  fprintf(stdout, "the standard output. If no input files are given, %s\n", progname);
  fprintf(stdout, "reads from standard input. The default input or output\n");
  fprintf(stdout, "file format is VRML'97.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "Note: not all input/output file formats listed above may be supported.\n");
  fprintf(stdout, "\n");
  exit(0);
}

int main(int argc, char **argv)
{
  char *slash = strrchr(argv[0], '/');
  progname = slash ? slash+1 : argv[0];

  // ParseOptions removes the arguments from argc/argv that
  // it recognizes.
  ParseOptions(options, &argc, argv);

  // create a new world to hold the scene graph
  xrml::world *wrl = new xrml::world;

  // import the scene graph from the input files
  if (!the_importer) the_importer = new vrml2_utf8::parser;
  if (argc < 2) {
    if (!wrl->parse((char*)0, 0., the_importer))
      return 1;
  } else {
    for (int i=1; i<argc; i++) {
      xrml::importer *imp = the_importer->instantiate();
      cerr << "Parsing " << argv[i] << "\n";
      if (!wrl->parse(argv[i], 0., imp))
	return 1;
      delete imp;
    }
  }
  delete the_importer;

  // write it to the standard output
  if (!the_exporter) the_exporter = new vrml2_utf8::exporter;
  wrl->save((char*)0 /*stdout*/, the_exporter);

  return 0;
}

