/*--------------------------------------------------------------------------
  This is one implementation file for the TASMAD simulation module for
  SICS. The requirement is to make SICS look as much as TASMAD as 
  possible. This includes:
  - TASMAD is variable driven
  - Sometimes variables are accessed in storage order.
  - A complicated calculation has to be done for getting the instruments
    settings right. The appropriate F77 routine from TASMAD will be 
    reused.
  - The scan logic is different.
  - Output of ILL-formatted data files is required. 

  This file implements the MAD sc command for scanning. The general idea is
  that specialized action functions are defined for the scan object and the
  main SICS scan loop is used as a driver.  

  Mark Koennecke, November-December 2000
---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <ctype.h>
#include <tcl.h>
#include <fortify.h>
#include <sics.h>
#include <sicsvar.h>
#include <counter.h>
#include <motor.h>
#include <scan.h>
#include <scan.i>
#include <lld.h>
#include "tas.h"
#include "tasu.h"
#include <scanvar.h>
#include <evcontroller.h>
#include <splitter.h>
#include <status.h>

/*------------------------------------------------------------------------
  a little local utility for making a line of characters
  -------------------------------------------------------------------------*/
static void charLine(char *pBueffel, char c)
{
  int i;

  for (i = 0; i < 80; i++) {
    pBueffel[i] = c;
  }
  pBueffel[80] = '\n';
  pBueffel[81] = '\0';
}

/*------------------------------------------------------------------------*/
static void strtoupper(char *pText)
{
  assert(pText);

  while (*pText != '\0') {
    *pText = toupper(*pText);
    pText++;
  }
}

/*-----------------------------------------------------------------------*/
static void strcenter(char *str, char *target, int iLength)
{
  int iPtr, i;

  /*
     catch the error if target is to long
   */
  if (strlen(str) >= iLength) {
    strlcpy(target, str, iLength);
  }

  iPtr = (iLength - strlen(str)) / 2;
  for (i = 0; i < iPtr; i++) {
    target[i] = ' ';
  }
  target[iPtr] = '\0';
  strcat(target, str);
  for (i = iPtr + strlen(str); i < iLength - 1; i++) {
    target[i] = ' ';
  }
  target[iLength - 1] = '\0';
}

/*-----------------------------------------------------------------------
  helper function for TASHeader
  ------------------------------------------------------------------------*/
static void writePolFile(FILE * fd, pTASdata pTAS)
{
  char pLine[132];
  FILE *fpol = NULL;

  assert(fd);
  assert(pTAS);

  fpol = fopen(pTAS->tasPar[POLFIL]->text, "r");
  if (!fpol) {
    /*
       error gets reported anyway later on
     */
    return;
  }

  while (fgets(pLine, 131, fpol) != NULL) {
    if (strstr(pLine, "\n") == NULL) {
      fprintf(fd, "POLAN: %s\n", pLine);
    } else {
      fprintf(fd, "POLAN: %s", pLine);
    }
  }
  fclose(fpol);
}

/*-----------------------------------------------------------------------*/
static char *findLastPoint(char *text)
{
  char *pPtr;
  int i;

  pPtr = text + strlen(text) - 1;
  for (i = strlen(text); i > 0; i--, pPtr--) {
    if (*pPtr == '.') {
      return pPtr;
    }
  }
  return NULL;
}

