#include <stdio.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h> 
#include "Vk/VkErrorDialog.h"
#include "AdvLogger.h"
#include "dopplerDM.h"
#include "DmErrno.h"
#include "ExternalApp.h"
#include "TiburonApp.h"
#include "advDataLog.h"
#define dprintf if (debug) fprintf

#define InvalidMnem "Invalid"

AdvLogger::AdvLogger(const char *name, Widget parent, int period)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  Widget label;

  _waterMgr = XtVaCreateWidget("waterMgr", xmFormWidgetClass, _baseWidget, 
			       XmNtopAttachment, XmATTACH_FORM,
			       XmNleftAttachment, XmATTACH_FORM,
			       XmNrightAttachment, XmATTACH_FORM,
			       NULL);

  label = 
    XtVaCreateManagedWidget("speedLabel", xmLabelWidgetClass, _waterMgr,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_FORM,
			    NULL);
  
  _speed = new TextField("speed", _waterMgr, False); 
  _speed->setColumns(4);
  
  XtVaSetValues(_speed->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label, 
		XmNtopAttachment, XmATTACH_FORM,
		NULL);

  _speed->show();
  
  label = 
    XtVaCreateManagedWidget("directionLabel", xmLabelWidgetClass, _waterMgr,
			    XmNleftAttachment, XmATTACH_POSITION,
			    XmNleftPosition, 50,
			    XmNtopAttachment, XmATTACH_FORM,
			    NULL);

  _direction = new TextField("direction", _waterMgr, False);
  _direction->setColumns(4);

  XtVaSetValues(_direction->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label, 
		XmNtopAttachment, XmATTACH_FORM,
		NULL);

  _direction->show();
  
  XtManageChild(_waterMgr);
  
  _grndMgr = XtVaCreateWidget("grndMgr", xmFormWidgetClass, _baseWidget, 
			      XmNtopAttachment, XmATTACH_WIDGET,
			      XmNtopWidget, _waterMgr,
			      XmNleftAttachment, XmATTACH_FORM,
			      XmNrightAttachment, XmATTACH_FORM,
			      NULL);
  label = 
    XtVaCreateManagedWidget("grndSpeedLabel", xmLabelWidgetClass, _grndMgr,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_FORM,
			    NULL);
  
  _grndSpeed = new TextField("grndSpeed", _grndMgr, False);
  _grndSpeed->setColumns(4);
  
  XtVaSetValues(_grndSpeed->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label, 
		XmNtopAttachment, XmATTACH_FORM,
		NULL);

  _grndSpeed->show();
  
  label = 
    XtVaCreateManagedWidget("grndDirectionLabel", xmLabelWidgetClass, 
			    _grndMgr,
			    XmNleftAttachment, XmATTACH_POSITION,
			    XmNleftPosition, 50,
			    XmNtopAttachment, XmATTACH_FORM,
			    NULL);

  _grndDirection = new TextField("grndDirection", _grndMgr, False);
  _grndDirection->setColumns(4);
  
  XtVaSetValues(_grndDirection->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label, 
		XmNtopAttachment, XmATTACH_FORM,
		NULL);

  _grndDirection->show();
  
  XtManageChild(_grndMgr);
  
  _logToFile = 
    XtVaCreateManagedWidget("logToFile", xmToggleButtonWidgetClass, 
			    _baseWidget,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _grndMgr,
			    NULL);
  
  XtAddCallback(_logToFile, XmNvalueChangedCallback, 
		&AdvLogger::logToFileCallback, (XtPointer )this);

  Widget mgr = 
    XtVaCreateWidget("mgr", xmRowColumnWidgetClass, _baseWidget,
		     XmNorientation, XmHORIZONTAL,
		     XmNleftAttachment, XmATTACH_WIDGET,
		     XmNleftWidget, _logToFile,
		     XmNtopAttachment, XmATTACH_WIDGET,
		     XmNtopWidget, _grndMgr,
		     NULL);
  
  _start = 
    XtVaCreateManagedWidget("start", xmPushButtonWidgetClass, mgr, NULL);

  XtAddCallback(_start, XmNactivateCallback, 
		&AdvLogger::startStopCallback, (XtPointer )this);
  
  _stop = 
    XtVaCreateManagedWidget("stop", xmPushButtonWidgetClass, mgr, NULL);

  XtAddCallback(_stop, XmNactivateCallback, 
		&AdvLogger::startStopCallback, (XtPointer )this);

  XtManageChild(mgr);
  
  _reset = 
    XtVaCreateManagedWidget("reset", xmPushButtonWidgetClass,
			    _baseWidget,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, mgr,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _grndMgr,
			    NULL);

  XtAddCallback(_reset, XmNactivateCallback, 
		&AdvLogger::startStopCallback, (XtPointer )this);


  label = 
    XtVaCreateManagedWidget("timeLabel", xmLabelWidgetClass, _baseWidget,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _logToFile,
			    NULL);

  _elapsedTime = new TextField("elapsedTime", _baseWidget, False);
  _elapsedTime->setColumns(5);

  XtVaSetValues(_elapsedTime->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label,
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _logToFile,
		NULL);

  _elapsedTime->show();
  
  label = 
    XtVaCreateManagedWidget("distanceLabel", xmLabelWidgetClass, _baseWidget,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _elapsedTime->baseWidget(),
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _logToFile,
			    NULL);

  _distance = new TextField("distance", _baseWidget, False);
  _distance->setColumns(6);
  
  XtVaSetValues(_distance->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label,
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _logToFile,
		NULL);

  _distance->show();
  
  label = 
    XtVaCreateManagedWidget("totalDistanceLabel", xmLabelWidgetClass, 
			    _baseWidget,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _distance->baseWidget(),
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _logToFile,
			    NULL);

  _totalDistance = new TextField("totalDistance", _baseWidget, False);
  _totalDistance->setColumns(6);
  
  XtVaSetValues(_totalDistance->baseWidget(),
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, label,
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _logToFile,
		NULL);

  _totalDistance->show();

  char errorBuf[errorMsgSize];
  
  _dmStart = new DmBooleanObject(DOPPLER_START_DISTANCE_LOG_DM);
  if (_dmStart->error())
  {
    _dmStart->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmStart, DmProvideFlag | DmConsumeFlag) == -1)
    return;
  
  _dmStop = new DmBooleanObject(DOPPLER_STOP_DISTANCE_LOG_DM);
  if (_dmStop->error())
  {
    _dmStop->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmStop, DmProvideFlag | DmConsumeFlag) == -1)
    return;

  _dmStopLogToFile = new DmBooleanObject(DOPPLER_STOP_DATA_LOG_DM);
  if (_dmStopLogToFile->error())
  {
    _dmStopLogToFile->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmStopLogToFile, DmProvideFlag | DmConsumeFlag) == -1)
    return;
  
  _dmSpeed = new DmFlt32Object(DOPPLER_WATER_MASS_CURRENT_DM);
  if (_dmSpeed->error())
  {
    _dmSpeed->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmSpeed, DmConsumeFlag) == -1)
    return;

  _dmDirection = new DmFlt32Object(DOPPLER_WATER_MASS_DIRECTION_DM);
  if (_dmDirection->error())
  {
    _dmDirection->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmDirection, DmConsumeFlag) == -1)
    return;

  _dmValid = new DmBooleanObject(DOPPLER_WATER_MASS_DATA_VALID_DM);
  if (_dmValid->error())
  {
    _dmValid->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmValid, DmConsumeFlag) == -1)
    return;

  _dmGrndSpeed = new DmFlt32Object(DOPPLER_BOTTOM_TRACK_CURRENT_DM);
  if (_dmGrndSpeed->error())
  {
    _dmGrndSpeed->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmGrndSpeed, DmConsumeFlag) == -1)
    return;

  _dmGrndDirection = new DmFlt32Object(DOPPLER_BOTTOM_TRACK_DIRECTION_DM);
  if (_dmGrndDirection->error())
  {
    _dmGrndDirection->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmGrndDirection, DmConsumeFlag) == -1)
    return;

  _dmGrndValid = new DmBooleanObject(DOPPLER_BOTTOM_TRACK_DATA_VALID_DM);
  if (_dmGrndValid->error())
  {
    _dmGrndValid->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmGrndValid, DmConsumeFlag) == -1)
    return;

  _dmDistance = new DmFlt32Object(DOPPLER_DISTANCE_DM);
  if (_dmDistance->error())
  {
    _dmDistance->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmDistance, DmConsumeFlag) == -1)
    return;
  
  _dmTotalDistance = new DmFlt32Object(DOPPLER_DISTANCE_TOTAL_DM);
  if (_dmTotalDistance->error())
  {
    _dmTotalDistance->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (addDmObject(_dmTotalDistance, DmConsumeFlag) == -1)
    return;

  if (setConsumePeriod(period) == -1)
    return;

  _prevLogging = _prevLoggingToFile = (MBool )999;
  
  updateGui();

}


