//<copyright>
// 
// Copyright (c) 1993,94,95,96
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// Name:        main.C
//
// Purpose:     main program for VRweb (VRML 3D scene viewer)
//
// Created:      3 Jun 93   Michael Pichler
//
// Changed:      1 Feb 96   Michael Pichler
//
// $Id: main.C,v 1.10 1996/02/01 10:04:56 mpichler Exp mpichler $
//
//
// Description
// -----------
//
// VRweb (vrweb-*) for UNIX/X11; communication with Web browsers.
//
//</file>


char HyperGWhat[] = "@(#)[Hyper-G] [HGC-H] vrweb*\t1.1.2 [VRweb Scene Viewer] [Michael Pichler]";



#include "scenewin.h"
#include "hg3dopt.h"
#include "geomobj.h"
#include "srcanch.h"
#include "stranslate.h"
#include "gecontext.h"
#include "urlserver.h"
#include "remote.h"

#include "imgview.h"
#include "txtview.h"

#include <hyperg/hyperg/message.h>
#include <hyperg/hyperg/verbose.h>
#include <hyperg/widgets/cursors.h>
#include <InterViews/enter-scope.h>

#include <IV-look/kit.h>
#include <InterViews/display.h>
#include <InterViews/event.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/window.h>
#include <OS/string.h>

#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>



/*** SceneViewer ***/

class SceneViewer: public SceneWindow  // standalone scene viewer
{
  public:
    SceneViewer (Session* session, const char* wintitle)
    : SceneWindow (session, wintitle)
    {
      urlserver_ = 0;
      opened_file_ = 0;
    }

    ~SceneViewer ()
    {
      delete urlserver_;
    }

    // Scene3D
    void requestInlineURL (             // URL-request for inline VRML
      QvWWWInline* node,                //   node (where attach inline data)
      const char* url,                  //   requested URL (of data to be fetched)
      const char* docurl                //   URL of requesting document (for local URLs)
    );

    int writeOriginalScene (ostream& os);

    void showHelp (const char* file);
    void hold ();

    int followLink (const GeometricObject*, const SourceAnchor*);

    void loadTexture (Material* mat);  // load material from file

    // SceneWindow/Scene3D
    void clear ();

    URLServer* urlserverInstance ()
    {
      if (!urlserver_)
        urlserver_ = new URLServer (this);
      return urlserver_;
    }

    virtual void acceptRemoteRequests (Window* win, int on);
    virtual void propertyRemoteRequest (Window* win, const Event& e);

  private:
    URLServer* urlserver_;
    FILE* opened_file_;
};  // SceneViewer


int verbose = 0;  /* turn on/off DEBUG-messages */


void helponarguments ()
{
  cerr <<
    "\n"
    "Usage: vrweb... [Options] [FILE]\n"
    "\n"
    "vrweb-ogl: Open GL - vrweb-gl: Iris GL - vrweb-mesa: Mesa library\n"
    "specify \"-\" as FILE to read from std. input\n"
    "\n"
    "Options:\n"
    "  -h[elp]        print this help\n"
    "  -remote file   open file in a running VRweb instance\n"
  << commonViewerOptions
  << endl;
}


/*** remote calls ***/

const char* const RemoteVersionProperty = "_VRWEB_VERSION";
const char* const RemoteCommandProperty = "_VRWEB_COMMAND";
const char* const remoteVersion = "VRweb 1.1";


void SceneViewer::acceptRemoteRequests (Window* win, int on)
{
  // window is bound, offer it for remote calls

  // cerr << "setting remote version to " << remoteVersion << endl;
  remoteSetVersion (win->display ()->rep (), win->rep (), on ? remoteVersion : (const char*) NULL);
}


