#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include "RovApp.h"

RovApp::RovApp(char *appClassName,
	       int *argc,
	       char **argv,
	       XrmOptionDescRec *optionList,
	       int sizeOfOptionList) : VkApp(appClassName,
					     argc, argv,
					     optionList,
					     sizeOfOptionList)

{
  _aperiodicHandler = NULL;
  setSignalHandlers();
  
  // Initialize Data Manager
  Errno ret;  
  if ((ret = dm_init(NULL)) != OK)
  {
    if (ret == EDM_NO_DAEMON)
    {
      fprintf(stderr, "Data Manager daemon isn't running\n");
    }
    else
    {
      fprintf(stderr, "Data Manager daemon initialization error %d\n", ret);
    }
    this->exit(1);
  }

  char errorBuf[512];
  
  if (defaultInputs(errorBuf) == -1)
  {
    fprintf(stderr, "%s\n", errorBuf);
    this->exit(1);
  }

  getOptions(*argc, argv);
  
  systemAlarms = new SystemAlarms(_systemAlarmFile);
  if (systemAlarms->error())
  {
    systemAlarms->prtErrorMsg();
    this->exit(1);
  }
  
  _aperiodicHandler = new AperiodicHandler();
  _periodicHandler = new PeriodicHandler();
}


RovApp::~RovApp()
{
}



void RovApp::startTelemetryHandlers()
{
  _periodicHandler->start();
  _aperiodicHandler->start();
}


int RovApp::registerConsumer(DmGuiObject *obj, int period)
{
  if (period == Aperiodic)
  {
    // Remove DmGuiObject from periodic handler if it's there
    _periodicHandler->unregisterObj(obj);

    // Register with aperiodic handler
    _aperiodicHandler->registerObj(obj);
  }
  else if (period <= 0)
  {
    // Bad period
    return -1;
  }
  else
  {
    // Remove DmGuiObject from aperiodic handler if it's there
    _aperiodicHandler->unregisterObj(obj);

    // Register with periodic handler
    _periodicHandler->registerObj(obj, period);  
  }
  return 0;
}


void RovApp::unregisterConsumer(DmGuiObject *obj)
{
  _aperiodicHandler->unregisterObj(obj);
  _periodicHandler->unregisterObj(obj);
  return;
}


AperiodicHandler *RovApp::aperiodicHandler()
{
  return _aperiodicHandler;
}


int RovApp::defaultInputs(char *errorBuf)
{
  char *ptr;
  if ((ptr = getenv(rovEnvVar)) == NULL)
  {
    sprintf(errorBuf, "Environment variable \"%s\" not set\n", rovEnvVar);
    return -1;
  }

  sprintf(_systemAlarmFile, "%s/%s", ptr, defaultAlarmDefFile);

  return 0;
}


void RovApp::getOptions(int argc, char **argv)
{
  BOOLEAN error = FALSE;
  _debug = FALSE;
  
  for (int i = 1; i < argc; i++)
  {
    if (!strcmp(argv[i], "-alarms") && i < argc - 1)
    {
      strcpy(_systemAlarmFile, argv[++i]);
    }
    else if (!strcmp(argv[i], "-debug"))
      _debug = TRUE;

    else
    {
      fprintf(stderr, "\"%s\" - invalid or incomplete option\n", argv[i]);
      error = TRUE;
    }
  }

  if (error)
  {
    fprintf(stderr, "usage: %s [-alarms file][-debug]\n", argv[0]);
    this->exit(1);
  }
}


void RovApp::exit(int status)
{
  BOOLEAN debug = TRUE;
  
  // Remove signal handlers
  clearSignalHandlers();

  if (_aperiodicHandler)
  {
    dprintf(stderr, "RovApp::exit() - delete _aperiodicHandler\n");
    
    // Clean up semaphore and child process 
    delete _aperiodicHandler;
  }
  
  ::exit(status);
}




void RovApp::signalHandler(int sigNo)
{
  static int signalNo;
  signalNo = sigNo;
  int status;
  BOOLEAN debug = TRUE;
  dprintf(stderr, "RovApp::signalHandler()\n");
  pid_t pid;
  
  BOOLEAN fatal = FALSE;

  // Get pointer to this object, since we are in a static function  
  RovApp *rovApp = (RovApp *)theApplication;

  if (sigNo == SIGCHLD) 
  {
    dprintf(stderr, "RovApp::signalHandler() - reap children...\n");
    // Reap child(ren)
    while ((pid = wait(&status)) != -1)
    {
      if (rovApp->_aperiodicHandler && 
	  pid == rovApp->_aperiodicHandler->_monitorPid)
	rovApp->_aperiodicHandler->_monitorPid = 0;
    }
  }
  
  dprintf(stderr, "RovApp::signalHandler() - got signal %d\n", 
	  signalNo);
  
  if (signalNo == SIGCHLD || signalNo == SIGINT || signalNo == SIGQUIT
      || signalNo == SIGTERM)
  {
    /* Do cleanup that absolutely must get done. Note that 
       followupSignalHandler() may not necessarily get called (e.g. if
       user hits two cntrl-C's while mouse is not in app's window)
    */
    if (rovApp->_aperiodicHandler)
      rovApp->_aperiodicHandler->cleanup();
  }
  
  XFlush(theApplication->display());
  
  XtAppAddWorkProc(theApplication->appContext(), 
		   &RovApp::followupSignalHandler, 
		   &signalNo);
}



Boolean RovApp::followupSignalHandler(XtPointer clientData)
{
  BOOLEAN debug = TRUE;
  int sigNo = *((int *)clientData);
  dprintf(stderr, 
	  "RovApp::followupSignalHandler() for signal %d\n",
	  sigNo);

  BOOLEAN fatal = FALSE;
  
  fprintf(stderr, "Received signal %d\n", sigNo);

  if (sigNo == SIGCHLD)
  {
    fprintf(stderr, "subprocess died\n");
    fatal = TRUE;
  }
  else if (sigNo == SIGTERM || sigNo == SIGINT || sigNo == SIGQUIT)
    fatal = TRUE;


  if (fatal)
  {
    ((RovApp *)theApplication)->exit(1);
  }

  
  return False;
}


void RovApp::setSignalHandlers()
{
  signal(SIGCHLD, &RovApp::signalHandler);
  signal(SIGTERM, &RovApp::signalHandler);
  signal(SIGINT, &RovApp::signalHandler);
  signal(SIGQUIT, &RovApp::signalHandler);
}


void RovApp::clearSignalHandlers()
{
  signal(SIGCHLD, SIG_IGN);
  signal(SIGTERM, SIG_IGN);
  signal(SIGINT, SIG_IGN);
  signal(SIGQUIT, SIG_IGN);
}
  

