/*--------------------------------------------------------------------------
                             H K L S C A N

 A class for doing scans in reciprocal space for a four circle 
 diffractometer. All the hard work is done in the scan and hkl objects.

 copyright: see copyright.h

 Mark Koennecke, June 1999
 --------------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <tcl.h>
#include "fortify.h"
#include "sics.h"
#include "scan.h"
#include "hkl.h"
#include "scan.i"
#include "hkl.i"
#include "hklscan.i"
#include "hklscan.h"

/*-----------------------------------------------------------------------*/
  static void KillHklscan(void *pData)
  {
      pHklscan self = NULL;

      self = (pHklscan)pData;
      if(!self)
      {
        return;
      }
      if(self->pDes)
      {
           DeleteDescriptor(self->pDes);
      }
      free(self);
  }
/*------------------------------------------------------------------------*/
  int HklscanFactory(SConnection *pCon, SicsInterp *pSics, void *pData,
                     int argc, char *argv[4])
  {
      pHklscan pNew = NULL;
      char pBueffel[512];
      int iRet;

      /* check number of arguments */
      if(argc < 3)
      {
         SCWrite(pCon,
           "ERROR: expected two arguments to MakeHklscan",eError);
         return 0;
      }

      /* allocate space */
      pNew = (pHklscan)malloc(sizeof(sHklscan));
      if(!pNew)
      {
         SCWrite(pCon,"ERROR: out of memory in MakeHklscan",eError);
         return 0;
      }
      memset(pNew,0,sizeof(sHklscan));
      pNew->pDes = CreateDescriptor("Hklscan");

      /* find scan object */
      pNew->pScan = (pScanData)FindCommandData(pSics,argv[1],"ScanObject");
      if(!pNew->pScan)
      {
         sprintf(pBueffel,"ERROR: %s not found or no scan object",argv[1]);
         SCWrite(pCon,pBueffel,eError);
         KillHklscan(pNew);
         return 0;
      }

      /* find HKL object */
      pNew->pCalc = (pHKL)FindCommandData(pSics,argv[2],
                    "4-Circle-Calculus");
      if(!pNew->pCalc)
      {
         sprintf(pBueffel,"ERROR: %s not found or no HKL object",argv[2]);
         SCWrite(pCon,pBueffel,eError);
         KillHklscan(pNew);
         return 0;
      }

      /* alright. Install command */
      iRet = AddCommand(pSics, "hklscan",
                        HklscanAction,KillHklscan,pNew);
      if(!iRet)
      {
         SCWrite(pCon,"ERROR: duplicate command hklscan NOT created",eError);
         KillHklscan(pNew);
         return 0;
      }
      return 1;
  }
/*------------------------------------------------------------------------*/
  static int HklscanDrive(pScanData pScan, int iPoint)
  {
     pHklscan self = NULL;
     int i, iRet;

     assert(pScan);
     self = (pHklscan)pScan->pSpecial;
     assert(self);

     /* calculate new Positions */ 
     for(i = 0; i < 3; i++)
     {
        self->fPos[i] = self->fStart[i] + iPoint * self->fStep[i]; 
     }
     iRet = DriveHKL(self->pCalc, self->fPos, 0.,0,pScan->pCon);
     if(!iRet)
     {
         SCWrite(pScan->pCon,"ERROR: aborting hklscan",eError);
     }
     return iRet;
  }