void SceneViewer::propertyRemoteRequest (Window* win, const Event& e)
{
  RString command;

  // command syntax (VRweb internal property):
  // file=FILENAME\n
  // URL=URL\n
  // other fields ignored (extensible)

  if (!remoteReceiveCommand (win->display ()->rep (), win->rep (), e.rep (), command))
    return;  // some other property changed

  DEBUGNL ("recieved remote command: " << command);

  RString filename, url;
  const char* cmd = command;
  while (*cmd)  // parse command fields
  {
    const char* nlpos = strchr (cmd, '\n');
    if (!nlpos)
    { DEBUGNL ("remote command garbled; ignoring fields from: " << cmd);
      break;
    }
    if (!strncmp (cmd, "file=", 5))
    { cmd += 5;
      RString temp (cmd, nlpos - cmd);
      filename = temp;
    }
    else if (!strncmp (cmd, "URL=", 4))
    { cmd += 4;
      RString temp (cmd, nlpos - cmd);
      url = temp;
    }
    else
    { DEBUGNL ("unknown remote command field (ignored) at beginning of: " << cmd);
    } // for future extension

    cmd = nlpos + 1;
  }

  DEBUGNL ("filename: " << filename << "; URL: " << url);

  GEContext* gecon = gecontext ();

  if (gecon)
    gecon->pushCursor (HgCursors::instance ()->hourglass ());

  FILE* file = fopen (filename, "r");
  if (!file)
  {
    cerr << "VRweb. Error: remote file '" << filename << "' not found." << endl;
    setTitle (STranslate::str (STranslate::ProgressREADY));
    return;
  }

  if (readSceneFILE (file))
    setTitle (STranslate::str (STranslate::ProgressREADY));  // parse error
  else
  {
    if (url.length ())
    { currentURL (url);
      setTitle (url);
    }
    else
      setTitle (filename);

    reset ();  // and redraw
  }

  if (opened_file_)
    fclose (opened_file_);

  // web browser erases temp file when helper app
  // (here: remote caller) terminates;
  // file is kept open to be able to save it, although it already
  // seems to have disappeared from the file system

  setCurrentFilename (filename);
  opened_file_ = file;  // will be closed in clear; available for saving
  DEBUGNL ("file " << (void*) opened_file_ << " is kept open until scene is destroyed.");

  if (gecon)
    gecon->popCursor ();
} // propertyRemoteRequest



/*** main ***/


int main (int argc, char* argv[])
{
#ifdef MEMLEAK
_do_not_compile_Interviews_programs_with_memleak_ _at_least_not_on_SGI_;
MemLeakBegin ();
#endif

/*
char* test = new char [20];  // test malloc-debug
test [20] = 'j';
delete test;
*/

  HgMessage::init ("VRweb Scene Viewer");

  Session* session = new Session ("Harmony", argc, argv, hg3d_options, hg3d_props);
  WidgetKit& kit = *WidgetKit::instance ();
  Style* style = new Style ("Scene", session->style ());
  kit.style (style);
  // style name (X/resources): Harmony.Scene
  // always use kit.style -- session.style never changes!!!

  if (style->value_is_on ("commandlinehelp"))
  {
    helponarguments ();
    return 0;  // exit
  }

  if (style->value_is_on ("verbose"))
    verbose = 1;

  // handle remote calls (see propertyRemoteRequest above)

  String filename;
  if (style->find_attribute ("remote", filename))
  {
    RString command ("file=");
    command += filename.string ();
    command += "\n";

    String strval;
    if (style->find_attribute ("URL", strval))
    { command += "URL=";
      command += strval.string ();
      command += "\n";
    }

    if (remoteSendCommand (session->default_display ()->rep (), command))
    {
      DEBUGNL ("VRweb: " << command << "... handled remote");
      // TODO: ensure remote instance has opened file before exiting here
      sleep (3);  // hack
      return 0;  // my work ends here
    }
    else
    { DEBUGNL ("VRweb remote: will open " << filename.string () << " myself");
    }
  }

  // create application window
  SceneViewer scene (session, STranslate::str (STranslate::VRWEBSCENEVIEWER));  // the scene

  DEBUGNL ("VRweb: mapping application window");
  Window* appwin = scene.appwin ();
  appwin->map ();  // map application window
/*
  // care for immediate mapping (feedback on loading) -- does not work
  appwin->display ()->flush ();
  appwin->repair ();
  appwin->display ()->sync ();  // flush not sufficient
*/
/*
  appwin->repair ();
  appwin->display ()->flush ();
  Event e;  // read pending events to map and draw window -- does not work
  while (session->pending ())
  {
    session->read (e);
    e.handle ();
  }
*/

  // read initial scene

  if (argc == 2)  // IV-options are already removed from argument list
    if (argv [1][0] == '-' && !argv [1][1])  // argument "-": stdin
    { scene.readSceneFILE (stdin);
      scene.setTitle ("<stdin>");
    }
    else                                     // filename argument
    {
      if (scene.readSceneFile (argv [1]))
        scene.setTitle (STranslate::str (STranslate::ProgressREADY));  // parse error
      else
      {
        const char* url = scene.mostRecentURL ();
        scene.setTitle (*url ? url : argv [1]);
      }
    }
  else if (filename.length ())  // remote file
  {
    scene.readSceneFile (filename.string ());

    const char* url = scene.mostRecentURL ();
    scene.setTitle (*url ? url : filename.string ());
  }
  else  // no filename on command line
  {
    if (style->find_attribute ("initialDemo", filename))
    { scene.readSceneFile (filename.string ());
      scene.setTitle (filename.string ());
    }
  }

  // run the session (dispatcher loop)
  // int retval = session->run_window (scene.appwin ());
  int retval = session->run ();  // window already mapped at the beginning

// if dbmalloc is active scan memory for traps
char* finaltest = new char;
delete finaltest;

#ifdef MEMLEAK
MemLeakEnd ();
#endif


  return retval;

} // main



