/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : StreamSerialDriver.cc                                         */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <string.h>
#include "StreamSerialDriver.h"
#include "Exception.h"
#include "Syslog.h"

StreamSerialDriver::StreamSerialDriver(const char *name,
				       SerialDevice *serialDevice,
				       unsigned maxRecordBytes,
				       const char *recordTerminator,
				       unsigned readTimeout,
				       unsigned maxInitTries)
  : Task(name)
{
  char errorBuf[512];

  _device = serialDevice;

  _recordTerminator = strdup(recordTerminator);
  _maxRecordBytes = maxRecordBytes;

  _readTimeout = readTimeout;

  _state = DeviceIF::Initializing;

  _maxInitTries = maxInitTries;

  // Allocate record buffer
  if ((_record = (unsigned char *)malloc(_maxRecordBytes)) == 0) {
    sprintf(errorBuf, 
	    "StreamSerialDriver::StreamSerialDriver() for %s - "
	    "malloc() of %d bytes failed", name, _maxRecordBytes);

    throw Exception(errorBuf);
  }
}


StreamSerialDriver::~StreamSerialDriver()
{
  free((void *)_recordTerminator);
  free((void *)_record);
}


unsigned StreamSerialDriver::readTimeout()
{
  return _readTimeout;
}


void StreamSerialDriver::setReadTimeout(unsigned timeout)
{
  _readTimeout = timeout;
}


void StreamSerialDriver::run()
{
  Boolean debug = False;
  char errorBuf[512];
  DeviceIF::Status newState;

  dprintf("StreamSerialDriver::initialize()");
  // Try to initialize device
  for (int i = 0; i < _maxInitTries; i++) {
    // Initialize device; pure virtual method
    if ((newState = initialize()) == DeviceIF::Ok)
      break;
  }

  setState(newState);

  if (state() != DeviceIF::Ok) {
    // initialize() failed
    sprintf(errorBuf, "%s::initialize() failed after %d tries", 
	    name(), _maxInitTries);

    throw Exception(errorBuf);
  }

  try {
    _device->flushReceiveBuf();
  }
  catch (...) {
    setState(DeviceIF::Error);
  }

  Boolean readError;
  int nBytesRead;

  dprintf("StreamSerialDriver::run() - start main loop");

  while (True) {

    nBytesRead = 0;

    // Prepare for data acquisition, etc
    newState = beginCycle();
    setState(newState);

    dprintf("StreamSerialDriver::run() - call readRecord()");

    // Read data record from device
    newState = readRecord(_record, _maxRecordBytes, _recordTerminator,
			  _readTimeout, &nBytesRead);

    setState(newState);

    if (newState == DeviceIF::Ok) {
      // Process device data record; pure virtual method
      newState = processRecord(_record, nBytesRead);
      setState(newState);
    }

    // Check for command, then process it
    newState = processCommand();
    setState(newState);
  }
}



Boolean StreamSerialDriver::setState(DeviceIF::Status newState)
{
  if (newState == _state) {
    // No change in state
    return False;
  }

  _state = newState;

  // Trigger event for subscribers
  triggerEvent(newState);

  // State changed
  return True;
}


DeviceIF::Status StreamSerialDriver::state()
{
  return _state;
}


DeviceIF::Status StreamSerialDriver::readRecord(unsigned char *record,
						int maxRecordBytes,
						const char *recordTerminator,
						unsigned readTimeout,
						int *nBytesRead)
{
  Boolean debug = False;
  dprintf("StreamSerialDriver::readRecord()");
  Boolean readError = False;

  try {

    // Read data record from device
    *nBytesRead = _device->readUntil((char *)record, maxRecordBytes, 
				     (char *)recordTerminator, 
				     readTimeout);
  }
  catch (SerialDevice::TimedOut) {
    readError = True;
    Syslog::write("%s::readRecord() - serial device timed out", name());
    handleReadTimeout();
  }
  catch (SerialDevice::BufferFull) {
    readError = True;
    Syslog::write("%s::readRecord() - buf=", name(), record);
    Syslog::write("%s::readRecord() - serial device buffer full", name());
  }
  catch (Exception e) {
    readError = True;
    Syslog::write("%s::readRecord() - caught exception:  abort", name());
    throw;
  }

  dprintf("StreamSerialDriver::readRecord() - done");

  if (readError)
    return DeviceIF::Error;
  else
    return DeviceIF::Ok;
}


DeviceIF::Status StreamSerialDriver::beginCycle()
{
  Boolean debug = False;
  dprintf("StreamSerialDriver::beginCycle()");
  return DeviceIF::Ok;
}


DeviceIF::Status StreamSerialDriver::processCommand()
{
  Boolean debug = False;
  dprintf("StreamSerialDriver::processCommand()");
  return DeviceIF::Ok;
}


void StreamSerialDriver::handleReadTimeout()
{
  // By default, don't do anything
}
