#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Vk/VkErrorDialog.h>
#include "ValveControl.h"
#include "TiburonApp.h"
#include "DmErrno.h"

#define dprintf if (debug) fprintf

const char *const ValveControl::startingValveCallback = "startValveCallback";
const char *const ValveControl::stoppedValveCallback = "stopValveCallback";

ValveControl::ValveControl(const char *name, Widget parent, 
			   const char *dmPrefix, int id) :
  DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();
  
  _id = id;
  
  _label = 
    XtVaCreateManagedWidget("valveLabel", xmLabelWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_FORM,
			    NULL);

  _openButton = 
    XtVaCreateManagedWidget("openButton", xmPushButtonWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _label,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_POSITION,
			    XmNrightPosition, 50,
			    NULL);

  // Get colors to use in re-drawing buttons
  XtVaGetValues(_openButton,
		XmNarmColor, &_armColor,
		XmNbackground, &_background,
		XmNtopShadowColor, &_lightEdgeColor, 
		XmNbottomShadowColor, &_darkEdgeColor,
		NULL);
  
  XtAddCallback(_openButton, XmNarmCallback, 
		&ValveControl::buttonCallback,
		(XtPointer )this);

  XtAddCallback(_openButton, XmNdisarmCallback, 
		&ValveControl::buttonCallback,
		(XtPointer )this);

  _closeButton = 
    XtVaCreateManagedWidget("closeButton", xmPushButtonWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _label,
			    XmNleftAttachment, XmATTACH_POSITION,
			    XmNleftPosition, 50,			    
			    XmNrightAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(_closeButton, XmNarmCallback, 
		&ValveControl::buttonCallback,
		(XtPointer )this);

  XtAddCallback(_closeButton, XmNdisarmCallback, 
		&ValveControl::buttonCallback,
		(XtPointer )this);


  char itemName[MaxDmNameLen];
  strcpy(itemName, dmPrefix);
  char *namePtr = itemName + strlen(itemName);

  strcpy(namePtr, "A_SWITCH");
  _dmOpen = new DmBooleanObject(itemName);

  if (addDmObject(_dmOpen, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, "B_SWITCH");
  _dmClose = new DmBooleanObject(itemName);
  
  if (addDmObject(_dmClose, DmConsumeFlag) == -1)
    return;

  if (setConsumePeriod(Aperiodic) == -1)
    return;

  _armed = False;
  
  updateGui();
}


ValveControl::~ValveControl()
{
}


void ValveControl::updateGui(DM_Item handle)
{
  Boolean debug = False;
  
  if (_armed)
    /* User armed button; just return so X doesn't get confused on button 
       drawing. */
    return;
  
  char errorBuf[errorMsgSize];
  Errno errno;
  DM_Time t;
  MBool active;
  
  if ((errno = _dmOpen->read(&active, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::updateGui(), read dmOpen", errorBuf);
    setError(errorBuf);
    return;
  }

  Pixel background;
  dprintf(stderr, "ValveControl::updateGui() - open: %d, ", active);
  
  drawButton(_openButton, active);
  
  if ((errno = _dmClose->read(&active, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::updateGui(), read dmClose", errorBuf);
    setError(errorBuf);
    return;
  }
  dprintf(stderr, "close: %d\n", active);
  
  drawButton(_closeButton, active);
}


void ValveControl::buttonCallback(Widget w,
				  XtPointer clientData, XtPointer callData)
{
  ValveControl *obj = (ValveControl *)clientData;

  /* Either opening or closing valve, determine if starting or stopping 
     motion. */
  XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *)callData;
  if (cbs->reason == XmCR_ARM)
  {
    // Call callbacks before valve is started
    obj->callCallbacks(startingValveCallback, (void *)obj);
    obj->startMotion(w);
    obj->_armed = True;
  }
  else if (cbs->reason == XmCR_DISARM)
  {
    obj->stopMotion(w);
    // Call callbacks after valve is stopped
    obj->callCallbacks(stoppedValveCallback, (void *)obj);
    obj->_armed = False;
  }    
}


int ValveControl::startMotion(Widget w)
{
  Boolean debug = True;
  dprintf(stderr, "startMotion()\n");
  
  DmBooleanObject *dmObject;
  DM_Time t;
  char errorBuf[errorMsgSize];
  Errno errno;

  static struct timezone tz =
  {
    0, 0
  };
  

  if (w == _openButton)
  {
    dmObject = _dmOpen;
    // First stop motion in other direction
    if (stopMotion(_closeButton) == -1)
      return -1;
  }
  else
  {
    dmObject = _dmClose;
    // First stop motion in other direction
    if (stopMotion(_openButton) == -1)
      return -1;
  }

  /* ***
  dprintf(stderr, "ValveControl::startMotion() - startProvide %s\n",
	  dmObject->getName());

  if ((errno = dmObject->startProvide(DM_STATIC, ExclusiveProvider)) 
      != SUCCESS)
  { 
    sprintDmError(errno, "ValveControl::startMotion()", errorBuf);
    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }
  *** */

  if ((errno = dmObject->urgentOpen(DM_STATIC)) != SUCCESS)
  { 
    sprintDmError(errno, "ValveControl::startMotion(), urgentOpen()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }

  // TEST TEST TEST
  // Inducing a delay seems to make DataManager more reliable!
  struct timespec sleepTime;
  sleepTime.tv_sec = 0;
  sleepTime.tv_nsec = 100000000;

  nanosleep(&sleepTime, 0);

  int ret = 0;
  gettimeofday(&t, &tz);
  
  // Start motion
  MBool value;
  value = TRUE;
  
  dprintf(stderr, "ValveControl::startMotion() - write TRUE to %s\n",
	  dmObject->getName());

  if ((errno = dmObject->write(value, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::startMotion(), starting", errorBuf);
    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }

  /* *********
  dprintf(stderr, "ValveControl::startMotion() - stopProvide %s\n",
	  dmObject->getName());

  if ((errno = dmObject->stopProvide()) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::startMotion(), stopProvider()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }
  *********** */

  if ((errno = dmObject->urgentClose()) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::startMotion(), urgentClose()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }

  return ret;
}



int ValveControl::stopMotion(Widget w)
{
  Boolean debug = True;
  dprintf(stderr, "stopMotion()\n");
  
  DmBooleanObject *dmObject;
  DM_Time t;
  char errorBuf[errorMsgSize];
  Errno errno;

  static struct timezone tz =
  {
    0, 0
  };
  
  if (w == _openButton)
    dmObject = _dmOpen;
  else
    dmObject = _dmClose;

  /* ***********
  dprintf(stderr, "ValveControl::stopMotion() - startProvide %s\n",
	  dmObject->getName());

  if ((errno = dmObject->startProvide(DM_STATIC, ExclusiveProvider)) 
      != SUCCESS)
  { 
    sprintDmError(errno, "ValveControl::stopMotion()", errorBuf);
    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }
  *********** */

  if ((errno = dmObject->urgentOpen(DM_STATIC)) != SUCCESS)
  { 
    sprintDmError(errno, "ValveControl::startMotion(), urgentOpen()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }


  // TEST TEST TEST
  // Inducing a delay seems to make DataManager more reliable!
  struct timespec sleepTime;
  sleepTime.tv_sec = 0;
  sleepTime.tv_nsec = 100000000;

  nanosleep(&sleepTime, 0);

  int ret = 0;
  gettimeofday(&t, &tz);

  MBool status = FALSE;
  
  dprintf(stderr, "ValveControl::stopMotion() - write FALSE to %s\n",
	  dmObject->getName());

  // Stop motion
  if ((errno = dmObject->write(status, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::stopMotion(), stopping", errorBuf);
    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }

  /* *******
  dprintf(stderr, "ValveControl::stopMotion() - stopProvide %s\n",
	  dmObject->getName());

  if ((errno = dmObject->stopProvide()) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::stopMotion(), stopProvider()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }
  ********* */

  if ((errno = dmObject->urgentClose()) != SUCCESS)
  {
    sprintDmError(errno, "ValveControl::startMotion(), urgentClose()", 
		  errorBuf);

    setError(errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    ret = -1;
  }

  return ret;
}


DmBooleanObject *ValveControl::dmOpen()
{
  return _dmOpen;
}

  
DmBooleanObject *ValveControl::dmClose()
{
  return _dmClose;
}
  


void ValveControl::drawButton(Widget button, MBool armed)
{
  if (armed)
  {
    // Display button as "pressed in"
    XtVaSetValues(button,
		  XmNbackground, _armColor,
		  XmNtopShadowColor, _darkEdgeColor, 
		  XmNbottomShadowColor, _lightEdgeColor, 
		  NULL);
  }
  else
  {
    // Display button as "out"
    XtVaSetValues(button,
		  XmNbackground, _background, 
		  XmNtopShadowColor, _lightEdgeColor, 
		  XmNbottomShadowColor, _darkEdgeColor, 
		  NULL);
  }
}