/*** SceneViewer ***/


// clear

void SceneViewer::clear ()
{
  DEBUGNL ("SceneViewer::clear: clearing pending requests and scene data");
  if (urlserver_)
    urlserver_->clearAllRequests ();
  if (opened_file_)
  { DEBUGNL ("closing file " << (void*) opened_file_ << endl);
    fclose (opened_file_);
    opened_file_ = 0;
  }
  SceneWindow::clear ();
}


// requestInlineURL
// request an inline URL (must be a VRML scene)
// asynchronous request (function will return immediately)

void SceneViewer::requestInlineURL (QvWWWInline* node, const char* url, const char* docurl)
{
Scene3D::requestInlineURL (node, url, docurl);  // until completion
return;

  URLServer* urlserver = urlserverInstance ();
  urlserver->appendInlineRequest (node, url, docurl);
  urlserver->handleRequests (0);  // begin to handle inline request now

} // requestInlineURL


// writeOriginalScene
// write original scene (either known by name or kept FILE* open)

int SceneViewer::writeOriginalScene (ostream& os)
{
  if (opened_file_)  // copy file still opened
  { DEBUGNL ("writeOriginalScene of file that has been kept open");
    rewind (opened_file_);
    return copyFile (opened_file_, os);
  }

  return Scene3D::writeOriginalScene (os);

} // writeOriginalScene


// showHelp
// now shared by Harmony version in scene3d.C
// will be moved here when Harmony has its own help system

void SceneViewer::showHelp (const char* file)
{
  GEContext* gecon = gecontext ();

  if (gecon)
    gecon->pushCursor (HgCursors::instance ()->hourglass ());
    
  SceneWindow::showHelp (file);

  if (gecon)
    gecon->popCursor ();
}


// hold this window

void SceneViewer::hold ()
{
  Window* win = appwin ();
  if (win->bound ())
    acceptRemoteRequests (win, 0);  // do no longer accept remote requests
}


// followLink (SDF)
// anachronism of early demo days - will be removed soon

// follow a link by loading another scene or starting another viewer
// name syntax: ".*(filename)T.*"  (.* ... any no. of any characters)
// where 'T' (type) is either T for text, I for image (TIFF) or 3 for 3D scenes
// returns 1 if new 3D scene must be displayed, zero otherwise
// group anchors are ignored in the standalone viewer

int SceneViewer::followLink (const GeometricObject* hitobj, const SourceAnchor*)
{
/*
  // group anchor tests
  long id = hitobj->groupAnchorId (groups);
  if (id)
    cerr << "followLink: would activate link with anchor id " << id << endl;
  else
  { cerr << "followLink: no link to activate" << endl;
    return 0;
  }
*/

  const char* name = hitobj->name ();

  const char* pos1 = strchr (name, '(');
  char* pos2 = (char*) strchr (name, ')');

  if (!pos1 || !pos2 || pos2-pos1 < 2)
  {
    cerr << "hg3d. syntax error in anchor name '" << name << "'." << endl;
    return '\0';
  }

  pos1++;  // pos1: filename
  *pos2 = '\0';
  char file [256];
  sprintf (file, "%s%s", currentPath (), pos1);

  *pos2 = ')';
  char type = pos2 [1];

  const char* ppos = strrchr (pos1, '.');
  const char* slpos = strchr (pos1, '/');
  int noext = !ppos || (ppos && slpos && ppos < slpos);  // no . or only in path

  switch (type)
  { case 'T':
      if (noext)  // default: .txt
        strcat (file, ".txt");
      showtext (file);
    break;

    case 'I':
      if (noext)  // default: .tif
        strcat (file, ".tif");
      showimage (file);
    break;

    case '3':
      if (noext)  // default: .sdf
        strcat (file, ".sdf");
      readSceneFile (file);
    return 1;

    default:
      cerr << "hg3d. unknown type '" << type << "' of anchor '" << name << "'." << endl;
  }

  return 0;

} // followLink
