#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/Label.h>
#include <Xm/Frame.h>
#include <Vk/VkErrorDialog.h>
#include "ReleaseControl.h"
#include "tiburon.h"
#include "ersDcon.h"
#include "ers.h"
#include "TiburonApp.h"
#include "DmErrno.h"
#include "TableForm.h"

const char *const ReleaseControl::latchDisarmedMnem = "Disarmed";
const char *const ReleaseControl::latchArmedMnem = "Armed";
const char *const ReleaseControl::latchArmedNoPwrMnem = "ArmedNoPwr";
const char *const ReleaseControl::latchRunningMnem = "Running";

const char *const ReleaseControl::dmArmCmdSuffix = "_LATCH_ARM_CMD";
const char *const ReleaseControl::dmJogCmdSuffix = "_LATCH_JOG_CMD";
const char *const ReleaseControl::dmReleaseCmdSuffix = "_LATCH_RELEASE_CMD";
const char *const ReleaseControl::dmStatusSuffix = "_LATCH_STATUS";

ReleaseControl::ReleaseControl(const char *name, Widget parent, 
			   const char *dmPrefix)
  : DmGuiObject(name, parent)
{
  _status = 0;
  _ready = False;
  
  _baseWidget = 
    XtVaCreateWidget(name, xmRowColumnWidgetClass, parent, 
		     XmNorientation, XmHORIZONTAL, 
		     NULL);
  
  installDestroyHandler();

  Widget frame;
  
  frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, _baseWidget,
				  NULL);
  
  Widget mgr = 
    XtVaCreateWidget("mgr", xmRowColumnWidgetClass, frame,
		     XmNorientation, XmVERTICAL,
		     NULL);

  _safeButton =
    XtVaCreateManagedWidget("safeButton", xmToggleButtonWidgetClass, mgr,
			    XmNset, True,
			    NULL);

  XtAddCallback(_safeButton, XmNvalueChangedCallback,
		&ReleaseControl::safeCallback,
		(XtPointer )this);

  _armButton = 
    XtVaCreateManagedWidget("armButton", xmToggleButtonWidgetClass, mgr,
			    NULL);
  
  XtAddCallback(_armButton, XmNvalueChangedCallback,
		&ReleaseControl::armCallback,
		(XtPointer )this);
  
  XtManageChild(mgr);
  
  frame = XtVaCreateWidget("frame", xmFrameWidgetClass, _baseWidget,
			   NULL);
  
  _armedMgr = 
    XtVaCreateWidget("armedMgr", xmRowColumnWidgetClass, frame,
		     XmNorientation, XmVERTICAL,
		     NULL);
  
  _jogButton = 
    XtVaCreateManagedWidget("jogButton", xmPushButtonWidgetClass, _armedMgr,
			    NULL);

  XtAddCallback(_jogButton, XmNarmCallback,
		&ReleaseControl::jogCallback,
		(XtPointer )this);
  
  XtAddCallback(_jogButton, XmNdisarmCallback,
		&ReleaseControl::jogCallback,
		(XtPointer )this);

  _releaseButton = 
    XtVaCreateManagedWidget("releaseButton", xmPushButtonWidgetClass, 
			    _armedMgr,
			    NULL);

  XtManageChild(_armedMgr);
  XtManageChild(frame);
  
  frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, _baseWidget,
				  NULL);

  _statusMgr =
    XtVaCreateWidget("mgr", xmRowColumnWidgetClass, frame,
		     XmNorientation, XmVERTICAL,
		     NULL);

  TableRowForm *row = new TableRowForm("row", _statusMgr, 2);
  
  Widget label = 
    XtVaCreateManagedWidget("statusLabel", xmLabelWidgetClass, 
			    row->baseWidget(), NULL);
  
  _status = new DynamicLabel("status", row->baseWidget());
  _status->show();

  row->append(label);
  row->append(_status);
  row->show();
  
  XtManageChild(_statusMgr);

  char errorBuf[errorMsgSize];
  char itemName[MaxDmNameLen];
  char *namePtr;
  
  strcpy(itemName, dmPrefix);
  namePtr = itemName + strlen(itemName);
  
  strcpy(namePtr, dmArmCmdSuffix);
  _dmArmCmd = new DmNat16Object(itemName);
  if (_dmArmCmd->error())
  {
    _dmArmCmd->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (addDmObject(_dmArmCmd, DmProvideFlag) == -1)
    return;

  strcpy(namePtr, dmJogCmdSuffix);
  _dmJogCmd = new DmBooleanObject(itemName);
  if (_dmJogCmd->error())
  {
    _dmJogCmd->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmJogCmd, DmProvideFlag) == -1)
    return;

  strcpy(namePtr, dmStatusSuffix);
  _dmStatus = new DmEnumObject(itemName);
  if (_dmStatus->error())
  {
    _dmStatus->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmStatus, DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(Aperiodic) == -1)
    return;

  _armCode = TRUE;
  _flashPeriod = 500;
  _timerPhase = 0;
  _flashTimer = 0;
  
  XtVaGetValues(_baseWidget, XmNforeground, &_normalForeground,
		XmNbackground, &_normalBackground,
		XmNcolormap, &_colormap, 
		NULL);
  
  TiburonApp *app = (TiburonApp *)theApplication;
  app->priority1Colors(&_alertForeground, &_alertBackground);
  
  safe();
  
  updateGui();
}


ReleaseControl::~ReleaseControl()
{
  delete _status;
}