/*-------------------------------------------------------------------------
  TASHeader writes the header of a TAS data file. The format is an 
  obscure format from ILL ( not ill but Institute Laue Langevin). No
  documentation about this exists except the files themselves. This is long 
  and tiresome code.
---------------------------------------------------------------------------*/
static int TASHeader(pScanData self)
{
  pTASdata pTAS = (pTASdata) self->pSpecial;
  int i, iCount, status, iFileNO=0;
  char pBueffel[1024], pHeader[1024], pWork[132], pWork2[60], pTen[12],
      *pcPtr;
  time_t iDate;
  struct tm *psTime;
  pVarEntry pVar = NULL;
  void *pPtr = NULL;
  pMotor pMot;
  float fVal, fVal2;
  CommandList *pCom = NULL;
  pDummy pDum = NULL;
  pIDrivable pDrive = NULL;
  pEVControl pTem = NULL;
  pSicsVariable sVar = NULL;
  float f1=.0, f2=.0, f3=.0, f4=.0;

  assert(self);
  assert(pTAS);
  assert(self->pCon);
  assert(self->pSics);

  /* open data file */
  self->fd = fopen(self->pFile, "w");
  if (!self->fd) {
    SCWrite(self->pCon, "ERROR: cannot write data file", eLogError);
    return 0;
  }

  /*
     extract the file number from the name for entry into the
     datafile
   */
  pcPtr = findLastPoint(self->pFile);
  if (pcPtr != NULL) {
    pcPtr -= 6;                 /* 6 digits for number */
    for (i = 0; i < 6; i++, pcPtr++) {
      pWork[i] = *pcPtr;
    }
    pWork[6] = '\0';
    iFileNO = atoi(pWork);
  } else {
    SCWrite(self->pCon, "WARNING: failed to decode file number", eLog);
  }

  /* the bizarre R, A, V header */
  charLine(pBueffel, 'R');
  fputs(pBueffel, self->fd);
  fprintf(self->fd, "%8d%8d%8d\n", iFileNO, 1, 0);
  fputs
      ("ILL TAS data in the new ASCII format follow after the line VV...V\n",
       self->fd);
  charLine(pBueffel, 'A');
  fputs(pBueffel, self->fd);
  fprintf(self->fd, "%8d%8d\n", 42, 0);

  /*
     format time to TAS format
   */
  iDate = time(NULL);
  psTime = localtime(&iDate);
  memset(pWork, 0, 59);
  strftime(pWork, 59, "%d-%b-%Y %H:%M:%S", psTime);
  fprintf(self->fd, "%-10s%-12s%-s\n", pTAS->tasPar[INST]->text,
          pTAS->tasPar[USR]->text, pWork);

  charLine(pBueffel, 'V');
  fputs(pBueffel, self->fd);

  /* 
     output a plethora of parameters
   */
  fprintf(self->fd, "INSTR: %s\n", pTAS->tasPar[INST]->text);
  fprintf(self->fd, "EXPNO: \n");
  fprintf(self->fd, "USER_: %s\n", pTAS->tasPar[USR]->text);
  fprintf(self->fd, "LOCAL: %s\n", pTAS->tasPar[LOC]->text);
  fprintf(self->fd, "FILE_: %d\n", iFileNO);
  fprintf(self->fd, "DATE_: %s\n", pWork);
  fprintf(self->fd, "TITLE: %s\n", pTAS->tasPar[TIT]->text);
  fprintf(self->fd, "COMND: %s\n", pTAS->tasPar[COM]->text);
  fprintf(self->fd,
          "POSQE: QH=%8.4f, QK=%8.4f, QL=%8.4f, EN=%8.4f, UN=MEV\n",
          pTAS->tasPar[QH]->fVal,
          pTAS->tasPar[QK]->fVal,
          pTAS->tasPar[QL]->fVal, pTAS->tasPar[EN]->fVal);
  /*
     build the steps line
   */
  sprintf(pBueffel, "STEPS: ");
  for (i = 0; i < self->iScanVar; i++) {
    DynarGet(self->pScanVar, i, &pPtr);
    pVar = (pVarEntry) pPtr;
    if (pVar) {
      strlcpy(pWork2, ScanVarName(pVar), 59);
      strtoupper(pWork2);
      sprintf(pWork, "D%s=%8.4f, ", pWork2, ScanVarStep(pVar));
      strcat(pBueffel, pWork);
    }
  }
  strcat(pBueffel, "\n");
  fputs(pBueffel, self->fd);

  /*
     a lot of parameters
   */
  fprintf(self->fd, "PARAM: DM=%8.4f, DA=%8.4f, SM=%2d, SS=%2d, SA=%2d\n",
          pTAS->tasPar[DM]->fVal,
          pTAS->tasPar[DA]->fVal,
          pTAS->tasPar[SM]->iVal,
          pTAS->tasPar[SS]->iVal, pTAS->tasPar[SA]->iVal);
  fprintf(self->fd, "PARAM: FX=%3d, KFIX=%8.4f\n",
          pTAS->tasPar[FX]->iVal, pTAS->tasPar[KF]->fVal);
  fprintf(self->fd,
          "PARAM: ALF1=%8.4f, ALF2=%8.4f, ALF3=%8.4f, ALF4=%8.4f\n",
          pTAS->tasPar[ALF1]->fVal, pTAS->tasPar[ALF2]->fVal,
          pTAS->tasPar[ALF3]->fVal, pTAS->tasPar[ALF4]->fVal);
  fprintf(self->fd,
          "PARAM: BET1=%8.4f, BET2=%8.4f, BET3=%8.4f, BET4=%8.4f\n",
          pTAS->tasPar[BET1]->fVal, pTAS->tasPar[BET2]->fVal,
          pTAS->tasPar[BET3]->fVal, pTAS->tasPar[BET4]->fVal);
  fprintf(self->fd, "PARAM: ETAM=%8.4f, ETAA=%8.4f\n",
          pTAS->tasPar[ETAM]->fVal, pTAS->tasPar[ETAA]->fVal);
  fprintf(self->fd, "PARAM: AS=%8.4f, BS=%8.4f, CS=%8.4f\n",
          pTAS->tasPar[AS]->fVal, pTAS->tasPar[BS]->fVal,
          pTAS->tasPar[CS]->fVal);
  fprintf(self->fd, "PARAM: AA=%8.4f, BB=%8.4f, CC=%8.4f\n",
          pTAS->tasPar[AA]->fVal, pTAS->tasPar[BB]->fVal,
          pTAS->tasPar[CC]->fVal);
  fprintf(self->fd, "PARAM: AX=%8.4f, AY=%8.4f, AZ=%8.4f\n",
          pTAS->tasPar[AX]->fVal, pTAS->tasPar[AY]->fVal,
          pTAS->tasPar[AZ]->fVal);
  fprintf(self->fd, "PARAM: BX=%8.4f, BY=%8.4f, BZ=%8.4f\n",
          pTAS->tasPar[BX]->fVal, pTAS->tasPar[BY]->fVal,
          pTAS->tasPar[BZ]->fVal);

  /*
   * write mupad stuff if available
   */
  sVar = FindCommandData(pServ->pSics, "w1", "SicsVariable");
  if (sVar != NULL) {
    f1 = sVar->fVal;
    sVar = FindCommandData(pServ->pSics, "w2", "SicsVariable");
    if (sVar != NULL) {
      f2 = sVar->fVal;
    }
    sVar = FindCommandData(pServ->pSics, "w3", "SicsVariable");
    if (sVar != NULL) {
      f3 = sVar->fVal;
    }
    sVar = FindCommandData(pServ->pSics, "w4", "SicsVariable");
    if (sVar != NULL) {
      f4 = sVar->fVal;
    }
    fprintf(self->fd, "PARAM: W1=%8.4f, W2=%8.4f, W3=%8.4f, W4=%8.4f\n",
            f1, f2, f3, f4);

    sVar = FindCommandData(pServ->pSics, "p1", "SicsVariable");
    if (sVar != NULL) {
      f1 = sVar->fVal;
    }
    sVar = FindCommandData(pServ->pSics, "p2", "SicsVariable");
    if (sVar != NULL) {
      f2 = sVar->fVal;
    }
    sVar = FindCommandData(pServ->pSics, "p3", "SicsVariable");
    if (sVar != NULL) {
      f3 = sVar->fVal;
    }
    sVar = FindCommandData(pServ->pSics, "p4", "SicsVariable");
    if (sVar != NULL) {
      f4 = sVar->fVal;
    }
    fprintf(self->fd, "PARAM: P1=%8.4f, P2=%8.4f, P3=%8.4f, P4=%8.4f\n",
            f1, f2, f3, f4);


  }

  /* 
     write motors and currents
   */
  fprintf(self->fd, "VARIA: ");
  iCount = 0;
  for (i = 0; i < MAXMOT; i++) {
    if (pTAS->iPOL >= 0
        || (tasMotorOrder[i][0] != 'i' && tasMotorOrder[i][0] != 'h')) {
      /* do not write currents (names starting with i or h) when not in pol. mode */
      fVal = readDrivable(tasMotorOrder[i], self->pCon);
      if (iCount == 3) {
        iCount = 0;
        fprintf(self->fd, "\nVARIA: ");
      }
      strcpy(pWork2, tasMotorOrder[i]);
      strtoupper(pWork2);
      fprintf(self->fd, "%-8s=%8.4f, ", pWork2, fVal);
      iCount++;
    }
  }
  fprintf(self->fd, "\n");

  /* 
     write zeros 
   */
  fprintf(self->fd, "ZEROS: ");
  iCount = 0;
  for (i = 0; i < CURMOT - 1; i++) {
    pMot = FindMotor(self->pSics, tasMotorOrder[i]);
    if (pMot) {
      status = MotorGetPar(pMot, "softzero", &fVal);
      if (!status) {
        fVal = -9999.77;
      }
      fVal *= -1;
      if (iCount == 3) {
        iCount = 0;
        fprintf(self->fd, "\nZEROS: ");
      }
      strcpy(pWork2, tasMotorOrder[i]);
      strtoupper(pWork2);
      fprintf(self->fd, "%-8s=%8.4f, ", pWork2, fVal);
      iCount++;
    } else {
      sprintf(pBueffel, "WARNING: motor %s NOT found, %s",
              tasMotorOrder[i], " possible data file corruption");
      SCWrite(self->pCon, pBueffel, eWarning);
    }
  }
  fprintf(self->fd, "\n");

  if (pTAS->iPOL >= 0) {
    writePolFile(self->fd, pTAS);
  }

  /*
     write counter parameters
   */
  fVal = GetCounterPreset(self->pCounterData);
  if (GetCounterMode(self->pCounterData) == eTimer) {
    fprintf(self->fd, "PARAM: TI=%8.4f\n", fVal);
  } else {
    fprintf(self->fd, "PARAM: MN=%8f\n", fVal);
  }

  /*
     write temperature data
   */
  pCom = FindCommand(self->pSics, "temperature");
  if (pCom) {
    pDum = (pDummy) pCom->pData;
    pDrive = pDum->pDescriptor->GetInterface(pDum, DRIVEID);
    if (pDrive) {               /* a proper environment device */
      fVal = pDrive->GetValue(pDum, self->pCon);
      pTem = (pEVControl) pCom->pData;
      EVCGetPar(pTem, "target", &fVal2);
      fprintf(self->fd, "PARAM: TEM=%8.4f, RT=%8.4f\n", fVal, fVal2);
    }
  }

  /* 
     build both the format and the data header line,
     start with the scan variables 
   */
  if (pTAS->iPOL >= 0) {
    strcpy(pBueffel, "FORMT: (I4,I4,");
    strcpy(pHeader, "  PNT PAL");
  } else {
    strcpy(pBueffel, "FORMT: (I4,1X,");
    strcpy(pHeader, " PNT ");
  }
  for (i = 0; i < self->iScanVar; i++) {
    DynarGet(self->pScanVar, i, &pPtr);
    pVar = (pVarEntry) pPtr;
    if (pVar) {
      strcat(pBueffel, "F9.4,1X,");
      strlcpy(pWork2, pVar->Name, 59);
      strtoupper(pWork2);
      strcenter(pWork2, pTen, 11);
      strcat(pHeader, pTen);
    }
  }
  /*
     put the standard counter things
   */
  strcat(pBueffel, "F8.0,1X,F8.0,1X,F9.2,1X,F8.0,1X,F8.0,1X,");
  strcat(pHeader, "      M1       M2      TIME     CNTS       M3 ");
  /*
     now the additional variables
   */
  for (i = 0; i < pTAS->addCount; i++) {
    if (i == pTAS->addCount - 1) {
      strcat(pBueffel, "F9.4");
    } else {
      strcat(pBueffel, "F9.4,1X,");
    }
    if (pTAS->addType[i] == 1) {        /* motor */
      strcpy(pWork2, tasMotorOrder[pTAS->addOutput[i]]);
    } else {
      strcpy(pWork2, tasVariableOrder[pTAS->addOutput[i]]);
    }
    strtoupper(pWork2);
    strcenter(pWork2, pTen, 11);
    strcat(pHeader, pTen);
  }
  strcat(pBueffel, ")");

  /* 
     write the final bit
   */
  fprintf(self->fd, "%s\nDATA_:\n%s\n", pBueffel, pHeader);

  /* 
     write header to screen as well
   */
  SCWrite(self->pCon, pHeader, eLog);

  /*
     close the file, we will reopen later with append for the data
   */
  fclose(self->fd);
  self->fd = NULL;
  return 1;
}