/*------------------------------------------------------------------------*/
  static int WriteHklscanPoint(pScanData self, int iPoint)
  {
    pHklscan pHaSca = NULL;
    int i, i2;
    char pLine[512], pItem[30], pInfo[512];
    pVarEntry pVar = NULL;
    pCountEntry pData = NULL;
    void *pPtr = NULL;

    assert(self);
    pHaSca = (pHklscan)self->pSpecial;
    assert(pHaSca);

    
    assert(self->pCon);
    assert(self->pSics);
    assert(self->fd);
    
    /* jump to end of header */
    fseek(self->fd,self->lPos, SEEK_SET);
    if(self->iChannel != 0)
    {
      fprintf(self->fd,"WARNING: Scanning monitor %d\n",self->iChannel);
    }
    
    /* make the data header */
    sprintf(pLine,"%-5s","NP");
    strcpy(pInfo,"Scanning Variables: H, K, L");
    strcat(pLine,"H     K     L     ");
    for(i = 0; i < self->iScanVar;i++)
    {
      DynarGet(self->pScanVar,i,&pPtr);
      pVar = (pVarEntry)pPtr;
      if(pVar)
      {
        sprintf(pItem,"%-6.6s ",pVar->Name);
        strcat(pLine,pItem);
      }
    }
    strcat(pLine,"Counts      ");
    strcat(pLine,"Monitor1    ");
    sprintf(pItem,"\n%d Points,",self->iNP);
    strcat(pInfo,pItem);
    if(self->iMode == eTimer)
    {
       strcat(pInfo," Mode: Timer,");
    }
    else
    {
      strcat(pInfo," Mode: Monitor,");
    }
    sprintf(pItem," Preset %f",self->fPreset);
    strcat(pInfo,pItem);
    fprintf(self->fd,"%s\n",pInfo);    
    fprintf(self->fd,"%s\n",pLine);

    /* print an addon to the status line going to the screen */
    sprintf(pLine,"NP   H     K     L ");
    SCWrite(self->pCon,pLine,eWarning);
    sprintf(pLine,"%-5d%-6.2f%-6.2f%-6.2f ",iPoint,pHaSca->fPos[0],
            pHaSca->fPos[1], pHaSca->fPos[2]);
    SCWrite(self->pCon,pLine,eWarning);

    /* now the scan points */
    for(i = 0; i < self->iCounts; i++)
    {
      sprintf(pLine,"%-5d",i);
      /* print HKL */
      for(i2 = 0; i2 < 3; i2++)
      {
         sprintf(pItem,"%-6.2f",pHaSca->fStart[i2] + i * pHaSca->fStep[i2]);
         strcat(pLine,pItem);
      }
      /* print chi, ph, om */
      for(i2 = 0; i2 < self->iScanVar; i2++)
      {
         DynarGet(self->pScanVar,i2,&pPtr);
         pVar = (pVarEntry)pPtr;
         if(pVar)
         {
           sprintf(pItem,"%-7.2f",pVar->fData[i]);
           strcat(pLine,pItem);
         }
      }
      /* print Counts & Monitor */
      DynarGet(self->pCounts,i,&pPtr);
      pData = (pCountEntry)pPtr;
      if(pData)
      {
        sprintf(pItem,"%-12ld",pData->lCount);
        strcat(pLine,pItem);
        sprintf(pItem,"%-12ld",pData->Monitors[0]);
        strcat(pLine,pItem);
      }
      fprintf(self->fd,"%s\n",pLine);
    }
   
    /* done */
    fprintf(self->fd,"END-OF-DATA\n");
    fclose(self->fd);
    self->fd = NULL;
    return 1;
  }
/*------------------------------------------------------------------------*/
  int Hklscan(pHklscan self, SConnection *pCon, 
             int iNP, int iMode, float fPreset)
  {
     int iRet;
     float fVal;

     assert(self);

     /* configure the scan thing for our bizarre purpose */
     ClearScanVar(self->pScan);
     MotorGetPar(self->pCalc->pTheta, "softlowerlim",&fVal);
     fVal += .5;
     AddScanVar(self->pScan,pServ->pSics,pCon,self->pCalc->pTheta->name,
                fVal,0.);
     MotorGetPar(self->pCalc->pOmega, "softlowerlim",&fVal);
     fVal += .5;
     AddScanVar(self->pScan,pServ->pSics,pCon,self->pCalc->pOmega->name,
                fVal,0.);
     MotorGetPar(self->pCalc->pChi, "softlowerlim",&fVal);
     fVal += .5;
     AddScanVar(self->pScan,pServ->pSics,pCon,self->pCalc->pChi->name,
                fVal,0.);
     MotorGetPar(self->pCalc->pPhi, "softlowerlim",&fVal);
     fVal += .5;
     AddScanVar(self->pScan,pServ->pSics,pCon,self->pCalc->pPhi->name,
                fVal,0.);
     self->pScan->WriteScanPoints = WriteHklscanPoint;
     self->pScan->ScanDrive = HklscanDrive;
     self->pScan->pSpecial = self;

     /* scan */
     iRet = DoScan(self->pScan,iNP, iMode, fPreset, pServ->pSics,pCon);

     /* unset all */
     ClearScanVar(self->pScan);
     ResetScanFunctions(self->pScan);
     self->pScan->pSpecial = NULL;

     return iRet;
  }
