/*--------------------------------------------------------------------------
                     S E R I A L W A I T
      
    wait for an command to execute in a serial device by polling with
    null messages in SICS.
    
    copyright: see copyright.h
    
    Mark Koennecke, June 1998                 
---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "fortify.h"
#include "serialwait.h"
#include "dynstring.h"

/*
#define SDEBUG 1
*/
/*--------------- The wait data structure --------------------------------*/
  typedef struct {
                   int iEnd;        /* end signal flag */
                   int iError;      /* error returns */
                   int iFirst;      /* first call, receive only */
                   time_t tEnd;     /* not more then a minute! */
                   pDynString data;  /* reply data read */
                   void **pData;    /* the serial IO data structure */
                 } SW, *pSW;      
                 
/*---------------- The signal function --------------------------------*/

  static void SWSignal(void *pUser, int iSignal, void *pSigData)
  {
      pSW self = NULL;
      int *iInt;
      
      self = (pSW)pUser;
      iInt = (int *)pSigData;
      
      if(iSignal == SICSINT)
      {
        if(*iInt > eContinue)
        {
           self->iEnd = 2;
        }  
      }
  }
/*---------------------- The task function ------------------------------*/
  static int SWTask(void *pData)
  {
      pSW self = NULL;
      char pReply[1024];
      int iRet, iCount;
      char *pPtr;
      
      self = (pSW)pData;
      assert(self);

      
      /* check for interrupt end */
      if(self->iEnd > 0)
      {
         return 0;
      }

     /* check for timeout */
     if(time(NULL) >= self->tEnd)
     {
         self->iError = TIMEOUT;
         self->iEnd = 3;
         return 0;
     }


      /* wait for a second */
      SicsWait(1);
      
      /* send a null command and read reply */
      if(!self->iFirst)
      {
          iRet = SerialSend(self->pData,"");
      }
      else
      {
        self->iFirst = 0;
      }    

      pReply[0] = '\0';
      iRet = SerialReceive(self->pData,pReply,1023);
#ifdef SDEBUG
      printf("Primary answer: %s\n",pReply);  
#endif      
      if((iRet == TIMEOUT) || (iRet == SELECTFAIL) )
      {
#ifdef SDEBUG      
        printf("Return 1 on timeout\n");
#endif        
        return 1;
      }
      else if(iRet < 0)
      {
          self->iError = iRet;
          self->iEnd   = 3;
#ifdef SDEBUG          
          printf("Error %d return Receive 0\n", iRet);
#endif          
          return 0; 
      }
      
      /* is there data ? */
      iCount = strlen(pReply);
      if(iCount <= 0)
      {
#ifdef SDEBUG
        printf("No data in Reply, return 1\n");
#endif        
        return 1;
      }
      
      /* do we have a tmo message ? */
      if(strncmp("?TMO",pReply,4) == 0)
      {
         /* is there additional data appended ? */
         if(iCount > 4)
         {
            /* store what we have */
            pPtr = pReply + 4;
            DynStringConcat(self->data, pPtr);
            
            /* set tmo high, read rest of reply */
            SerialConfig(self->pData, 90000);
            pReply[0] = '\0';
            iRet = SerialWriteRead(self->pData,"",pReply,1023);
#ifdef SDEBUG            
            printf(" sec rep: %s\n",pReply);  
#endif
            if(iRet != 1)
            {
               self->iError = iRet;
               self->iEnd   = 3;
#ifdef SDEBUG               
               printf("return 0, on secondary read error\n");
#endif
               return 0;
            }
            DynStringConcat(self->data,pReply);
            self->iEnd = 1;
#ifdef SDEBUG            
            printf("Complete read on second try, return 0\n");
#endif            
            return 0;
         }
         else /* schnuuuueeeeeffffff! no data, poll again */
         {
#ifdef SDEBUG
            printf("Do agaian, got a TMO \n"); 
#endif
            return 1;
         }
      }
      else /* we obtained all the data right away */
      {
           DynStringConcat(self->data,pReply);
           self->iEnd = 1;
#ifdef SDEBUG           
           printf("Return 0, received all data in one go \n");
#endif           
           return 0;
      }
      /* should not get here */
#ifdef SDEBUG      
      printf("Should not get here return 1\n");
#endif      
      return 1;
  }

/*-------------------------- The actual working function ------------------*/                 
  int SerialSicsExecute(void **pData, char *pCommand, 
                        char *pReply, int iBufLen)
  {
     int iOldTmo, iRet, iResult;
     long lTask;
     SW control;
     char *pPtr;
     
     /* set timeout to 0 */
     iOldTmo = SerialGetTmo(pData);
     SerialConfig(pData,200); 
     
     /* send the command to execute */
     iRet = SerialSend(pData,pCommand);
     if(iRet != 1)
     {
         SerialConfig(pData,iOldTmo);
         return iRet;
     }
     
     /* initialize task data structure */
     control.iEnd = 0;
     control.iError = 0;
     control.iFirst = 1;
     control.pData = pData;
     /* do a timeout after a minute without a reply */
     control.tEnd = time(NULL) + 60; 
     control.data = CreateDynString(1024,1024);
     
     /* start task */
     lTask = TaskRegister(pServ->pTasker,
                          SWTask,
                          SWSignal,
                          NULL,
                          &control,
                          1);
     /* wait for it to end */
     TaskWait(pServ->pTasker,lTask);
     
     /* analyse what we have got, success first */
     if(control.iEnd == 1)
     {
        iResult = 1;
        pPtr = GetCharArray(control.data);
        strncpy(pReply,pPtr,iBufLen);
     }
     else if(control.iEnd == 2) /* interrupted */
     {
        iResult = INTERRUPTED;
     }  
     else
     {
        iResult = control.iError;
     } 
     control.iEnd = 10;                  
     
     /* clear up and go */
     DeleteDynString(control.data);
     SerialConfig(pData,iOldTmo);
     return iResult;                       
  }
