#include <Xm/Form.h>
#include <Xm/PushB.h>
#include "TiburonApp.h"
#include "HeartBeat.h"
#include "DmErrno.h"

#define dprintf if (debug) fprintf

HeartBeat::HeartBeat(const char *name, Widget parent, const char *dmItemName,
		     int period)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();

  if (period <= 0)
  {
    setError("Invalid heartbeat period");
    return;
  }
  
  _beatsPerSec = 1. / period * 1000.;
  
  char labelName[100];
  sprintf(labelName, "%sLabel", name);
  
  Widget label = 
    XtVaCreateManagedWidget(labelName, xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
			   
  int screen = DefaultScreen(theApplication->display());
  int depth = XDisplayPlanes(theApplication->display(), screen);

  _pixmapWidth = _pixmapHeight = 10;
  
  _okPixmap = 
    XCreatePixmap(theApplication->display(), 
		  RootWindow(theApplication->display(), screen),
		  _pixmapWidth, _pixmapHeight,
		  depth);

  _okPixmap2 = 
    XCreatePixmap(theApplication->display(), 
		  RootWindow(theApplication->display(), screen),
		  _pixmapWidth, _pixmapHeight,
		  depth);

  _errorPixmap = 
    XCreatePixmap(theApplication->display(), 
		  RootWindow(theApplication->display(), screen),
		  _pixmapWidth, _pixmapHeight,
		  depth);

  XGCValues gcValues;
  
  GC gc = XCreateGC(theApplication->display(), 
		    RootWindow(theApplication->display(), screen),
		    0, &gcValues);

  Pixel foreground, background;

  ((TiburonApp *)theApplication)->powerOnColors(&foreground, &background);

  XSetForeground(theApplication->display(), gc, background);
  XFillRectangle(theApplication->display(), _okPixmap, gc, 0, 0,
		 _pixmapWidth, _pixmapHeight);

  XSetForeground(theApplication->display(), gc, background);
  XFillRectangle(theApplication->display(), _okPixmap2, gc, 0, 0,
		 _pixmapWidth, _pixmapHeight);


  ((TiburonApp *)theApplication)->priority1Colors(&foreground, &background);

  XSetForeground(theApplication->display(), gc, background);
  XFillRectangle(theApplication->display(), _errorPixmap, gc, 0, 0,
		 _pixmapWidth, _pixmapHeight);


  _statusLight = 
    XtVaCreateManagedWidget("statusLight", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, label,
			    XmNrightAttachment, XmATTACH_FORM,
			    XmNlabelType, XmPIXMAP,
			    XmNwidth, _pixmapWidth,
			    XmNheight, _pixmapHeight,
			    XmNlabelPixmap, _okPixmap,
			    NULL);

  XtAddEventHandler(_statusLight,
		    ExposureMask,
		    False,
		    (XtEventHandler )&HeartBeat::exposeCallback,
		    (XtPointer )this);

  _dmHeartBeat = new DmNat32Object(dmItemName);
  if (_dmHeartBeat->error())
  {
    char errorBuf[errorMsgSize];
    _dmHeartBeat->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (addDmObject(_dmHeartBeat, DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(period) == -1)
    return;
 
  _inError = False;
  _phase = 0;
  _first = True;
}


HeartBeat::~HeartBeat()
{
  XFreePixmap(theApplication->display(), _okPixmap);
  XFreePixmap(theApplication->display(), _okPixmap2);
  XFreePixmap(theApplication->display(), _errorPixmap);
}


void HeartBeat::updateGui(DM_Item handle)
{
  Nat32 count;
  DM_Time t;
  Errno errno;
  Boolean debug = False;
  static struct timezone tz =
  {
    0, 0
  };
  
  
  struct timeval readTime;
  gettimeofday(&readTime, &tz);
  
  if ((errno = _dmHeartBeat->read(&count, &t)) != SUCCESS)
  {
    char errorBuf[errorMsgSize];
    sprintDmError(errno, 
		  "updateGui() - error reading heartbeat",
		  errorBuf);

    setError(errorBuf);
    return;
  }
  
  Pixmap pixmap;

  int diff = count - _prevCount;

  double secs = readTime.tv_sec + readTime.tv_usec / 1e6;
  double beats = (secs - _prevSecs) * _beatsPerSec;

  if (!strcmp(_name, "vehicleHeartBeat"))
  {
    dprintf(stderr, "HeartBeat of \"%s\": count=%d, prevCount=%d, beats=%d\n", 
	    _dmHeartBeat->getName(), count, _prevCount, beats);
  }
  
  if (!_first && (diff == 0 || diff < beats - HeartBeatSkipTolerance ||
		  diff > beats + HeartBeatSkipTolerance))
  {
    // Set indicator light background
    pixmap = _errorPixmap;  
    
    if (!_inError)
    {
      _inError = True;
    }
  }
  else
  {
    if (_phase == 0)
    {
      pixmap = _okPixmap;
      _phase = 1;
    }
    else
    {
      pixmap = _okPixmap2;
      _phase = 0;
    }

    _prevOkTime = t.tv_sec; 
    
    _inError = False;
  }
  
  XtVaSetValues(_statusLight, XmNlabelPixmap, pixmap, NULL);

  _prevSecs = secs;
  _first = False;
  _prevCount = count;
}


void HeartBeat::ackButtonReset()
{
  Pixel foreground, background;
  XtVaGetValues(_baseWidget, 
		XmNforeground, &foreground,
		XmNbackground, &background,
		NULL);
  
  XtVaSetValues(_ackButton,
		XmNforeground, foreground,
		XmNbackground, background,
		XmNsensitive, False,
		NULL);
}


void HeartBeat::exposeCallback(Widget w, XtPointer clientData,
			       XEvent *event)
{
  HeartBeat *obj = (HeartBeat *)clientData;
}


void HeartBeat::ackButtonCallback(Widget w, 
				  XtPointer clientData, XtPointer callData)
{
  HeartBeat *obj = (HeartBeat *)clientData;
  obj->ackButtonReset();
}

