/*-------------------------------------------------------------------------
                             D I F R A C

 A four-circle diffractometer requires sophicticated procedures for 
 searching peaks, orienting crystals and for performing data collection.
 Rather then invent all of this again the DIFRAC-F77 program written by
 Peter White and Eric Gabe has been incoporated into SICS. This module
 provides the C-language side of the interface between DIFRAC and SICS.
 DIFRAC can only be included once into SICS, it is not possible to have
 more then one copy of this. This is why there is file static global data
 here.

 copyright: see copyright.h

 Mark Koennecke, November 1999
 --------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include "fortify.h"
#include "sics.h"
#include "motor.h"
#include "counter.h"
#include "status.h"
#include "splitter.h"
#include "difrac.h"

/*--------------------------------------------------------------------------
  In order to deal with multiple request to the DIFRAC subsystem we need to
  keep the connection objects on a stack. This stack is defined here.
  ---------------------------------------------------------------------------*/
#define MAXSTACK 50

  static SConnection *ConStack[MAXSTACK];
  static int  iConStackPtr = -1;

/*--------------------------------------------------------------------------
  In order to do the four circle work we need to know the motors of the 
  eulerian cradle and access to the counter. Furthermore we need to know
  about the omega2theta motor and the scanning routine. 
  These data structures are initialized by the installation routine.
  ---------------------------------------------------------------------------*/

   static pMotor pTTH, pOM, pCHI, pPHI;
   static pCounter counter; 
/*---------------------------------------------------------------------------
  The following routines will be called from F77. Their names take care of the
  system dependent name mangling scheme for calling C from F77. This may
  need adjustment when porting to another system
  ---------------------------------------------------------------------------*/
/*========= read angles */
  void sicsanget_(float *fTH, float *fOM, float *fCHI, float *fPHI)
  {
     int iRet;

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         return;
     }
     if(ConStack[iConStackPtr] == NULL)
     {
        return;
     }

     iRet = MotorGetSoftPosition(pTTH,ConStack[iConStackPtr],fTH);
     if(iRet != 1)
     {
        SCWrite(ConStack[iConStackPtr],
                "ERROR: failed to read two theta, DIFRAC may be confused now",
                eError);
     }
     iRet = MotorGetSoftPosition(pOM,ConStack[iConStackPtr],fOM);
     if(iRet != 1)
     {
        SCWrite(ConStack[iConStackPtr],
                "ERROR: failed to read omega, DIFRAC may be confused now",
                eError);
     }
     iRet = MotorGetSoftPosition(pCHI,ConStack[iConStackPtr],fCHI);
     if(iRet != 1)
     {
        SCWrite(ConStack[iConStackPtr],
                "ERROR: failed to read chi, DIFRAC may be confused now",
                eError);
     }
     iRet = MotorGetSoftPosition(pPHI,ConStack[iConStackPtr],fPHI);
     if(iRet != 1)
     {
        SCWrite(ConStack[iConStackPtr],
                "ERROR: failed to read two theta, DIFRAC may be confused now",
                eError);
     }
  }
#define ABS(x) (x < 0 ? -(x) : (x))    
/*=========== check angles */
  void sicsangcheck_(float *fTH, float *fOM, float *fCHI, float *fPHI, 
                     int *iInvalid)
  {
     int iRet;
     SConnection *pCon = NULL;
     float fHard;
     char pBueffel[256], pError[131];

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         return;
     }
     if(ConStack[iConStackPtr] == NULL)
     {
        return;
     }
     pCon = ConStack[iConStackPtr];

     *iInvalid = 0;
     iRet = MotorCheckBoundary(pTTH,*fTH,&fHard,pError,131);
     if(iRet != 1)
     {
       sprintf(pBueffel,
        "ERROR: %6.2f %6.2f %6.2f %6.2f violates twotheta limits",
        *fTH, *fOM, *fCHI, *fPHI);
       SCWrite(pCon,pBueffel,eError);
       *iInvalid = 4;
       return;
     }
     iRet = MotorCheckBoundary(pOM,*fOM,&fHard,pError,131);
     if(iRet != 1)
     {
       sprintf(pBueffel,
        "ERROR: %6.2f %6.2f %6.2f %6.2f violates omega limits",
        *fTH, *fOM, *fCHI, *fPHI);
       SCWrite(pCon,pBueffel,eError);
       *iInvalid = 4;
       return;
     }
     iRet = MotorCheckBoundary(pCHI,*fCHI,&fHard,pError,131);
     if(iRet != 1)
     {
       sprintf(pBueffel,
        "ERROR: %6.2f %6.2f %6.2f %6.2f violates chi limits",
        *fTH, *fOM, *fCHI, *fPHI);
       SCWrite(pCon,pBueffel,eError);
       *iInvalid = 4;
       return;
     }
     iRet = MotorCheckBoundary(pPHI,*fPHI,&fHard,pError,131);
     if(iRet != 1)
     {
       sprintf(pBueffel,
        "ERROR: %6.2f %6.2f %6.2f %6.2f violates phi limits",
        *fTH, *fOM, *fCHI, *fPHI);
       SCWrite(pCon,pBueffel,eError);
       *iInvalid = 4;
       return;
     }
  }