/*--------------------------------------------------------------------------
  TASScanPoint writes the data at each ScanPoint
---------------------------------------------------------------------------*/
static int TASScanPoint(pScanData self, int iPoint)
{
  pTASdata pTAS = (pTASdata) self->pSpecial;
  pVarEntry pVar = NULL;
  void *pPtr = NULL;
  int i, status, iPtr;
  float fVal;
  pMotor pMot = NULL;
  long m1, m2, m3, cnts;
  char pBueffel[1024], pWork[80], pError[132];

  /*
     after polarisation analysis, this has to be ignored as it is called 
     another time from the ScanLoop
   */
  if (pTAS->iIgnore) {
    return 1;
  }

  /*
     reopen file for appending
   */
  self->fd = fopen(self->pFile, "a");
  if (!self->fd) {
    SCWrite(self->pCon, "ERROR: cannot append to data file", eLogError);
    return 0;
  }

  /* 
     write point number
   */
  if (pTAS->iPOL > 0) {
    sprintf(pBueffel, "%3d %3d", iPoint + 1, pTAS->iPOL);
  } else {
    sprintf(pBueffel, "%4d ", iPoint + 1);
  }

  /*
     write the scan variables
   */
  for (i = 0; i < self->iScanVar; i++) {
    DynarGet(self->pScanVar, i, &pPtr);
    pVar = (pVarEntry) pPtr;
    if (pVar) {
      if (isTASMotor(ScanVarName(pVar)) >= 0) {
        if (pVar->pInter != NULL) {
          fVal = pVar->pInter->GetValue(pVar->pObject, self->pCon);
        } else {
          fVal = -9999.99;
        }
      } else if ((iPtr = isTASVar(pVar->Name)) >= 0) {
        fVal = pTAS->tasPar[iPtr]->fVal;
      }
    }
    sprintf(pWork, "%9.4f ", fVal);
    strcat(pBueffel, pWork);
  }

  /* 
     write monitors and counters
   */
  m1 = GetMonitor(self->pCounterData, 1, self->pCon);
  m2 = GetMonitor(self->pCounterData, 2, self->pCon);
  m3 = GetMonitor(self->pCounterData, 3, self->pCon);
  cnts = GetCounts(self->pCounterData, self->pCon);
  fVal = GetCountTime(self->pCounterData, self->pCon);
  sprintf(pWork, "%8ld %8ld %9.2f %8ld %8ld ", m1, m2, fVal, cnts, m3);
  strcat(pBueffel, pWork);

  /* 
     write additional parameters
   */
  for (i = 0; i < pTAS->addCount; i++) {
    fVal = -999.99;
    if (pTAS->addType[i] == 1) {        /* motor */
      fVal = readDrivable(tasMotorOrder[pTAS->addOutput[i]], self->pCon);
      if (fVal < -990.) {
        sprintf(pError, "WARNING: problem reading %s",
                tasMotorOrder[pTAS->addOutput[i]]);
        SCWrite(self->pCon, pError, eLog);
      }
    } else {
      fVal = pTAS->tasPar[pTAS->addOutput[i]]->fVal;
    }
    sprintf(pWork, "%9.4f ", fVal);
    strcat(pBueffel, pWork);
  }

  /*
     write both to file and onto screen
   */
  fprintf(self->fd, "%s\n", pBueffel);
  SCWrite(self->pCon, pBueffel, eLog);

  /*
     close the file
   */
  fclose(self->fd);
  self->fd = NULL;
  return 1;

}

