// This may look like C code, but it is really -*- C++ -*-

// **********************************************************
// File hgdemovd.C
// ----------------------------------------------------------
// Created: J. Faschingbauer
// **********************************************************

// changed: mpichler, 19940922


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


#include "compdate.h"
#include <iostream.h>

/*#include <osfcn.h>*/
#include <stdlib.h>

// Signal handling
#include <signal.h>

// Environment-variables
/*#include <hyperg/utils/options.h>*/
#include <hyperg/utils/environ.h>
#include <sys/file.h>
#include <fcntl.h>

#include <hyperg/hyperg/message.h>
#include <hyperg/hyperg/verbose.h>

/*#ifdef sparc*/
#include <sys/wait.h>
/*#endif*/

#if defined(PMAX)
extern "C" {
  int sigaction (
    int signo,
    struct sigaction *act,
    struct sigaction *oldact
  );
}
#endif

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

#include <Dispatch/dispatcher.h>
#include <Dispatch/rpcservice.h>
#include <Dispatch/rpcstream.h>

#include <hyperg/viewer/rvwstuff.h>





/* VERBOSE/DEBUG stuff now in verbose.h */
int verbose = 0;


// **********************************************************
// class ViewerServer
// **********************************************************
class ViewerServer: public RpcService
{
  public:
    ViewerServer (int port);
    virtual ~ViewerServer ()  { }
    int newfd () const  { return _newfd; }
    int port () const  { return _port; }
   
  private:
    virtual void createReader (int fd);

    int _newfd;
};

ViewerServer::ViewerServer (int port = 0): RpcService (port)
{
  _newfd = 0;
}

void ViewerServer::createReader (int fd)
{
  long pid;
  if (!(pid = fork()))
  {
    _newfd = fd;
    quitRunning ();
  }
  else
    close (fd);
}


ViewerServer* server = nil;
Dispatcher* dispatcher = nil;



// static void syserr (const char* msg)
// {
//   extern int  errno, sys_nerr;
//   extern char *sys_errlist [];

//   cerr << "ViewerServer: " << msg << " (Error #" << errno;
//   if (errno > 0  && errno < sys_nerr)
//     cerr << ": " << sys_errlist[errno];
//   cerr << ") -- process stopped\n";
//   exit (1);
// }

//
// This bit of code catches control-c's, it cleans up everything.
//
// #ifdef SYSV
#ifdef SGI
//#define BADSIG (void (*)(int ...))-1
/* mpichler, 19940905 */
#define BADSIG (void (*)(...))-1
// #else
// #define BADSIG (void (*)(int))-1
#endif
// #endif

// static void SigTerm (int sig)
// {
//   if (signal (sig, SIG_IGN) == SIG_ERR)
//     syserr ("signal");
//   delete server;
//   delete dispatcher;

//   cerr << "Program stopped (caught signal " << sig << ")." << endl;
//   close (2);
//   exit (0);
// }


// static void cleanup (int i)
// {
//   if (signal (i, SIG_IGN) == SIG_ERR)
//     syserr ("signal");
//   if (i)
//     cerr << "ViewerServer: got signal " << i << endl ;
//   cerr << "Program stopped" << endl ;
//   exit (i);
// }


// #ifndef SYSV
// /*
// * void wait_for_child()
// * =====================
// */
// void wait_for_child(int) {
//    int pid;
//    union wait status;
//    static int options = WNOHANG;
//    struct rusage rusage;
   
//    DEBUGNL ("hg3d: wait_for_child: got SIGCLD.");
//    if ( (pid = wait3(&status, options, &rusage)) <= 0 ) {
//       DEBUGNL("hg3d: wait3 failed: pid=" << pid);
//    }
//    DEBUGNL("hg3d: wait_for_child: pid=" << pid);
// }
// #endif