void ReleaseControl::updateGui(DM_Item handle)
{
  char errorBuf[errorMsgSize];
  Errno errno;
  DM_Time t;
  XmString xmstr;
  MBool debug = FALSE;
  Pixel foreground, background;
  char buf[100];

  TiburonApp *app = (TiburonApp *)theApplication;
  
  Int32 status;
  
  if ((errno = _dmStatus->read(&status, &t)) != SUCCESS)
  {
    _dmStatus->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  switch (status)
  {
    case LATCH_DISARMED:
    strcpy(buf, latchDisarmedMnem);
    app->okColors(&foreground, &background);
    break;
      
    case LATCH_ARMED:
    strcpy(buf, latchArmedMnem);
    app->priority1Colors(&foreground, &background);
    break;
      
    case LATCH_ARMED_NO_PWR:
    strcpy(buf, latchArmedNoPwrMnem);
    app->priority1Colors(&foreground, &background);
    break;
      
    case LATCH_RUNNING:
    strcpy(buf, latchRunningMnem);
    app->priority1Colors(&foreground, &background);
    break;

    default:
    strcpy(buf, "Unknown");
    app->priority1Colors(&foreground, &background);
  }
  _status->setText(buf);
  _status->setColors(foreground, background);


  if (status == LATCH_ARMED || status == LATCH_RUNNING)
    _ready = True;
  else
    _ready = False;
  
  if (_ready)
    XtVaSetValues(_armButton, XmNset, True, NULL);
  else
    XtVaSetValues(_armButton, XmNset, False, NULL);

  if (_ready)
    enableControls();
  else
    disableControls();

  safetyCheck();
}


void ReleaseControl::safetyCheck()
{
  Boolean safeSet;
  XtVaGetValues(_safeButton, XmNset, &safeSet, NULL);

  if (safeSet)
    safe();
  else
    unsafe();
}


void ReleaseControl::enableControls()
{
  XtSetSensitive(_armedMgr, True);

  // NOTE: No release function for now!
  XtSetSensitive(_releaseButton, False);

  Pixel borderColor, topColor, bottomColor, selectColor;
  
  XmGetColors(XtScreen(_baseWidget), _colormap, _alertBackground, 
	      &borderColor, 
	      &topColor, &bottomColor, &selectColor);

  XtVaSetValues(_releaseButton, 
		XmNforeground, _alertForeground,
		XmNbackground, _alertBackground,
		XmNtopShadowColor, topColor,
		XmNbottomShadowColor, bottomColor,
		XmNarmColor, selectColor, 
		NULL);
  
  if (_flashTimer)
  {
    XtRemoveTimeOut(_flashTimer);
    _flashTimer = 0;
  }

  _flashTimer = XtAppAddTimeOut(theApplication->appContext(), _flashPeriod,
				&ReleaseControl::timerCallback, 
				(XtPointer )this);

}


void ReleaseControl::disableControls()
{
  XtSetSensitive(_armedMgr, False);

  Pixel borderColor, topColor, bottomColor, selectColor;

  XmGetColors(XtScreen(_baseWidget), _colormap, _normalBackground, 
	      &borderColor, 
	      &topColor, &bottomColor, &selectColor);

  XtVaSetValues(_releaseButton, XmNforeground, _normalForeground,
		XmNbackground, _normalBackground, 
		XmNtopShadowColor, topColor,
		XmNbottomShadowColor, bottomColor,
		XmNarmColor, selectColor, 
		NULL);
  
  if (_flashTimer)
  {
    XtRemoveTimeOut(_flashTimer);
    _flashTimer = 0;
  }
}


void ReleaseControl::flash()
{
  Pixel foreground, background;
  
  if (_timerPhase)
  {
    _timerPhase = 0;
    foreground = _normalForeground;
    background = _normalBackground;
  }
  else
  {
    _timerPhase = 1;
    foreground = _alertForeground;
    background = _alertBackground;
  }

  _status->setColors(foreground, background);

  _flashTimer = XtAppAddTimeOut(theApplication->appContext(), _flashPeriod,
				&ReleaseControl::timerCallback, 
				(XtPointer )this);
}


void ReleaseControl::safe()
{
  XtSetSensitive(_armButton, False);
  XtSetSensitive(_armedMgr, False);
}


void ReleaseControl::unsafe()
{
  XtSetSensitive(_armButton, True);
  if (_ready)
    enableControls();
  else
    disableControls();
}


void ReleaseControl::safeCallback(Widget w,
				  XtPointer clientData,
				  XtPointer callData)
{
  ReleaseControl *obj = (ReleaseControl *)clientData;

  XmToggleButtonCallbackStruct *cb = 
    (XmToggleButtonCallbackStruct *)callData;

  if (cb->set)
    obj->safe();
  else
    obj->unsafe();
}


void ReleaseControl::armCallback(Widget w,
				 XtPointer clientData,
				 XtPointer callData)
{
  ReleaseControl *obj = (ReleaseControl *)clientData;

  Nat16 armValue;

  XmToggleButtonCallbackStruct *cb = 
    (XmToggleButtonCallbackStruct *)callData;
  
  if (cb->set)
    armValue = obj->_armCode;
  else
    armValue = 0;
  
  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if ((errno = obj->_dmArmCmd->write(armValue, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ReleaseControl::armCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }
}


void ReleaseControl::jogCallback(Widget w,
				 XtPointer clientData,
				 XtPointer callData)
{
  ReleaseControl *obj = (ReleaseControl *)clientData;

  XmPushButtonCallbackStruct *cb = 
    (XmPushButtonCallbackStruct *)callData;
  
  MBool jog;
  
  if (cb->reason == XmCR_ARM)
    jog = TRUE;
  else
    jog = FALSE;
  
  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if ((errno = obj->_dmJogCmd->write(jog, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ReleaseControl::jogCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }
}


void ReleaseControl::timerCallback(XtPointer clientData,
				   XtIntervalId *id)
{
  ReleaseControl *obj = (ReleaseControl *)clientData;
  obj->flash();

}