/*-------------------------------------------------------------------------*/
  int HklscanAction(SConnection *pCon, SicsInterp *pSics, void *pData,
                    int argc, char *argv[])
  {
      char pBueffel[512];
      int iRet, iMode, iNP;
      pHklscan self = NULL;
      double dVal;
      float fPreset;

      assert(pCon);
      assert(pSics);
      assert(pData);

      self = (pHklscan)pData;

      /* check for the command word */
      if(argc < 2)
      {
        SCWrite(pCon,"ERROR: Insufficient number of arguments to hklscan",
                eError);
        return 0;
      }

      /* do work according to command word */
      strtolower(argv[1]);
      if(strcmp(argv[1],"start") == 0)
      {
         if(!SCMatchRights(pCon,usUser))
	 {
           return 0;
         }
         if(argc < 5)
	 {
             SCWrite(pCon,
               "Insufficient number of  arguments to hklscan",
                eError);
             return 0;
         }
         iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStart[0] = (float)dVal;
         iRet = Tcl_GetDouble(pSics->pTcl,argv[3],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStart[1] = (float)dVal;
         iRet = Tcl_GetDouble(pSics->pTcl,argv[4],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[4]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStart[2] = (float)dVal;
         SCSendOK(pCon);
         return 1;
      }
      else if(strcmp(argv[1],"step") == 0)
      {
         if(!SCMatchRights(pCon,usUser))
	 {
           return 0;
         }
         if(argc < 5)
	 {
             SCWrite(pCon,
               "Insufficient number of  arguments to hklscan",
                eError);
             return 0;
         }
         iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStep[0] = (float)dVal;
         iRet = Tcl_GetDouble(pSics->pTcl,argv[3],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStep[1] = (float)dVal;
         iRet = Tcl_GetDouble(pSics->pTcl,argv[4],&dVal);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[4]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         self->fStep[2] = (float)dVal;
         SCSendOK(pCon);
         return 1;
      }
      else if(strcmp(argv[1],"run") == 0)
      {
         if(!SCMatchRights(pCon,usUser))
	 {
           return 0;
         }
         if(argc < 5)
	 {
             SCWrite(pCon,
               "Insufficient number of  arguments to hklscan run",
                eError);
             return 0;
         }
         iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iNP);
         if(iRet != TCL_OK)
	 {
             sprintf(pBueffel,"ERROR: failed to convert %s to number",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
         }
         /* interpret Mode */
         if(strcmp(argv[3],"timer") == 0)
         {
             iMode = eTimer;
         }
         else if(strcmp(argv[3],"monitor") == 0)
         {
             iMode = ePreset;
         }
         else
         {
             sprintf(pBueffel,"ERROR: %s not recognized as valid counter mode",
                     argv[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;         
         }
         /* preset */
         iRet = Tcl_GetDouble(pSics->pTcl,argv[4],&dVal);
         if(iRet != TCL_OK)
         {
             sprintf(pBueffel,"ERROR: expected number, got %s",argv[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;        
         }
         fPreset = (float)dVal;
         iRet = Hklscan(self,pCon,iNP,iMode,fPreset);
         if(iRet)
	 {
            SCSendOK(pCon);
	 }
         return iRet;
      }
      sprintf(pBueffel,
        "ERROR: %s not recognized as command word to hklscan",argv[1]);
      SCWrite(pCon,pBueffel,eError);
      return 0;
  } 