AdvLogger::~AdvLogger()
{
}


void AdvLogger::updateGui(DM_Item handle)
{
  DM_Time t;
  Errno errno;
  char errorBuf[errorMsgSize];
  time_t logStartTime;

  MBool logging;
  MBool stoppedLogToFile;
  
  if ((errno = _dmStopLogToFile->read(&stoppedLogToFile, &t)) != SUCCESS)
  { 
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }

  if (stoppedLogToFile != _prevLoggingToFile)
  {
    if (stoppedLogToFile)
      XtVaSetValues(_logToFile, XmNset, False, NULL);
    else
      XtVaSetValues(_logToFile, XmNset, True, NULL);

    _prevLoggingToFile = stoppedLogToFile;
  }


  if ((errno = _dmStart->read(&logging, &t)) != SUCCESS)
  { 
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (logging != _prevLogging)
  { 
    _prevLogging = logging;
    
    if (logging)
    {
      XtSetSensitive(_logToFile, False);
      XtSetSensitive(_start, False);
      XtSetSensitive(_stop, True);
    }
    else
    {
      XtSetSensitive(_logToFile, True);
      XtSetSensitive(_start, True);
      XtSetSensitive(_stop, False);
    }
  }
 
  logStartTime = t.tv_sec;

  char buf[100];
  
  Flt32 fltValue;
  MBool valid;
  
  XmString xmstr;
  
  if ((errno = _dmValid->read(&valid, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }

  if (valid)
  {
    XtSetSensitive(_waterMgr, True);
    
    if ((errno = _dmSpeed->read(&fltValue, &t)) != SUCCESS)
    {
      sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
      setError(errorBuf);
      return;
    }
    sprintf(buf, "%.2f", fltValue);
    _speed->set(buf);

    if ((errno = _dmDirection->read(&fltValue, &t)) != SUCCESS)
    {
      sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
      setError(errorBuf);
      return;
    }
    sprintf(buf, "%.2f", fltValue);
    _direction->set(buf);
  }
  
  if ((errno = _dmGrndValid->read(&valid, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }

  if (valid)
  {
    XtSetSensitive(_grndMgr, True);

    if ((errno = _dmGrndSpeed->read(&fltValue, &t)) != SUCCESS)
    {
      sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
      setError(errorBuf);
      return;
    }
    sprintf(buf, "%.2f", fltValue);
    _grndSpeed->set(buf);

    if ((errno = _dmGrndDirection->read(&fltValue, &t)) != SUCCESS)
    {
      sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
      setError(errorBuf);
      return;
    }
    sprintf(buf, "%.2f", fltValue);
    _grndDirection->set(buf);
  }

  if (logging)
  {
    sprintf(buf, "%d", time(NULL) - logStartTime);
    _elapsedTime->set(buf);
  }
  
  if ((errno = _dmDistance->read(&fltValue, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }
  
  sprintf(buf, "%.2f", fltValue);  
  _distance->set(buf);
  
  if ((errno = _dmTotalDistance->read(&fltValue, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }

  sprintf(buf, "%.2f", fltValue);
  _totalDistance->set(buf);
}


int AdvLogger::start()
{
  // If file logging specified, make sure logger program is alive
  Boolean set;
  XtVaGetValues(_logToFile, XmNset, &set, NULL);
  if (set)
    checkFileLogger();
  
  if (toggleItem(_dmStop, FALSE) == -1)
    return -1;
 
  return toggleItem(_dmStart, TRUE);
}


int AdvLogger::stop()
{
  if (toggleItem(_dmStart, FALSE) == -1)
    return -1;
  
  return toggleItem(_dmStop, TRUE);
}


int AdvLogger::reset()
{
  char errorBuf[errorMsgSize];

  DmBooleanObject *dmObj = new DmBooleanObject(DOPPLER_RESET_DISTANCE_LOG_DM);

  if (dmObj->error())
  {
    dmObj->errorMsg(errorBuf);
    setError(errorBuf);
    delete dmObj;
    return -1;
  }

  Errno errno;
  if ((errno = dmObj->startProvide(DM_ASYNC)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::start()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    delete dmObj;
    return -1;
  }
  
  DM_Time t;
  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  MBool value = TRUE;
  
  if ((errno = dmObj->write(value, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::start()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    dmObj->stopProvide();
    delete dmObj;
    return -1;
  }

  dmObj->stopProvide();
  return 0;

}


int AdvLogger::checkFileLogger()
{
  // The following item is provided by the data logger program
  DmEmptyObject *dataLoggerFlag = 
    new DmEmptyObject(ADV_DATA_LOGGER_ALIVE_DM);

  Boolean error = False;
  
  if (dmPrvdrStatus(dataLoggerFlag->getHandle()) == NOT_PRVDR)
  {
    theErrorDialog->postAndWait("ADV data file logger apparently "
				"not running");

    error = True;
  }
  
  delete dataLoggerFlag;
  if (error)
    return -1;
  else
    return 0;
}


int AdvLogger::toggleItem(DmBooleanObject *dmObj, MBool value)
{
  char errorBuf[errorMsgSize];
  Errno errno;
  DM_Time t;
  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if ((errno = dmObj->write(value, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::start()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }
  return 0;
}


void AdvLogger::toggleFileLogging(Boolean log)
{
  MBool debug = FALSE;
  
  MBool value;
  if (log)
    value = FALSE;
  else
    value = TRUE;
  
  char errorBuf[512];
  
  DM_Time t;
  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  Errno errno;
  if ((errno = _dmStopLogToFile->write(value, &t)) != SUCCESS)
  {
    sprintDmError(errno, "AdvLogger::toggleFileLogging()", errorBuf);
    theErrorDialog->postAndWait(errorBuf);
  }

  if (log)
    checkFileLogger();
}


int AdvLogger::startConsume()
{
  if (XtIsRealized(_baseWidget))
    checkFileLogger();

  DmGuiObject::startConsume();
}


void AdvLogger::startStopCallback(Widget w,
				  XtPointer clientData,
				  XtPointer callData)
{
  AdvLogger *obj = (AdvLogger *)clientData;
  if (w == obj->_start)
    obj->start();
  else if (w == obj->_stop)
    obj->stop();
  else if (w == obj->_reset)
    obj->reset();
}


void AdvLogger::logToFileCallback(Widget w,
				  XtPointer clientData,
				  XtPointer callData)
{
  AdvLogger *obj = (AdvLogger *)clientData;

  XmToggleButtonCallbackStruct *state =
    (XmToggleButtonCallbackStruct *)callData;

  obj->toggleFileLogging(state->set);
}


