
static char DmStripChart_id[] = "$Header: /usr/tiburon/unix/gui/tmacs/RCS/DmStripChart.cc,v 1.2 1997/09/10 08:49:15 oreilly Exp $";
#include <values.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/Separator.h>
#include <Xm/ToggleB.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include "DmStripChart.h"
#include "DmErrno.h"
#include <mbari/param.h>

#define dprintf if (debug) fprintf

XtResource DmStripChart::_resources[] = 
{
  {
    "dataColor",
    "DataColor",
    XmRString,
    sizeof(XmRString),
    XtOffset(DmStripChart *, _dataColor),
    XmRString, 
    "green"
  }
};


DmStripChart::DmStripChart(const char *name, Widget parent,
			   const char *itemName,
			   unsigned historyMinutes,
			   int nDataSets)

  : DmGuiObject(name, parent)
{
  _points = NULL;
  _xrtData = NULL;
  
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, 
				 NULL);
  
  installDestroyHandler();
  getResources(_resources, XtNumber(_resources));
  
  char errorBuf[errorMsgSize];

  if ((_dmObject = createDmObject(itemName, errorBuf)) == NULL)
  {
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmObject, DmConsumeFlag) == -1)
    return;

  Period period = 500;
  
  if (setConsumePeriod((int )period) == -1)
    return;

  int totalPoints = historyMinutes * 60 * 1000 / period;
  if (!totalPoints)
  {
    sprintf(errorBuf, "Bad historyMinutes (%d) and/or period (%d)",
	    historyMinutes, period);

    setError(errorBuf);
    return;
  }
  int nbytes = totalPoints * sizeof(Point);

  _points = new CircularQueue<Point>(totalPoints);
  if (_points->error())
  {
    _points->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if ((_xrtData = XrtMakeData(XRT_DATA_ARRAY, nDataSets, totalPoints, True)) 
      == NULL)
  {
    sprintf(errorBuf, 
	    "XrtMakeData() failed for _xrtData (%d points)", totalPoints);

    setError(errorBuf);
    return;
  }
  float xmax = historyMinutes;

  _dataStyle.lpat = XRT_LPAT_SOLID;
  _dataStyle.fpat = XRT_FPAT_SOLID;
  _dataStyle.psize = 3;
  _dataStyle.point = XRT_POINT_NONE;
    
  _dataStyle.color = _dataColor;
  _dataStyle.pcolor = _dataColor;
  _dataStyle.width = 2;
    
  XrtDataStyle *dataStylesPtr[2];
  dataStylesPtr[0] = &_dataStyle;
  dataStylesPtr[1] = NULL;
  
  _graph = 
    XtVaCreateManagedWidget("graph", xtXrtGraphWidgetClass, _baseWidget,
			    XtNxrtType, XRT_TYPE_PLOT,
			    XtNxrtXAxisMin, XrtFloatToArgVal(0.),
			    XtNxrtXAxisMax, XrtFloatToArgVal(xmax),
			    XtNxrtXNumMethod, XRT_NUM_PRECISION,
			    XtNxrtXPrecision, 0,
			    XtNxrtXNum, XrtFloatToArgVal(1.0),
			    XtNxrtXGrid, XrtFloatToArgVal(1.0),
			    XtNxrtXTick, XrtFloatToArgVal(0.5),
			    XtNxrtYPrecision, 0,
			    XtNxrtDataStyles, dataStylesPtr,
			    XtNxrtXTitle, "Minutes",
			    NULL);

  XtVaSetValues(_graph, 
		XmNleftAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_FORM,
		XmNtopAttachment, XmATTACH_FORM,
		XmNbottomAttachment, XmATTACH_FORM,
		NULL);

  updateGui();

  // Since DmStripChart displays data as a function of time, we
  // want to always consume, to avoid gaps in data.
  setStickyConsumer(True);
}


DmStripChart::~DmStripChart()
{
  Boolean debug = False;
  
  dprintf(stderr, "DmStripChart::~DmStripChart()\n");
  
  if (_points)
    delete _points;

  if (_xrtData)
    XrtDestroyData(_xrtData, True);
}


void DmStripChart::createDataArrays()
{
  
}


