/**
 * This is a protocol implementation for the new SICS asynchronous I/O
 * infrastructure and the Juelich chopper system as used at MARS.
 * 
 * The Juelich chopper control program likes checksums to come with 
 * messages and uses $ as both send and reply terminators.
 * 
 * copyright: see file COPYRIGHT
 * 
 * Mark Koennecke, June 2008
 */
#include <errno.h>
#include <ascon.h>
#include <ascon.i>
#include <dynstring.h>

static int calculateJulCheckSum(char *realCommand)
{
  int i, checkSum = 0;

  for (i = 1; i < strlen(realCommand); i++) {
    checkSum += (int) realCommand[i];
  }
  return checkSum;
}

/*---------------------------------------------------------------------------*/
int JulchoHandler(Ascon * a)
{
  char *data = NULL;
  int checkSum, ret;
  char checkBuffer[30], chr;

  switch (a->state) {
  case AsconWriteStart:
    data = GetCharArray(a->wrBuffer);
    checkSum = calculateJulCheckSum(data);
    snprintf(checkBuffer, 30, "{%d}$", checkSum);
    DynStringConcat(a->wrBuffer, checkBuffer);
    a->state = AsconWriting;
    a->wrPos = 0;
    break;
  case AsconReading:
    ret = AsconReadChar(a->fd, &chr);
    if (ret < 0) {
      /* EINTR means we must retry */
      if (errno != EINTR && errno != EAGAIN) {
        AsconError(a, "AsconReadChar failed:", errno);
      }
      return 1;
    } else if (ret > 0) {
      a->start = DoubleTime();
      if (chr == '$') {
        DynStringConcatChar(a->rdBuffer, '\0');
        a->state = AsconReadDone;
        break;
      } else {
        if (DynStringConcatChar(a->rdBuffer, chr) == 0) {
          AsconError(a, "DynStringConcatChar failed:", ENOMEM);
          break;
        }
      }
    } else if (ret == 0) {
      if (a->timeout > 0) {
        if (DoubleTime() - a->start > a->timeout) {
          AsconError(a, "read timeout", 0);
          a->state = AsconTimeout;
        }
      }
      return 0;
    }
    break;
  default:
    return AsconStdHandler(a);
  }
  return 1;
}

/*-------------------------------------------------------------------------*/
void AddJulChoProtocoll()
{
  AsconProtocol *prot = NULL;

  prot = calloc(sizeof(AsconProtocol), 1);
  prot->name = strdup("julcho");
  prot->init = AsconStdInit;
  prot->handler = JulchoHandler;
  AsconInsertProtocol(prot);
}
