#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <Vk/VkErrorDialog.h>
#include "DmGuiApp.h"
#include "stringUtils.h"
#include "CtdSetup.h"
#include "TiburonApp.h"

#define dprintf if (debug) fprintf


CtdSetup::CtdSetup(const char *name, Widget parent)
  : VkComponent(name)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();

  Widget label;
  
  label = 
    XtVaCreateManagedWidget("programLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
  

  _program = 
    XtVaCreateManagedWidget("program", xmTextFieldWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_POSITION,
			    XmNleftPosition, 50,
			    NULL);
  
  XmTextFieldSetString(_program, "displayCtd");
  
  label = 
    XtVaCreateManagedWidget("configFileLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _program,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
  
  _configFile =
    XtVaCreateManagedWidget("configFile", xmTextFieldWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _program,
			    XmNleftAttachment, XmATTACH_POSITION,
			    XmNleftPosition, 50,	
			    NULL);
  

  XmTextFieldSetString(_configFile, "/usr/tiburon/unix/gui/ctd/ctd.cfg");

  _autoStart = 
    XtVaCreateManagedWidget("autoStart", xmToggleButtonWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _configFile,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);

  _displayPid = 0;
}


CtdSetup::~CtdSetup()
{
}


int CtdSetup::startDisplay()
{
  if (_displayPid)
  {
    theErrorDialog->postAndWait("CTD display program already started");
    return -1;
  }
  
  Boolean debug = ((TiburonApp *)theApplication)->debug();
  
  char program[256];
  char configFile[256];

  char *ptr;
  
  ptr = XmTextFieldGetString(_program);
  strcpy(program, ptr);
  XtFree(ptr);
  
  dprintf(stderr, "CtdSetup::startDisplay() - program = %s\n", program);

  if (!strlen(program) || allBlanks(program))
  {
    theErrorDialog->postAndWait("Must specify program");
    return -1;
  }

  ptr = XmTextFieldGetString(_configFile);
  strcpy(configFile, ptr);
  XtFree(ptr);
  
  if (!strlen(configFile) || allBlanks(configFile))
  {
    theErrorDialog->postAndWait("Must specify configuration file");
    return -1;
  }

  Boolean autoStart;
  XtVaGetValues(_autoStart, XmNset, &autoStart, NULL);

  char *argv[10];
  int nargs = 0;
  argv[nargs++] = program;
  argv[nargs++] = configFile;
  if (autoStart)
    argv[nargs++] = "-a";

  argv[nargs] = NULL;
  
  int pipeFd[2];

  if (pipe(pipeFd) == -1)
  {
    perror("CtdSetup::startDisplay() - pipe() failed");
    exit(1);
  }
  char buf[512];
  
  switch (_displayPid = fork())
  {
    case 0:
    // Child process

    // Re-direct stderr to pipe
    close(STDERR_FILENO);
    dup(pipeFd[1]);
    close(pipeFd[0]);
    
    execvp(program, argv);
    sprintf(buf, "Couldn't exec \"%s\"", program); 
    perror(buf);
    _exit(1);
    break;
    
    case -1:
    perror("CtdSetup::fork() failed");
    exit(1);
    break;
  }

  close(pipeFd[1]);

  _inputId = XtAppAddInput(theApplication->appContext(), pipeFd[0],
			   (XtPointer )XtInputReadMask, 
			   &CtdSetup::msgCallback, this);

  dprintf(stderr, "CtdSetup::startDisplay() - child pid = %d\n", _displayPid);
}



Widget CtdSetupDialog::createDialog(Widget parent)
{
  Widget base = VkGenericDialog::createDialog(parent);
  _setup = new CtdSetup("ctdSetup", base);
  _setup->show();
  _showOK = False;
  _showApply = _showCancel = True;
  return base;
}


void CtdSetupDialog::applyCallback(Widget parent,
				   XtPointer clientData,
				   XtPointer callData)
{
  Boolean debug = ((TiburonApp *)theApplication)->debug();
  
  CtdSetupDialog *obj = (CtdSetupDialog *)clientData;
  obj->_setup->startDisplay();
  dprintf(stderr, 
	  "CtdSetupDialog::applyCallback() - back from startDisplay()\n");
  
}


void CtdSetup::displayErrors(int fd)
{
  // Check to see if child is still alive
  if (_displayPid)
  {   
    if (kill(_displayPid, 0) == -1)
    { 
      /* Child is dead, so don't check its pipe anymore */
      XtRemoveInput(_inputId);
      _displayPid = 0;
      close(fd);
    }
  }

  int nBytes;
  char buf[512];

  char *ptr = buf;
  *ptr = '\0';
  
  for (int nTries = 0; nTries < 10; nTries++)
  {
    int maxBytes = sizeof(buf) - (ptr - buf);
    
    if ((nBytes = read(fd, ptr, maxBytes)) <= 0)
    {
      if (nTries == 0)
	break;
    }

    if (ptr[nBytes] == '\n')
      // Got full message
      break;
    
    ptr += nBytes; 
  }

  if (strlen(buf) == 0)
    return;
  
  fprintf(stderr, "displayErrors() - %s\n", buf);
  theErrorDialog->postAndWait(buf);
  return;
}


void CtdSetup::msgCallback(XtPointer clientData, int *fd, XtInputId *id)
{
  CtdSetup *obj = (CtdSetup *)clientData;
  obj->displayErrors(*fd);
}