#if defined(SGI) || defined(SGI_GNU) || defined(SUN5) || defined(SUN5_GNU)
static void sigchild_handler (...)
#else
# if defined(PMAX) || defined(PMAX_GNU) || defined(UnixWare) || defined(SUN4_GNU)
static void sigchild_handler ()
# else
static void sigchild_handler (int)
# endif
#endif /* SGI, PMAX et. al. */
{
  pid_t pid;
  int stat;
  // wait for the parser to be removed from the process table
  pid = ::wait (&stat);
// #if defined(PMAX) ||defined(SUN4) 
//      wait((union wait*)0);
// #else
//      wait((int*)0);
// #endif
}



// ----------------------------------------------------------
// change this to move over to an individual viewer !!!!!!!!!
#include "iv3dvw.h"
// ----------------------------------------------------------

#include "gecontext.h"

#define HARMONY3DSCENE
#include "hg3dopt.h"


void helponarguments ()
{
  cerr  <<
    "\n"
    "Usage: harscened [DaemonOptions] [-- ViewerOptions]\n"
    "\n"
    "DaemonOptions:\n"
    "  -h             print this help\n"
    "  -p port        document port number (for passing with -sp to harmony)\n"
    "  -c callerport  caller port number\n\n"
    "\n"
    "ViewerOptions:\n"
    "  --             separates viewer from daemon options\n"
    "  -help          same as -h\n"
  << commonViewerOptions
  << endl;

} // helponarguments


int main (int argc, char** argv)
{
  HgMessage::init ("3D Scene Viewer");
  cerr << "--- Harmony Scene Viewer --- Version 1.0 (" << GEContext::implementation ()
       << ") of " << compDate () << " ---" << endl;

  int port = 0;  // default: get the next free port number
  int caller_port = 0;
   
  extern char* optarg;
  int copt;
  while ((copt = getopt (argc, argv, "hp:c:"))  !=   EOF)
  {
     switch (copt)
     {
       case 'h':
         helponarguments ();
       return 0;  // exit program
       case 'p':
         port = atoi (optarg);
       break;
       case 'c':
         caller_port = atoi (optarg);
       break;
     }
  }

  dispatcher = new Dispatcher ();
  Dispatcher::instance (dispatcher);
   
  server = new ViewerServer (port);

  cout << "port: " << server->port() << endl;

  if (caller_port)
  {
    sleep (1);
    // char caller_host[32];
    // gethostname (caller_host, 32);
    RString caller_host = Environ::hostName ();

    rpcstream to_caller;  // jfasch, 19941129 - moved outside if-block for gnucc
    // mpichler, 19941206 - moved back inside if-block; get seg'fault on SGI on exiting harmony otherwise

    to_caller.connect (caller_host, caller_port);
    if (to_caller.fail ())
    { // "nix connect"
      cerr << "Harmony Scene Daemon: connection to caller failed" << endl;
      exit (-1);
    }
    to_caller << server->port ();
    to_caller.close ();
  }

  // catch signals
  struct sigaction chldaction1;
  // if you get an error here check the type of sigchild_handler above with the man page
  chldaction1.sa_handler = sigchild_handler;
     
  if (sigaction(SIGCHLD, &chldaction1, nil) == -1)
  {
      cerr << "Scene daemon: Could not set signal handler for child process!" << endl;
      return 1;
  }

  server->run ();

  DEBUGNL ("hg3d: ViewerServer forked.");

  // this is a child of the server
  int fd = server->newfd ();

  delete server;
  delete dispatcher;
   
  server = nil;
  dispatcher = nil;
   
  if (fd)
  {
      dispatcher = new Dispatcher ();
      Dispatcher::instance (dispatcher);

      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);
      // hg3d style name: Harmony.Scene

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

      RemoteViewerManager* manager = new RemoteViewerManager (fd);

      // ----------------------------------------------------------
      // change this to move over to an individual viewer !!!!!!!!!
      HgViewer* hgviewer = new HgIv3dViewer (manager, session) ;
      // ----------------------------------------------------------

      manager->viewer (hgviewer);

      session->run();

      delete hgviewer;
      delete manager;
      delete dispatcher;

      close (fd);
      DEBUGNL ("hg3d: child of scene viewer daemon exits.");
  }

  exit (0);
  return 0;  // never reached
}