/*======== set angles */
  void sicsangset_(float *fTTH, float *fOM, float *fCHI, float *fPHI, 
                   int *icol)
  {
     pDummy pDum;
     int iRet;
     SConnection *pCon = NULL;
     float fT1, fT2, fT3, fT4;

     *icol = 0;

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         return;
     }
     if(ConStack[iConStackPtr] == NULL)
     {
        return;
     }
     pCon = ConStack[iConStackPtr];


     /* check if this is possible, if not complain */
     sicsangcheck_(fTTH, fOM,fCHI,fPHI, &iRet);
     if(iRet >= 4)
     {
        *icol = 1;
        return;
     }

     /* start */
     pDum = (pDummy)pTTH;
     iRet = StartDevice(pServ->pExecutor, "TTH",
                        pDum->pDescriptor, pDum,pCon, *fTTH);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
        StopExe(pServ->pExecutor,"all");
        *icol = 10;
     }           
     pDum = (pDummy)pOM;
     iRet = StartDevice(pServ->pExecutor, "OM",
                        pDum->pDescriptor, pDum,pCon, *fOM);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start omega motor",eError);
        StopExe(pServ->pExecutor,"all");
        *icol = 10;
     }           
     pDum = (pDummy)pCHI;
     iRet = StartDevice(pServ->pExecutor, "CHI",
                        pDum->pDescriptor, pDum,pCon, *fCHI);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start chi motor",eError);
        StopExe(pServ->pExecutor,"all");
        *icol = 10;
     }           
     pDum = (pDummy)pPHI;
     iRet = StartDevice(pServ->pExecutor, "PHI",
                        pDum->pDescriptor, pDum,pCon, *fPHI);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
        StopExe(pServ->pExecutor,"all");
        *icol = 10;
     }           

     /* wait for end of it */
      iRet = Wait4Success(pServ->pExecutor);
      switch(iRet)
      {
         case DEVINT:
                 if(SCGetInterrupt(pCon) == eAbortOperation)
                 {
                       SCSetInterrupt(pCon,eContinue);
                       SCSetError(pCon,OKOK);
                 }
                 break;
          case DEVDONE:
                 break;
          default:
                 break;                          
      }   

      /*
        As TRICS has such a shitty cradle check angles and report error
        if bad
      */
      sicsanget_(&fT1, &fT2, &fT3, &fT4);
      if(ABS(fT1 - *fTTH) > .2)
      {
        *icol = 10;
      }
      if(ABS(fT2 - *fOM) > .2)
      {
        *icol = 10;
      }
      if(ABS(fT3 - *fCHI) > .2)
      {
        *icol = 10;
      }
      if(ABS(fT4 - *fPHI) > .2)
      {
        *icol = 10;
      }
  }
/*=========== count */
  void sicscount_(float *fPreset, float *fCounts)
  {
    pDummy pDum;
    int iRet;
    SConnection *pCon = NULL;
    long lTask;

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         return;
     }
    if(ConStack[iConStackPtr] == NULL)
    {
        return;
    }
    pCon = ConStack[iConStackPtr];

    pDum = (pDummy)counter;
    SetCounterPreset(counter,*fPreset);
    iRet = StartDevice(pServ->pExecutor,
		       "DifracCount",
		       pDum->pDescriptor,
		       counter,
		       pCon,
		       *fPreset);
    if(!iRet)
    {
	SCWrite(pCon,"ERROR: Failed to start counting ",eError);
	return;
    }
    SetStatus(eCounting);
    /* wait for finish */
    lTask = GetDevexecID(pServ->pExecutor);
    if(lTask > 0);
    {
      TaskWait(pServ->pTasker,lTask);
    }
    *fCounts = (float)GetCounts(counter,pCon);
  }
/*========= sicswrite */
  void sicswrite_(int *iText, int *iLen)
  {
    SConnection *pCon = NULL;
    char pBueffel[256];
    int i;

    if(*iLen > 255)
      return;

    for(i = 0; i < *iLen; i++)
    {
      pBueffel[i] = (char)iText[i];
    } 
    pBueffel[i] = '\0';

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         puts(pBueffel);
         return;
     }
    if(ConStack[iConStackPtr] == NULL)
    {
        puts(pBueffel);
        return;
    }
    pCon = ConStack[iConStackPtr];
     
    SCWrite(pCon,pBueffel,eValue);
  }
