#include <xpm.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/Label.h>
#include <Xm/Frame.h>
#include <Xm/Form.h>
#include <Vk/VkErrorDialog.h>
#include <Vk/VkPromptDialog.h>
#include <Vk/VkQuestionDialog.h>
#include <Vk/VkInfoDialog.h>
#include "FireControl.h"
#include "tiburon.h"
#include "ersDcon.h"
#include "ers.h"
#include "TiburonApp.h"
#include "DmErrno.h"
#include "fireCover.xpm"

const char *const FireControl::disarmedMnem = "Disarmed";
const char *const FireControl::armedMnem = "ARMED";
const char *const FireControl::fastFiringMnem = "FAST-FIRING";
const char *const FireControl::slowFiringMnem = "SLOW-FIRING?";
const char *const FireControl::firedMnem = "Fired";
const char *const FireControl::unknownMnem = "UNKNOWN!";


FireControl::FireControl(const char *name, Widget parent, 
			 FireControlDm *dmObjects)
  : DmGuiObject(name, parent)
{
  _dmObjects = dmObjects;

  _status = 0;
  
  _baseWidget = 
    XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);

  installDestroyHandler();

  Widget label = XtVaCreateManagedWidget("fireControlLabel", 
					 xmLabelWidgetClass, _baseWidget,
					 XmNleftAttachment, XmATTACH_FORM,
					 XmNrightAttachment, XmATTACH_FORM,
					 XmNtopAttachment, XmATTACH_FORM,
					 NULL);

  Widget statusLabel = 
    XtVaCreateManagedWidget("statusLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, label,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
  
  _status = new DynamicLabel("status", _baseWidget);
  XtVaSetValues(_status->baseWidget(), 
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, label,
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, statusLabel,
		NULL);

  _status->show();

  _pressureSwitch = new DmBooleanIndicator("pressureSwitch", _baseWidget,
					   _dmObjects->pressureSwitchStatus);

  XtVaSetValues(_pressureSwitch->baseWidget(), 
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, label,
		XmNrightAttachment, XmATTACH_FORM,
		NULL);

  _pressureSwitch->show();

  Layout layout;

  // Control cover
  _coveredView = new View("coveredView", _baseWidget);

  XtVaSetValues(_coveredView->baseWidget(),
		XmNorientation, XmHORIZONTAL, 
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, statusLabel,
		XmNbottomAttachment, XmATTACH_FORM,
		NULL);

  // Set covered pixmap
  XpmAttributes xpmAttributes;

  xpmAttributes.closeness = 40000;
  xpmAttributes.valuemask = XpmSize | XpmCloseness;
  
  Pixmap mask;
  
  if (XpmCreatePixmapFromData(theApplication->display(), 
			      DefaultRootWindow(theApplication->display()),
			      fireCover_xpm,
			      &_fireCoverPixmap,
			      &mask,
			      &xpmAttributes) != XpmSuccess)
  {
    _fireCoverPixmap = NULL;
  }

  
  Widget uncoverButton = 
    XtVaCreateManagedWidget("uncoverButton", xmPushButtonWidgetClass, 
			    _coveredView->baseWidget(),
			    XmNlabelType, XmPIXMAP,
			    XmNlabelPixmap, _fireCoverPixmap,
			    XmNwidth, 232, XmNheight, 165,
			    NULL);

  XtAddCallback(uncoverButton, XmNactivateCallback,
		&FireControl::uncoverButtonCallback,
		(XtPointer )this);

  layout.reset();
  layout.attachToEdge(Left);
  layout.attachToEdge(Right);
  layout.attachToEdge(Top);
  layout.attachToEdge(Bottom);

  _coveredView->add(uncoverButton, &layout);

  // Controls/displays
  _uncoveredView = new View("uncoveredView", _baseWidget);
  XtVaSetValues(_uncoveredView->baseWidget(),
		XmNorientation, XmHORIZONTAL, 
		XmNleftAttachment, XmATTACH_FORM,
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, statusLabel,
		XmNbottomAttachment, XmATTACH_FORM,
		NULL);
  
  _controlsMgr =
    XtVaCreateWidget("controlsMgr", xmRowColumnWidgetClass, 
		     _uncoveredView->baseWidget(),
		     XmNorientation, XmVERTICAL,
		     NULL);

  layout.reset();
  layout.attachToEdge(Left);
  layout.attachToEdge(Right);
  layout.attachToEdge(Top);
  layout.attachToEdge(Bottom);

  _uncoveredView->add(_controlsMgr, &layout);

  Widget frame;

  frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, _controlsMgr,
				  NULL);
  
  Widget mgr = 
    XtVaCreateWidget("mgr", xmFormWidgetClass, frame, NULL);

  _disarmButton = 
    XtVaCreateManagedWidget("disarmButton", xmPushButtonWidgetClass, mgr,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(_disarmButton, XmNactivateCallback,
		&FireControl::disarmCallback,
		(XtPointer )this);

  _armButton = 
    XtVaCreateManagedWidget("armButton", xmPushButtonWidgetClass, mgr,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _disarmButton,
			    NULL);

  XtAddCallback(_armButton, XmNactivateCallback,
		&FireControl::armCallback,
		(XtPointer )this);


  TiburonApp *app = (TiburonApp *)theApplication;
  app->priority1Colors(&_alertForeground, &_alertBackground);
  
  _fireButton = 
    XtVaCreateManagedWidget("fireButton", xmPushButtonWidgetClass, mgr,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _armButton,
			    XmNforeground, _alertForeground,
			    XmNbackground, _alertBackground,
			    NULL);

  XtAddCallback(_fireButton, XmNactivateCallback,
		&FireControl::fireCallback,
		(XtPointer )this);

  _safeButton =
    XtVaCreateManagedWidget("safeButton", xmPushButtonWidgetClass, mgr,
			    XmNrightAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(_safeButton, XmNactivateCallback,
		&FireControl::safeCallback,
		(XtPointer )this);

  XtManageChild(mgr);
  
  XtManageChild(frame);
  
  XtManageChild(_controlsMgr);

  char errorBuf[errorMsgSize];

  char itemName[MaxDmNameLen];
  char *namePtr;
  
  if (addDmObject(_dmObjects->disarm, DmProvideFlag) == -1)
    return;

  if (addDmObject(_dmObjects->arm, DmProvideFlag) == -1)
    return;

  if (addDmObject(_dmObjects->armPassword, DmProvideFlag) == -1)
    return;

  if (addDmObject(_dmObjects->fastFire, DmProvideFlag) == -1)
    return;

  if (addDmObject(_dmObjects->firePassword, DmProvideFlag) == -1)
    return;

  if (addDmObject(_dmObjects->state, DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(Aperiodic) == -1)
    return;

  _flashPeriod = 500;
  _timerPhase = 0;
  _flashTimer = 0;
  
  XtVaGetValues(_baseWidget, XmNforeground, &_normalForeground,
		XmNbackground, &_normalBackground,
		XmNcolormap, &_colormap, 
		NULL);
  
  safe();
  
  updateGui();
}


FireControl::~FireControl()
{
  delete _status;
  delete _pressureSwitch;
  free((void *)_fireCoverPixmap);
}


void FireControl::updateGui(DM_Item handle)
{
  char errorBuf[errorMsgSize];
  Errno errno;
  DM_Time t;
  XmString xmstr;
  MBool debug = FALSE;
  char buf[100];

  TiburonApp *app = (TiburonApp *)theApplication;
  
  Int32 status;
  
  if ((errno = _dmObjects->state->read(&status, &t)) != SUCCESS)
  {
    _dmObjects->state->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  Boolean flash = False;
  Pixel foreground, background;

  switch (status)
  {
  case FireControlDm::Disarmed:
    strcpy(buf, disarmedMnem);
    XtSetSensitive(_disarmButton, False);
    XtSetSensitive(_armButton, True);
    XtSetSensitive(_fireButton, False);
    foreground = _normalForeground;
    background = _normalBackground;
    break;
      
  case FireControlDm::Armed:
    strcpy(buf, armedMnem);
    XtSetSensitive(_disarmButton, True);
    XtSetSensitive(_armButton, False);
    XtSetSensitive(_fireButton, True);
    foreground = _alertForeground;
    background = _alertBackground;
    break;
      
  case FireControlDm::FastFiring:
    strcpy(buf, fastFiringMnem);
    flash = True;
    XtSetSensitive(_disarmButton, True);
    XtSetSensitive(_armButton, False);
    XtSetSensitive(_fireButton, False);
    foreground = _alertForeground;
    background = _alertBackground;
    break;

  case FireControlDm::SlowFiring:
    strcpy(buf, slowFiringMnem);
    flash = True;
    XtSetSensitive(_disarmButton, True);
    XtSetSensitive(_armButton, False);
    XtSetSensitive(_fireButton, True);
    foreground = _alertForeground;
    background = _alertBackground;
    break;

  case FireControlDm::Fired:
    strcpy(buf, firedMnem);
    XtSetSensitive(_disarmButton, True);
    XtSetSensitive(_armButton, False);
    XtSetSensitive(_fireButton, False);
    foreground = _normalForeground;
    background = _normalBackground;
    break;

  case FireControlDm::Unknown:
    strcpy(buf, unknownMnem);
    flash = True;
    XtSetSensitive(_disarmButton, True);
    XtSetSensitive(_armButton, True);
    XtSetSensitive(_fireButton, False);
    foreground = _alertForeground;
    background = _alertBackground;
    break;

  default:
    sprintf(buf, "VALUE=%d???", status);
    foreground = _alertForeground;
    background = _alertBackground;
  }

  _status->setText(buf);
  _status->setColors(foreground, background);

  if (_flashTimer)
  {
    XtRemoveTimeOut(_flashTimer);
    _flashTimer = 0;
  }

  if (flash) {
    _flashTimer = XtAppAddTimeOut(theApplication->appContext(), _flashPeriod,
				  &FireControl::timerCallback, 
				  (XtPointer )this);
  }
}



void FireControl::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,
				&FireControl::timerCallback, 
				(XtPointer )this);
}


void FireControl::safe()
{
  // Close the "cover"
  _uncoveredView->hide();
  _coveredView->show();
}


void FireControl::unsafe()
{
  // Open the "cover"
  _coveredView->hide();
  _uncoveredView->show();
}


void FireControl::safeCallback(Widget w,
			       XtPointer clientData,
			       XtPointer callData)
{
  FireControl *obj = (FireControl *)clientData;

  // Close the "cover"
  obj->safe();
}



void FireControl::fireCallback(Widget w,
			       XtPointer clientData,
			       XtPointer callData)
{
  Boolean debug = False;

  // "Fire" button was pushed
  FireControl *obj = (FireControl *)clientData;

  if (thePromptDialog->postAndWait("Enter firing password:") != 
      VkDialogManager::OK)
    // Canceled
    return;

  if (thePromptDialog->text() && 
      strcmp(thePromptDialog->text(), FirePassword)) {
    theErrorDialog->postAndWait("Invalid firing password entered");
    return;
  }

  theQuestionDialog->setButtonLabels("FIRE!",
				     "Cancel firing",
				     0);

  if (theQuestionDialog->postAndWait("Are you sure you want to fast-fire?") !=
      VkDialogManager::OK) {

    theInfoDialog->postAndWait("Fast-firing cancelled.");
    theQuestionDialog->setButtonLabels(0, 0, 0);

    return;
  }

  theQuestionDialog->setButtonLabels(0, 0, 0);

  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];

  t.tv_sec = time(NULL);
  t.tv_usec = 0;

  char *password = (char *)thePromptDialog->text();

  if ((errno = obj->_dmObjects->firePassword->write(password, &t)) != SUCCESS)
  {
    sprintDmError(errno, "FireControl::fireCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }

  if ((errno = obj->_dmObjects->fastFire->write(&t)) != SUCCESS)
  {
    sprintDmError(errno, "FireControl::fireCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }
}


void FireControl::armCallback(Widget w,
			      XtPointer clientData,
			      XtPointer callData)
{
  char buf[100];
  FireControl *obj = (FireControl *)clientData;

  // "Arm" button was pushed
  sprintf(buf, "Enter arming password (\"%s\"):", ArmPassword);

  if (thePromptDialog->postAndWait(buf) != VkDialogManager::OK)
    // Canceled
    return;

  if (thePromptDialog->text() && 
      strcmp(thePromptDialog->text(), ArmPassword)) {
    theErrorDialog->postAndWait("Invalid arming password entered");
    return;
  }

  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  char *password = (char *)thePromptDialog->text();
  if ((errno = obj->_dmObjects->armPassword->write(password, &t)) 
      != SUCCESS) {
    sprintDmError(errno, "FireControl::armCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }

  if ((errno = obj->_dmObjects->arm->write(&t)) != SUCCESS) {
    sprintDmError(errno, "FireControl::armCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }
}


void FireControl::disarmCallback(Widget w,
				 XtPointer clientData,
				 XtPointer callData)
{
  char buf[100];
  FireControl *obj = (FireControl *)clientData;

  // "Disarm" button was pushed
  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if ((errno = obj->_dmObjects->disarm->write(&t)) != SUCCESS) {
    sprintDmError(errno, "FireControl::disarmCallback()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }
}




void FireControl::uncoverButtonCallback(Widget w,
					XtPointer clientData,
					XtPointer callData)
{
  FireControl *obj = (FireControl *)clientData;

  // Open the "cover"
  obj->unsafe();
}


void FireControl::timerCallback(XtPointer clientData,
				XtIntervalId *id)
{
  FireControl *obj = (FireControl *)clientData;
  obj->flash();
}


