/*
 *  zwickroll.c
 *
 *  This is a variation of the standard AsonHandler which does not 
 *  clear the garbage away before writing. This lead to lost data with 
 * the zwickroll machine.
 *
 * This is actually a fix rather then a solution. The Zwickroll test rig
 * sends unsolicited messages. A better way to deal with this is to store them all 
 * in a queue which clients can inspect and consume from. Or use a callback system
 * for processing incoming messages.   
 *
 *  Created on: December, 4, 2013
 *      Author: koennecke
 *
 * Revised to identify out-of-band messages and to pass them on to a separate 
 * handler script
 * 
 * Mark Koennecke, December 2014
 */
#include <errno.h>
#include <ascon.h>
#include <ascon.i>
#include <lld.h> 
#include <lld_str.h>
#include <stptok.h>
#include <trace.h>

static char *outofBandHandler = NULL;
static int outofBandList = -1;
/*----------------------------------------------------------------------------*/
static int outofBandHandled(Ascon *a)
{
  int status;
  char testString[80];
  char command[1024];
  char *toTest;

  if(outofBandList < 0){
    return 0;
  }

  status = LLDnodePtr2First(outofBandList);
  toTest = GetCharArray(a->rdBuffer);
  /* printf("outofBandHandler processing: %s\n", toTest); */
  while(status != 0){
    LLDstringData(outofBandList, testString);
    if(strstr(toTest, testString) != NULL){
      strcpy(command,outofBandHandler);
      strncat(command," \"",sizeof(command));
      strncat(command,GetCharArray(a->rdBuffer),sizeof(command));
      strncat(command,"\"",sizeof(command));
      /* printf("outofBandHandler found match with %s\n", testString);  */
      status = Tcl_Eval(InterpGetTcl(pServ->pSics), command);
      if(status != TCL_OK){
	traceIO("zwickroll","Tcl Error %s while processing out-of-band", Tcl_GetStringResult(InterpGetTcl(pServ->pSics)));
	/* printf("Tcl Error %s while processing out-of-band\n", Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); */
      }
      DynStringClear(a->rdBuffer);
      return 1;
    }
    status = LLDnodePtr2Next(outofBandList);
  }
  return 0;
}
/*----------------------------------------------------------------------------*/
static int ZwickrollHandler(Ascon *a)
{
  int ret, l;

  switch(a->state){
  case AsconWriting:
    l = GetDynStringLength(a->wrBuffer) - a->wrPos;
    ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
    if (ret < 0) {
      AsconError(a, "ASC4", errno); /* sets state to AsconFailed */ 
    } else {
      a->wrPos += ret;
      if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
        a->state = AsconWriteDone;
      }
    }
    break;
  case AsconReading:
    ret = AsconStdHandler(a);
    if(a->state == AsconReadDone){
      if(outofBandHandled(a)){
        a->state = AsconReading;
      }
    }
    return ret;
  default:
    return AsconStdHandler(a);
  }
  return 1;
}
/*------------------------------------------------------------------------*/
static int ZwickRollInit(Ascon *a, SConnection *con, int argc, char *argv[])
{
  char token[80], *pPtr;

  assert(argc>1);
  a->hostport = strdup(argv[1]);
  a->sendTerminator = strdup("\r\n");
  a->replyTerminator = strdup("\n");
  a->timeout = 2.;

  if(argc > 3){
    outofBandHandler = strdup(argv[3]);
    outofBandList = LLDstringCreate();
    pPtr = argv[2];
    while(pPtr != NULL){
      pPtr = stptok(pPtr,token,sizeof(token),":");
      if(pPtr != NULL){
        LLDstringAdd(outofBandList,token);
      }
    }
  }

  return 1;
}

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

  prot = calloc(sizeof(AsconProtocol), 1);
  prot->name = strdup("zwickroll");
  prot->init = ZwickRollInit;
  prot->handler = ZwickrollHandler;
  AsconInsertProtocol(prot);
}