void DmStripChart::updateGui(DM_Item handle)
{
  Boolean debug = False;
  DM_Time t;
  Point point;
  char errorBuf[errorMsgSize];

  /* Note that "time" is time right now, rather than time at which Dm items
     were written; this insures that depth and altitude use same time axis */
  time_t now = point.time = time(NULL);
  Errno errno;

  if ((errno = readDmObject(_dmObject, &point.value, &t)) != SUCCESS)
  {
    sprintDmError(errno, 
		  "DmStripChart::updateGui(), error reading value", 
		  errorBuf);

    setError(errorBuf);
    point.value = MissingValue;
    return;
  }

  _points->add(&point);
  
  // Update xrtData structure
  _points->startIter();
  Point *pnt;

  Flt64 yMax = -MAXFLOAT;
  Flt64 yMin = MAXFLOAT;
  
  int nRealPoints = 0;
  
  for (int i = 0; i < _points->maxQueueSize(); i++)
  {
    if (pnt = _points->next())
    {
      _xrtData->arr_xel(i) = (now - pnt->time) / 60.;

      _xrtData->arr_yel(0, i) = pnt->value;

      if (pnt->value > yMax)
	yMax = pnt->value;
      if (pnt->value < yMin)
	yMin = pnt->value;

      nRealPoints++;
    }
    else
    {
      _xrtData->arr_yel(0, i) = MissingValue;
    }
  }

  if (nRealPoints)
  {
    _yAxisMin = yMin;
    _yAxisMax = yMax;
  }
  else
  {
    _yAxisMin = 0.;
    _yAxisMax = 1.;
  }
  
  char modeBuf[100];

  // Update graph Widget
  XtVaSetValues(_graph,
		XtNxrtData, _xrtData,
		XtNxrtYAxisMin, XrtFloatToArgVal(_yAxisMin),
		XtNxrtYAxisMax, XrtFloatToArgVal(_yAxisMax),  
		NULL);
}





void DmStripChart::rescaleYAxis(unsigned newRange)
{
  _yAxisRange = newRange;
  XtVaSetValues(_graph, 
		XtNxrtYNum, XrtFloatToArgVal(newRange / 5.),
		XtNxrtYTick, XrtFloatToArgVal(newRange / 10.),
		NULL);
  
  _yAxisMin = MAXFLOAT;
  _yAxisMax = -MAXFLOAT;
  updateGui();
}


void DmStripChart::buttonEventHandler(Widget w, 
				      XtPointer clientData, 
				      XEvent *event)
{
  Boolean debug = False;
  dprintf(stderr, "DmStripChart::buttonEventCallback()\n");
  DmStripChart *obj = (DmStripChart *)clientData;
}



DmObject *createDmObject(const char *itemName, char *errorBuf)
{
  DM_Item dmItem = NULL;
  Boolean error = False;

  if (!itemName)
  {
    strcpy(errorBuf, "NULL itemName");
    return NULL;
  }

  dmItem = dm_lookup((char *)itemName, 0);
  if (!dmItem)
  {
    sprintf(errorBuf, "Item \"%s\" not found", itemName);
    return NULL;
  }


  DmItemSts status;
  if (dm_item_status(dmItem, &status) != SUCCESS)
  {
    theErrorDialog->postAndWait("ValueBox::findItem() - "
				"dm_item_status() failed\n");
    
    return NULL;
  }

  DM_Type dmType = status.is_type;
  
  dm_item_status_free(&status);

  DmObject *dmObject;
  
  switch (dmType)
  {
    case DM_INT16:
    dmObject = new DmInt16Object(itemName);
    break;
    
    case DM_NAT16:
    dmObject = new DmNat16Object(itemName);
    break;
    
    case DM_INT32:
    dmObject = new DmInt32Object(itemName);
    break;
    
    case DM_NAT32:
    dmObject = new DmNat32Object(itemName);
    break;
    
    case DM_FLT32:
    dmObject = new DmFlt32Object(itemName);
    break;
    
    case DM_FLT64:
    dmObject = new DmFlt64Object(itemName);
    break;

    default:
    char buf[100];
    DmObject::sprintDataType(dmType, buf);
      
    sprintf(errorBuf, "\"%s\", type %s is not supported\n",
	    itemName, buf);
    
    return NULL;
  }

  return dmObject;
}


Errno readDmObject(DmObject *dmObject, Flt64 *value, DM_Time *t)
{
  switch (dmObject->objectType())
  {
    case DmInt16Type:
    Int16 int16Value;
    errno = ((DmInt16Object *)dmObject)->read(&int16Value, t);
    *value = int16Value;
    break;
    
    case DmNat16Type:
    Nat16 nat16Value;
    errno = ((DmNat16Object *)dmObject)->read(&nat16Value, t);
    *value = nat16Value;
    break;
    
    case DmInt32Type:
    Int32 int32Value;
    errno = ((DmInt32Object *)dmObject)->read(&int32Value, t);
    *value = int32Value;
    break;
    
    case DmNat32Type:
    Nat32 nat32Value;
    errno = ((DmNat32Object *)dmObject)->read(&nat32Value, t);
    *value = nat32Value;
    break;
    
    case DmFlt32Type:
    Flt32 flt32Value;
    errno = ((DmFlt32Object *)dmObject)->read(&flt32Value, t);
    *value = flt32Value;
    break;
    
    case DmFlt64Type:
    errno = ((DmFlt64Object *)dmObject)->read(value, t);
    break;

    default:
    errno = EDM_GENERAL;
  }

  return errno;
}