/*-----------------------------------------------------------------------
  disables a3 driving in powder mode. In powder mode a3 is fixed. This
  supresses the error message issued otherwise.
  ------------------------------------------------------------------------*/
static void fixPowder(unsigned char tasTargetMask[20])
{
  char *pPtr = NULL;

  pPtr =
      (char *) Tcl_GetVar(pServ->pSics->pTcl, "powder", TCL_GLOBAL_ONLY);
  if (pPtr) {
    if (strstr(pPtr, "1") != NULL) {
      tasTargetMask[2] = 0;
    }
  }
}

/*------------------------------------------------------------------------
 TASScanDrive starts all the motors necessary to drive to a new scan
 position. It thereby takes care of the TAS calculation if necessary.
 -------------------------------------------------------------------------*/
static int TASScanDrive(pScanData self, int iPoint)
{
  pTASdata pTAS = (pTASdata) self->pSpecial;
  pVarEntry pVar = NULL;
  void *pPtr = NULL;
  int i, status, iPtr;
  int iTAS = 0;
  pMotor pMot;
  unsigned char tasTargetMask[20], tasMask[MAXEVAR];
  float tasTargets[20];

  /* 
     initialize masks to 0
   */
  for (i = 0; i < 10; i++) {
    tasMask[i] = 0;
    tasTargetMask[i] = 0;
    tasTargetMask[i + 10] = 0;
    tasTargets[i] = .0;
    tasTargets[i + 10] = .0;
  }
  tasMask[10] = 0;
  tasMask[11] = 0;

  /*
     store SRO motor value
   */
  getSRO(self->pCon, &pTAS->oldSRO);

  /*
     loop through all the scan variables
   */
  for (i = 0; i < self->iScanVar; i++) {
    DynarGet(self->pScanVar, i, &pPtr);
    pVar = (pVarEntry) pPtr;
    if (pVar) {
      /* set variable if QE-variable */
      if ((iPtr = isTASEnergy(ScanVarName(pVar))) >= 0) {
        iTAS = 1;
        pTAS->tasPar[EI + iPtr]->fVal =
            ScanVarStart(pVar) + iPoint * ScanVarStep(pVar);
        pTAS->tasPar[ETARGET + iPtr]->fVal =
            ScanVarStart(pVar) + iPoint * ScanVarStep(pVar);
        tasMask[iPtr] = 1;
      } else {
        /* 
           This is a motor, start it.
         */
        StartMotor(pServ->pExecutor, self->pSics, self->pCon, pVar->Name,
                   RUNDRIVE, pVar->fStart + iPoint * pVar->fStep);
        /*
           Ignore errors. TAS scans continues when a motor runs into
           a limit.
         */
      }
    }
  }

  /* 
     now check if we need a TAS calculation: if so do it and start 
     the motors
   */
  if (iTAS) {
    status = TASCalc(pTAS, self->pCon, tasMask, tasTargets, tasTargetMask);
    if (status) {
      /*
         Errors both in calculation or in starting motors are
         ignored here on purpose. There is a slight chance that
         other points in the scan fit the bill.
       */
      fixPowder(tasTargetMask);
      TASStart(pTAS, self->pCon, self->pSics, tasTargets, tasTargetMask);
    } else {
      SCSetInterrupt(self->pCon, eAbortOperation);
      return 1;
    }
  }

  /*
     now wait for our motors to arrive, thereby ignoring any error 
     returns. DO NOT WAIT if fast scan!
   */
  if (pTAS->iFast) {
    return 1;
  } else {
    status = Wait4Success(GetExecutor());
  }

  TASUpdate(pTAS, self->pCon);
  return 1;
}

/*-----------------------------------------------------------------------
 RunPolScan does a polarized scan
 ------------------------------------------------------------------------*/
static int RunPolScan(pScanData self, int iPoint)
{
  FILE *fd = NULL;
  char buffer[132];
  pTASdata pTAS = (pTASdata) self->pSpecial;

  fd = fopen(pTAS->tasPar[POLFIL]->text, "r");
  if (!fd) {
    SCWrite(self->pCon, "ERROR: failed to open polarisation analysis file",
            eLogError);
    return 0;
  }
  pTAS->iPOL = 0;
  while (fgets(buffer, 131, fd) != NULL) {
    /*
      ignore all errors here 
    */
    if (buffer[0] != '#') {
      InterpExecute(self->pSics,self->pCon,buffer);
      strtolower(buffer);
      if(strstr(buffer,"co") != NULL){
        pTAS->iPOL++;
        self->WriteScanPoints(self,iPoint);
      }
    }
    /*
       but allow for interrupts
     */
    if (SCGetInterrupt(self->pCon) != eContinue) {
      break;
    }
  }
  pTAS->iIgnore = 1;
  fclose(fd);
  pTAS->iPOL = 1;

  return 1;
}