/*========== sicsgetline */
  void sicsgetline_(int *iText, int *iLen)
  {
    SConnection *pCon = NULL;
    char pBueffel[256];
    int i, iRet;

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         return;
     }
    if(ConStack[iConStackPtr] == NULL)
    {
        return;
    }
    pCon = ConStack[iConStackPtr];
     
    iRet = SCPrompt(pCon,"Enter data please >>" , pBueffel, 255);
    /* difrac cannot handle an interrupted input operation */
    if(iRet == 0)
    {
        SCSetInterrupt(pCon,eContinue);
    }
    for(i = 0; i < strlen(pBueffel); i++)
    {
       iText[i] = (int)pBueffel[i];
    }
    *iLen = strlen(pBueffel);
  }
/*============= checkint */
  void checkint_(int *iK)
  {
    SConnection *pCon = NULL;
    char pBueffel[256];
    int i;

    /* this is just security, may never happen */
     if(iConStackPtr < 0)
     {
         *iK = 0;
         return;
     }
    if(ConStack[iConStackPtr] == NULL)
    {
        return;
    }
    pCon = ConStack[iConStackPtr];

    if(SCGetInterrupt(pCon) >= eAbortScan)
    {
       *iK = 0;
    }    
    else 
    {
       *iK = 1;
    }
  }
/*--------------------------------------------------------------------------
  DifracAction is the interface routine between the SICS interpreter and 
  the DIFRAC subsystem. What it basically does is: pop the connection onto 
  the stack. Concatenate all pending command line data to a string. Then
  call DIFRAC with this string as an parameter. On return, remove the
  connection from the stack again and return.
  -------------------------------------------------------------------------*/
/* some protoypes for things defined in F77 */

  extern void difini_(void);
  extern void difint_(int *iText, int *iLen);

  int DifracAction(SConnection *pCon, SicsInterp *pSics, void *pData,
                   int argc, char *argv[])
  {
      char pInput[256];
      int iInput[256];
      int iLen, i;


      if(argc < 2)
      {
         SCWrite(pCon,"ERROR: dif expects at least one argument",eError);
         return 0;
      }

      /* user privilege required */
      if(!SCMatchRights(pCon,usUser))
      {
        return 0;
      }

      /* steal: redirect the I/O to me */
      strcpy(pInput,argv[1]);
      strtolower(pInput);
      if(strcmp(pInput,"steal") == 0)
      {
        if(iConStackPtr >= 0)
	{
          ConStack[iConStackPtr] = pCon;
        }
        SCSendOK(pCon);
        return 1;
      }
      
      iConStackPtr++;
      ConStack[iConStackPtr] = pCon;      
   
      Arg2Text(argc-1, &argv[1],pInput,255);
      iLen = strlen(pInput);
      for(i = 0; i < iLen; i++)
      {  
         iInput[i] = toupper((int)pInput[i]);
      }

      /* do difrac */      
      difint_(iInput, &iLen);      
      SCWrite(pCon,"Difrac subsystem finished",eWarning);
      
      iConStackPtr--;
      if(SCGetInterrupt(pCon) != eContinue)
      {
          return 0;
      }
      else
      {
          return 1;
      }
  }
/*-------------------- The initialization routine ----------------------*/
  int MakeDifrac(SConnection *pCon, SicsInterp *pSics, void *pData,
                 int argc, char *argv[])
  {
     CommandList *pCom = NULL;
     pICountable pCts = NULL;
     int iRet;
     
     if(argc < 6)
     {
       SCWrite(pCon,
         "ERROR: Insufficient number of arguments to MakeDifrac",eError);
       return 0;
     }
     
     /* find motors */
     pTTH = FindMotor(pSics,argv[1]);
     if(!pTTH)
     {
        SCWrite(pCon,"ERROR: cannot find two theta motor",eError);
        return 0;
     }
     pOM = FindMotor(pSics,argv[2]);
     if(!pOM)
     {
        SCWrite(pCon,"ERROR: cannot find omega motor",eError);
        return 0;
     }
     pCHI = FindMotor(pSics,argv[3]);
     if(!pTTH)
     {
        SCWrite(pCon,"ERROR: cannot find chi motor",eError);
        return 0;
     }
     pPHI = FindMotor(pSics,argv[4]);
     if(!pTTH)
     {
        SCWrite(pCon,"ERROR: cannot find phi motor",eError);
        return 0;
     }

     /* locate counter */
      pCom = FindCommand(pSics,argv[5]);
      if(pCom == NULL)
      {
         SCWrite(pCon,"ERROR: counter not found in MakeDifrac",
                 eError);
         return 0;        
      }
      pCts = GetCountableInterface(pCom->pData);
      if(!pCts)
      {
        SCWrite(pCon,"ERROR: argument to MakeDifrac is no counter",
                eError);
        return 0;
      }
      counter = (pCounter)pCom->pData;

      /* initialize difrac */
      difini_();

      /* install command */
      iRet = AddCommand(pSics,
                        "dif",
                        DifracAction,
                        NULL,
                        NULL);
       if(!iRet)
       {
          SCWrite(pCon,"ERROR: duplicate command dif NOT created",
                  eError);
       }      
       return iRet;
  }
   