/*---------------------------------------------------------------------------
    TASScanCount starts the counter for a TAS scan or runs the polarization
    analysis.
---------------------------------------------------------------------------*/
static int TASScanCount(pScanData self, int iPoint)
{
  pTASdata pTAS = (pTASdata) self->pSpecial;
  float fVal;
  int status, iRet;
  Status eOld;

  pTAS->iIgnore = 0;
  /*
     call the polarisation scan function if necessary
   */
  if (pTAS->iPOL >= 0) {
    return RunPolScan(self, iPoint);
  }

  /* 
     this is easy, just an ordinary scan, start a counter
   */
  fVal = GetCounterPreset(self->pCounterData);
  status = DoCount(self->pCounterData, fVal, self->pCon, 2);
  iRet = Wait4Success(GetExecutor());
  if (iRet == DEVINT) {
    SCWrite(self->pCon, "Counting aborted due to Interrupt", eLog);
    status = 0;
  } else if (iRet == DEVERROR) {
    SCWrite(self->pCon, "Counting finished with Problems", eLog);
    status = 0;
  } else {
    status = 1;
  }

  return status;
}

/*--------------------------------------------------------------------------
  TAS stores the scan data. This is necessary in order to support things 
  like peak or center which require the scan data for operation
  --------------------------------------------------------------------------*/
static int TASCollect(pScanData self, int iPoint)
{
  pVarEntry pVar = NULL;
  void *pDings;
  int i, iRet, status, iPtr;
  float fVal;
  CountEntry sCount;
  char *pAns = NULL, *pPtr = NULL;
  pTASdata pTAS = (pTASdata) self->pSpecial;
  pMotor pMot;

  assert(self);
  assert(self->pCon);

  /* loop over all scan variables */
  status = 1;
  for (i = 0; i < self->iScanVar; i++) {
    DynarGet(self->pScanVar, i, &pDings);
    pVar = (pVarEntry) pDings;
    if (pVar) {
      if ((iPtr = isTASEnergy(pVar->Name)) >= 0) {
        fVal = pTAS->tasPar[EI + iPtr]->fVal;
      } else {
        fVal = readDrivable(pVar->Name, self->pCon);
      }
      AppendScanVar(pVar, fVal);
    }
  }

  /* store counter data */
  /* monitors */
  for (i = 1; i < 10; i++) {
    sCount.Monitors[i - 1] = GetMonitor((pCounter) self->pCounterData, i,
                                        self->pCon);
  }
  if (self->iChannel != 0 && self->iChannel != -10) {
    sCount.Monitors[self->iChannel - 1] =
        GetCounts((pCounter) self->pCounterData, self->pCon);
  }

  /* counts, depending on iChannel */
  if (self->iChannel == 0) {
    sCount.lCount = GetCounts((pCounter) self->pCounterData, self->pCon);
  } else {
    sCount.lCount = GetMonitor((pCounter) self->pCounterData,
                               self->iChannel, self->pCon);
  }

  /* get time */
  sCount.fTime = GetCountTime((pCounter) self->pCounterData, self->pCon);
  /* stow away */
  DynarReplace(self->pCounts, self->iCounts, &sCount, sizeof(CountEntry));
  self->iCounts++;
  return 1;
}

/*----------------------------------------------------------------------
  SetTASScan sets the special TAS scan functions in the scan data 
  structure
  ------------------------------------------------------------------------*/
static void SetTASScan(pScanData self, pTASdata pTAS)
{
  self->PrepareScan = NonCheckPrepare;
  self->WriteHeader = TASHeader;
  self->WriteScanPoints = TASScanPoint;
  self->ScanDrive = TASScanDrive;
  self->ScanCount = TASScanCount;
  self->CollectScanData = TASCollect;
  self->pSpecial = pTAS;
}

/*-------------------------------------------------------------------------
  AddTASScanVar is the same as the standard AddScanVar but without the
  error checking for a drivable parameter
  --------------------------------------------------------------------------*/
static int AddTASScanVar(pScanData self, SicsInterp * pSics,
                         SConnection * pCon, char *name, float fStart,
                         float fStep)
{
  CommandList *pCom = NULL;
  pIDrivable pDriv = NULL;
  pDummy pData = NULL;
  VarEntry pVar;
  char pBueffel[512];

  if (self->iActive) {
    SCWrite(pCon,
            "ERROR: cannot change parameters while scan is running",
            eError);
    return 0;
  }

  /* find the thing */
  pCom = FindCommand(pSics, name);
  if (!pCom) {
    sprintf(pBueffel, "ERROR: Cannot find variable %s to scan", name);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  pData = (pDummy) pCom->pData;
  if (!pData) {
    sprintf(pBueffel, "ERROR: Cannot find data for variable %s", name);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* got everything, fill in the VarEntry structure */
  strcpy(pVar.Name, name);
  pVar.pInter = GetDrivableInterface(pData);
  pVar.pObject = pData;
  pVar.fStart = fStart;
  pVar.fStep = fStep;
  pVar.fData = NULL;
  pVar.dataList = LLDcreate(sizeof(float));

  /* put it away */
  DynarPutCopy(self->pScanVar, self->iScanVar, &pVar, sizeof(VarEntry));
  self->iScanVar++;

  return 1;
}

/*-----------------------------------------------------------------------
  The TAS scan command line contains two types of items: Scan variables 
  and their center values and normal SICS variables such as the monitor
  to use and the steps in each scan variable. As this is decidely easier
  this command line is parsed in two pathes: In the first pass all the 
  variables are set and removed. In the second pass the scan variables
  are parsed and entered into the scan data structure. 

  ParseVar implements the first pass.
  -------------------------------------------------------------------------*/
#define NONE 0
#define NUM 1
#define TXT 2
extern char *stptok(const char *s, char *tok, size_t toklen, char *brk);


static int ParseVar(pTASdata pTAS, char *pBueffel, SConnection * pCon)
{
  char *myBuffer = NULL, pToken[20], *pPtr, pError[132];
  int iPtr, lastToken, lastVar;
  VarType vt;
  int qhPars;

  /*
     set up our own buffer
   */
  myBuffer = (char *) malloc((strlen(pBueffel) + 10) * sizeof(char));
  if (!myBuffer) {
    SCWrite(pCon, "ERROR: out of memory in TASSCan", eError);
    return 0;
  }
  memset(myBuffer, 0, 10 + strlen(pBueffel));

  /* 
     parse loop
   */
  lastToken = NONE;
  lastVar = -1;
  qhPars = 0;
  pPtr = stptok(pBueffel, pToken, 20, " ,=");   /* step over sc */
  while (pPtr != NULL) {
    pPtr = stptok(pPtr, pToken, 20, " ,=");
    if (strlen(pToken) < 1 || pPtr == NULL)
      continue;
    switch (lastToken) {
    case NONE:
      if (tasNumeric(pToken)) {
        sprintf(pError,
                "ERROR: parse error at %s, expected variable", pToken);
        SCWrite(pCon, pError, eError);
        goto error;
      }
      lastToken = TXT;
      if ((iPtr = isTASVar(pToken)) >= 0) {
        if (isTASEnergy(pToken) >= 0) {
          /* energy variables are scan variables */
          strcpy(myBuffer, pToken);
          strcat(myBuffer, " ");
        } else {
          lastVar = iPtr;
        }
      } else {
        /* this is the motor case */
        strcpy(myBuffer, pToken);
        strcat(myBuffer, " ");
      }
      break;
    case TXT:
      if (tasNumeric(pToken)) {
        lastToken = NUM;
        if (qhPars == 3) {
          SCWrite(pCon, "DQH cannot have 2 values", eError);
          goto error;
        }
        qhPars = 0;
        if (lastVar >= 0) {
          vt = GetVarType(pTAS->tasPar[lastVar]);
          if (vt == veFloat) {
            VarSetFloat(pTAS->tasPar[lastVar], atof(pToken), usInternal);
            SCparChange(pCon);
          } else if (vt == veInt) {
            VarSetInt(pTAS->tasPar[lastVar],
                      (int) atof(pToken), usInternal);
            SCparChange(pCon);
          } else {
            pTAS->tasPar[lastVar]->text = strdup(pToken);
          }
          lastVar++;
        } else {
          strcat(myBuffer, pToken);
          strcat(myBuffer, " ");
        }
      } else {
        sprintf(pError, "ERROR: parse error at %s, expected number",
                pToken);
        SCWrite(pCon, pError, eError);
        goto error;
      }
      break;
    case NUM:
      if (tasNumeric(pToken)) {
        lastToken = NUM;
        if (lastVar >= 0) {
          if (qhPars == 0) {
            if (lastVar != DQK) {
              SCPrintf(pCon, eError, "ERROR: %s can have only one value",
                       tasVariableOrder[lastVar - 1]);
              goto error;
            }
            qhPars = 3;
          } else if (qhPars == 1) {
            SCWrite(pCon, "ERROR: DQH can have only 4 values", eError);
            goto error;
          } else {
            qhPars--;
          }

          vt = GetVarType(pTAS->tasPar[lastVar]);
          if (vt != veFloat) {
            SCPrintf(pCon, eError,
                     "ERROR: %s must be float (internal error)",
                     tasVariableOrder[lastVar]);
            goto error;
          }
          VarSetFloat(pTAS->tasPar[lastVar], atof(pToken), usInternal);
          SCparChange(pCon);
          lastVar++;
        } else {
          strcat(myBuffer, pToken);
          strcat(myBuffer, " ");
        }
      } else {
        lastToken = TXT;
        lastVar = -1;
        if ((iPtr = isTASVar(pToken)) >= 0) {
          if (isTASEnergy(pToken) >= 0) {
            /* energy variables are scan variables */
            strcat(myBuffer, pToken);
            strcat(myBuffer, " ");
          } else {
            lastVar = iPtr;
          }
        } else {
          /* this is the motor case */
          strcat(myBuffer, pToken);
          strcat(myBuffer, " ");
        }
      }
      break;
    }
  }
  if (qhPars == 3) {
    SCWrite(pCon, "DQH cannot have 2 values", eError);
    goto error;
  }
  strcpy(pBueffel, myBuffer);
  free(myBuffer);
  return 1;
error:
  free(myBuffer);
  return 0;
}

/*-------------------------------------------------------------------
EnterScanVar adds the scan variable given to the scan. Thereby it 
caluclate sthe start of the scan from the center given and NP. To this
and other purposes it locates the scan variable increment.
-----------------------------------------------------------------------*/

static int EnterScanVar(pTASdata pTAS, char *name, float fCenter,
                        SConnection * pCon)
{
  int iPtr;
  char pIncrement[30], pError[132];
  float fIncrement, fStart, fTemp;

  /*
     get increment
   */
  strcpy(pIncrement, "d");
  strcat(pIncrement, name);
  iPtr = isTASVar(pIncrement);
  if (iPtr < 0) {
    sprintf(pError, "ERROR: internal error, increment %s not found",
            pIncrement);
    SCWrite(pCon, pError, eError);
    return 0;
  }
  fIncrement = pTAS->tasPar[iPtr]->fVal;

  /*
     calculate start
   */
  fStart = fCenter - (pTAS->pScan->iNP / 2) * fIncrement;

  /* 
     If step > 0 save it for scaninfo
   */
  if (fIncrement < 0)
    fTemp = -fIncrement;
  else
    fTemp = fIncrement;
  if (fTemp > .001 && strlen(pTAS->scanVar) < 2) {
    sprintf(pTAS->scanVar, "%s,%f,%f", name, fStart, fIncrement);
  }

  /*
     put it in
   */
  return AddTASScanVar(pTAS->pScan, pTAS->pScan->pSics, pCon,
                       name, fStart, fIncrement);
}

/*----------------------------------------------------------------------
  ParseScanVars parses the scan variables and enters them into the
  scan. Thereby it takes care of the odd MAD syntax and off the fact that
  the values given to sc are the center of the scan whereas we need the
  start of the scan. Also ParseScanVars locates the increments for the
  various variables which are represented as SICS variables. They are 
  just the variable name prepended with a "d".
  ------------------------------------------------------------------------*/
static int ParseScanVar(pTASdata pTAS, char *pBueffel, SConnection * pCon,
                        int *iTas)
{
  char pToken[20], *pPtr, pError[132];
  int iPtr, lastToken, lastVar, lastMotor, status;
  VarType vt;
  int qhPars;

  /* 
     parse loop
   */
  lastToken = NONE;
  lastVar = lastMotor = -1;
  pPtr = pBueffel;
  qhPars = 0;
  while (pPtr != NULL) {
    pPtr = stptok(pPtr, pToken, 20, " ,=");
    if (strlen(pToken) < 1 || pPtr == NULL)
      continue;
    switch (lastToken) {
    case NONE:
      if (tasNumeric(pToken)) {
        sprintf(pError,
                "ERROR: parse error at %s, expected variable", pToken);
        SCWrite(pCon, pError, eError);
        return 0;
      }
      lastToken = TXT;
      if ((iPtr = isTASEnergy(pToken)) >= 0) {
        /* QE - variables are scan variables */
        lastVar = iPtr;
        (*iTas)++;
      } else if ((iPtr = isTASMotor(pToken)) >= 0) {
        /* as are motors */
        lastMotor = iPtr;
      } else {
        sprintf(pError, "ERROR: cannot scan %s", pToken);
        SCWrite(pCon, pError, eError);
        return 0;
      }
      break;
    case TXT:
      if (tasNumeric(pToken)) {
        lastToken = NUM;
        if (qhPars == 3) {
          SCWrite(pCon, "QH cannot have 2 values", eError);
          return 0;
        }
        qhPars = 0;
        if (lastVar >= 0) {
          /* 
             add energy variable
           */
          status = EnterScanVar(pTAS, tasVariableOrder[EI + lastVar],
                                atof(pToken), pCon);
          pTAS->tasPar[EI + lastVar]->fVal = atof(pToken);
          if (!status)
            return status;
          lastVar++;
        } else if (lastMotor >= 0) {
          /*
             add motor 
           */
          status = EnterScanVar(pTAS, tasMotorOrder[lastMotor],
                                atof(pToken), pCon);
          if (!status)
            return status;
          lastMotor++;
        } else {
          /* internal error */
          assert(0);
        }
      } else {
        sprintf(pError, "ERROR: parse error at %s, expected number",
                pToken);
        SCWrite(pCon, pError, eError);
        return 0;
      }
      break;
    case NUM:
      if (tasNumeric(pToken)) {
        lastToken = NUM;
        if (lastVar >= 0) {
          if (qhPars == 0) {
            if (EMIN + lastVar != QK) {
              SCPrintf(pCon, eError, "ERROR: %s can have only one value",
                       tasVariableOrder[EMIN + lastVar - 1]);
              return 0;
            }
            qhPars = 3;
          } else if (qhPars == 1) {
            SCWrite(pCon, "ERROR: QH can have only 4 values", eError);
            return 0;
          } else {
            qhPars--;
          }

          /* 
             add energy variable
           */
          status = EnterScanVar(pTAS, tasVariableOrder[EI + lastVar],
                                atof(pToken), pCon);
          pTAS->tasPar[EI + lastVar]->fVal = atof(pToken);
          if (!status)
            return status;
          lastVar++;
        } else {
          assert(lastMotor >= 0);
          SCPrintf(pCon, eError, "ERROR: %s can have only one value",
                   tasMotorOrder[lastMotor - 1]);
          return 0;
        }
      } else {
        lastToken = TXT;
        lastVar = lastMotor = -1;
        if ((iPtr = isTASEnergy(pToken)) >= 0) {
          /* QE - variables are scan variables */
          lastVar = iPtr;
          (*iTas)++;
        } else if ((iPtr = isTASMotor(pToken)) >= 0) {
          /* as are motors */
          lastMotor = iPtr;
        } else {
          sprintf(pError, "ERROR: cannot scan %s", pToken);
          SCWrite(pCon, pError, eError);
          return 0;
        }
      }
      break;
    }
  }
  if (qhPars == 3) {
    SCWrite(pCon, "QH cannot have 2 values", eError);
    return 0;
  }
  return 1;
}

/*-----------------------------------------------------------------------
 During a TAS scan additional output variables can be printed. These are
  defined in a text variable called ouput. ParseOutput now parses this
  variable and enters the variables found in there into the TAS data
  structure so that they can be printed during the scan.
  ------------------------------------------------------------------------*/
static void ParseOutput(pTASdata pTAS, SConnection * pCon)
{
  char *pText = NULL, *pPtr, pToken[20], pWarn[256];
  int iPtr;

  /*
     get a copy of output
   */
  pText =
      (char *) malloc((strlen(pTAS->tasPar[OUT]->text) + 10) *
                      sizeof(char));
  if (!pText) {
    SCWrite(pCon, "WARNING: out of memory while parsing output variable",
            eWarning);
    return;
  }
  strcpy(pText, pTAS->tasPar[OUT]->text);

  /*
     wash it
   */
  prepare2Parse(pText);

  /*
     parse
   */
  pTAS->addCount = 0;
  pPtr = pText;
  while (pPtr != NULL) {
    pPtr = stptok(pPtr, pToken, 20, " ,=");
    if (strlen(pToken) < 1 || pPtr == NULL)
      continue;

    if ((iPtr = isTASMotor(pToken)) >= 0) {
      pTAS->addOutput[pTAS->addCount] = iPtr;
      pTAS->addType[pTAS->addCount] = 1;
      pTAS->addCount++;
    } else if ((iPtr = isTASVar(pToken)) >= 0) {
      pTAS->addOutput[pTAS->addCount] = iPtr;
      pTAS->addType[pTAS->addCount] = 0;
      pTAS->addCount++;
    } else {
      sprintf(pWarn, "WARNING: ignored invalid token > %s < in output",
              pToken);
      SCWrite(pCon, pWarn, eWarning);
    }
  }
  free(pText);
}

/*--------------------------------------------------------------------------
  Now, after so many lines of code we are really ready to boogey: Parse
  the scan command line, set variables, set the scan up and then actually
  perform the scan. 
  --------------------------------------------------------------------------*/
int TASScan(SConnection * pCon, SicsInterp * pSics, void *pData,
            int argc, char *argv[])
{
  pTASdata pTAS = (pTASdata) pData;
  char pLine[1024], pToken[20], *pPtr, pWork[70];
  int i, status, iPtr, iTas;
  time_t iDate;
  struct tm *psTime;
  float ach;

  /* 
     check authorization
   */
  if (!SCMatchRights(pCon, usUser))
    return 0;

  if (pTAS->pScan->iActive) {
    SCWrite(pCon, "ERROR: a scan is still running", eError);
    return 0;
  }
  /*
     check if we are a fast scan or a normal scan
   */
  strtolower(argv[0]);
  if (strcmp(argv[0], "sf") == 0) {
    pTAS->iFast = 1;
  } else {
    pTAS->iFast = 0;
  }

  /* Initialize parsing */
  Arg2Text(argc, argv, pLine, 1023);
  strtolower(pLine);

  /*
     set command text for data file 
   */
  VarSetText(pTAS->tasPar[COM], pLine, usInternal);


  /* 
     first pass: parse simple variables
   */
  status = ParseVar(pTAS, pLine, pCon);
  if (!status)
    return status;

  /*
     clear out old scan
   */
  ClearScanVar(pTAS->pScan);
  pTAS->scanVar[0] = '\0';


  /*
     at this stage all simple variables have been set. Now we get the values
     of some important inital values which we need for entering the scan
     variables.
   */
  pTAS->pScan->iNP = pTAS->tasPar[NP]->iVal;
  pTAS->pScan->iMode = GetCounterMode(pTAS->pScan->pCounterData);
  pTAS->pScan->fPreset = GetCounterPreset(pTAS->pScan->pCounterData);
  pTAS->pScan->pCon = pCon;
  pTAS->pScan->pSics = pSics;

  /*
     check for polarisation 
   */
  if (pTAS->tasPar[LPA]->iVal == 1) {
    pTAS->iPOL = 1;
  } else {
    pTAS->iPOL = -1;
  }

  /* 
     now parse the scan variables 
   */
  iTas = 0;
  status = ParseScanVar(pTAS, pLine, pCon, &iTas);
  if (!status)
    return status;

  /*
     check if there is something to scan
   */
  if (pTAS->pScan->iScanVar < 1) {
    SCWrite(pCon, "ERROR: nothing to scan", eError);
    return 0;
  }

  /*
     now there is enough information to set the scaninfo variable which
     is needed to help the status display.
   */
  sprintf(pWork, "%d,%s", pTAS->pScan->iNP, pTAS->scanVar);
  VarSetText(pTAS->tasPar[SINFO], pWork, 0);

  /*
     parse the output variable in order to get additional stuff to write 
   */
  ParseOutput(pTAS, pCon);

  /* 
     print some status information for the looser
   */
  iDate = time(NULL);
  psTime = localtime(&iDate);
  memset(pWork, 0, 69);
  strftime(pWork, 69, "%d-%b-%Y %H:%M:%S", psTime);
  snprintf(pLine, 1023, "%8s   %3d Points Scan %3d Scanned Variable(s) %s",
           pTAS->tasPar[INST]->text,
           pTAS->pScan->iNP, pTAS->pScan->iScanVar, pWork);
  SCWrite(pCon, pLine, eLog);
  if (GetCounterMode(pTAS->pScan->pCounterData) == eTimer) {
    sprintf(pLine, "     %8f Seconds per point",
            GetCounterPreset(pTAS->pScan->pCounterData));
  } else {
    sprintf(pLine, "     %8f Monitor Counts per point",
            GetCounterPreset(pTAS->pScan->pCounterData));

  }
  SCWrite(pCon, pLine, eLog);
  SCWrite(pCon, " ", eWarning);
  SCWrite(pCon, " ", eWarning);
  sprintf(pLine,
          " %12s %6s      HKLE: %5.2f %5.2f %5.2f %8.4f %6.3f %6.3f %6.3f %6.3f",
          pTAS->tasPar[TIT]->text,
          pTAS->tasPar[USR]->text,
          pTAS->tasPar[QH]->fVal -
          (pTAS->pScan->iNP / 2) * pTAS->tasPar[DQH]->fVal,
          pTAS->tasPar[QK]->fVal -
          (pTAS->pScan->iNP / 2) * pTAS->tasPar[DQL]->fVal,
          pTAS->tasPar[QL]->fVal -
          (pTAS->pScan->iNP / 2) * pTAS->tasPar[DQK]->fVal,
          pTAS->tasPar[EN]->fVal -
          (pTAS->pScan->iNP / 2) * pTAS->tasPar[DEN]->fVal,
          pTAS->tasPar[DQH]->fVal, pTAS->tasPar[DQK]->fVal,
          pTAS->tasPar[DQL]->fVal, pTAS->tasPar[DEN]->fVal);
  SCWrite(pCon, pLine, eLog);
  if (iTas > 0) {
    if (pTAS->tasPar[FX]->iVal == 1) {
      sprintf(pLine, "   Constant KI Scan: KI= %7.4f Angs-1; EI= %9.4f",
              pTAS->tasPar[KI]->fVal, pTAS->tasPar[EI]->fVal);
    } else {
      sprintf(pLine, "   Constant KF Scan: KF= %7.4f Angs-1; EF= %9.4f",
              pTAS->tasPar[KF]->fVal, pTAS->tasPar[EF]->fVal);
    }
    SCWrite(pCon, pLine, eLog);
  }

  /*
    check ACH
  */
  ach = readDrivable("ach",pCon);
  if(ach < .05) {
    SCWrite(pCon,"WARNING: Analyser is flat",eWarning);
  }

  /*
     set our scan functions
   */
  SetTASScan(pTAS->pScan, pTAS);


  /*
     finally scan
   */
  status = DoScan(pTAS->pScan,
                  pTAS->pScan->iNP,
                  pTAS->pScan->iMode, pTAS->pScan->fPreset, pSics, pCon);

  /*
     cleanup a little
   */
  ResetScanFunctions(pTAS->pScan);

  /*
     print a message for Severian Gvassilja
   */
  sprintf(pLine, "Scan finished, data saved to %s", pTAS->pScan->pFile);
  SCWrite(pCon, pLine, eLog);

  return status;
}
