/*--------------------------------------------------------------------------
         
                             N X A M O R

  Routines for writing NeXus files for the reflectometer AMOR at PSI.

  copyright: see copyright.h


  Mark Koennecke, September 1999
--------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include "fortify.h"
#include "sics.h"
#include "nxdict.h"
#include "nxutil.h"
#include "scan.h"
#include "scan.i"
#include "HistMem.h"
#include "counter.h"
#include "nxamor.h"

/* some defines for some names */
#define AMORDICT "amor.dic"
#define INSTNAME "AMOR at SINQ, PSI"
#define SOURCENAME "Spallation source SINQ"
#define SOURCETYPE "Continous flux spallation source"
#define CHOPPERNAME "Dornier Chopper System"
/* 
   this is the real detector size, the one in the dictionary will be
   updated from this!
*/
#define DETSIZE 128
/*------------------------------------------------------------------------*/
  static void WriteDiaphragm(NXhandle hfil, NXdict hdict, int i, 
                             SConnection *pCon)
  {
    char pThing[30];

    sprintf(pThing,"d%1.1ddist",i);
    SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,pThing,pThing);

    sprintf(pThing,"d%1.1dt",i);
    SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,pThing,pThing);
    sprintf(pThing,"d%1.1db",i);
    SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,pThing,pThing);
    sprintf(pThing,"d%1.1dl",i);
    SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,pThing,pThing);
    sprintf(pThing,"d%1.1dr",i);
    SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,pThing,pThing);
  }
/*----------------------------------------------------------------------*/
  int WriteAmorHeader(char *file, SConnection *pCon)
  {
       NXhandle hfil;
       NXdict hdict;
       int iRet;
       char pBueffel[512];
       CounterMode eMode;
       CommandList *pCom = NULL;
       float fVal;

       /* open files */
       iRet = NXopen(file,NXACC_CREATE,&hfil);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open file %s for writing",file);
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }
       iRet = NXDinitfromfile(AMORDICT,&hdict);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open dictionary file %s");
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }
 
       /* put some global information */
       SNXSPutGlobals(hfil,file,"AMOR",pCon);
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"etitle","title");
       SNXFormatTime(pBueffel,512);
       NXDputalias(hfil,hdict,"estart",pBueffel);

       /* instrument vGroup */
       NXDputalias(hfil,hdict,"iname",INSTNAME);

       /* source */
       NXDputalias(hfil,hdict,"sname",SOURCENAME);
       NXDputalias(hfil,hdict,"stype",SOURCETYPE);

       /* chopper */
       NXDputalias(hfil,hdict,"cname",CHOPPERNAME);
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"crot",
                       "chopperrotation");

       /* frame overlap mirror */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"fomname",
                       "fomname");
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"fodist",
                       "fomdist");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"fomh","ftz");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"fomom","fom");

       /* first Diaphragm */
       WriteDiaphragm(hfil,hdict,1,pCon);

       /* polarizing, monochromating mirror */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"polname",
                       "polname");
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"poldist",
                       "poldist");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"polz","moz");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"polom","mom");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"poly","mty");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"polzom","mtz");

       /* second Diaphragm */
       WriteDiaphragm(hfil,hdict,2,pCon);

       /* third Diaphragm */
       WriteDiaphragm(hfil,hdict,3,pCon);

       /* sample table */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"saname",
                       "sample");
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"stdist",
                       "sampledist");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"somheight","stz");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"schi","sch");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"somega","som");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"stheight","soz");

       /* fourth Diaphragm */
       WriteDiaphragm(hfil,hdict,4,pCon);
      
       /* analyzer */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"anname",
                       "ananame");
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"andist",
                       "anadist");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"anoz","atz");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"anom","aom");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"antz","aoz");

       /* fifth Diaphragm!!!!!!!!! */
       WriteDiaphragm(hfil,hdict,5,pCon);
       
       /* counting data */
       pCom = FindCommand(pServ->pSics,"counter");
       if(pCom)
       {
         if(pCom->pData)
	 {
            eMode = GetCounterMode((pCounter)pCom->pData);
            if(eMode == eTimer)
            {
              strcpy(pBueffel,"timer");
            }           
            else
            {
              strcpy(pBueffel,"monitor");
            }
            NXDputalias(hfil,hdict,"cnmode",pBueffel);
            fVal = GetCounterPreset((pCounter)pCom->pData);
            NXDputalias(hfil,hdict,"cnpreset",&fVal);
	 }       
       }
       else
       {
           SCWrite(pCon,"WARNING: failed to find counter!",eWarning);
       }
       NXclose(&hfil);
       NXDclose(hdict,NULL);

       return 1;      
  }
/*------------------------------------------------------------------------*/
  int WriteAmorScan(char *file, SConnection *pCon, pScanData pScan)
  {
       NXhandle hfil;
       NXdict hdict;
       int iRet, i;
       char pBueffel[512], pDefinition[1024];
       CommandList *pCom = NULL;
       float fVal;
       float *fAxis = NULL;
       long *lData  = NULL;
       int  *iData  = NULL;

       /* open files */
       iRet = NXopen(file,NXACC_RDWR,&hfil);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open file %s for writing");
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }
       iRet = NXDinitfromfile(AMORDICT,&hdict);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open dictionary file %s");
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }

       /* write general detector data */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"sdetdist",
                       "detectordist");
       

       /* allocate memory for writing scan data */
       fAxis = (float *)malloc(pScan->iNP*sizeof(float));       
       lData = (long *)malloc(pScan->iNP*sizeof(long));
       iData = (int *)malloc(pScan->iNP*sizeof(int));
       if( (!fAxis) || (!lData) || (!iData) )
       {
          SCWrite(pCon,"ERROR: out of memory in WriteAmorScan",eError);
          return 0;
       }       
       memset(fAxis,0,pScan->iNP*sizeof(float));
       memset(lData,0,pScan->iNP*sizeof(long));
       memset(iData,0,pScan->iNP*sizeof(int));

       /* write scan variables */
       for(i = 0; i < pScan->iScanVar; i++)
       {
	 /* build definition string */
          NXDget(hdict,"scanroot",pBueffel, 512);
          strcpy(pDefinition,pBueffel);
          strcat(pDefinition,"  ");
          GetScanVarName(pScan,i,pBueffel,512);
          strcat(pDefinition,pBueffel);
          sprintf(pBueffel," -rank 1 -dim {%d} ", pScan->iNP);
          strcat(pDefinition,pBueffel);
          if(i == 0)
          {
            strcat(pDefinition," -attr {axis,1}");
          }
          /* get data and write */
          GetScanVar(pScan,i,fAxis,pScan->iNP);
          NXDputdef(hfil,hdict,pDefinition,fAxis);
          if(i == 0)
          {
             NXDget(hdict,"sdana",pBueffel,512);
             NXDdeflink(hfil,hdict,pBueffel,pDefinition);
          }
       }

       /* 
          Write counted data: Here there is a convention which has to be
          valid in amorscan as well:  GetCounts gets the main detector.
          GetMonitor 0 the second detector, GetMonitor 1 the first detector
          other spin, GetMonitor 2, the second detector the other spin,
          getMonitor 3, the first monitor, getMonitor 4 the second monitor
        */
       sprintf(pBueffel,"%d",pScan->iNP);
       NXDupdate(hdict,"scanlength",pBueffel);
       GetScanCounts(pScan,lData,pScan->iNP);
       for(i = 0; i < pScan->iNP; i++)
       {
          iData[i] = (int)lData[i];
       }
       NXDputalias(hfil,hdict, "spinupup",iData);
       NXDaliaslink(hfil,hdict,"sdana","spinupup");
       GetScanMonitor(pScan,0,lData,pScan->iNP);
       for(i = 0; i < pScan->iNP; i++)
       {
          iData[i] = (int)lData[i];
       }
       NXDputalias(hfil,hdict, "spinuplo",iData);
       NXDaliaslink(hfil,hdict,"sdana","spinuplo");

       /* monitors */
       GetScanMonitor(pScan,3,lData,pScan->iNP);
       for(i = 0; i < pScan->iNP; i++)
       {
          iData[i] = (int)lData[i];
       }
       NXDputalias(hfil,hdict, "smonitor1",iData);
       GetScanMonitor(pScan,4,lData,pScan->iNP);
       for(i = 0; i < pScan->iNP; i++)
       {
          iData[i] = (int)lData[i];
       }
       NXDputalias(hfil,hdict, "smonitor2",iData);

       /* to be added: polarizing mode, possibly coz, cox, com etc. */
              
       /* a few motors which may or may not be useful */
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"sdetx","cox");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"sdetom","com");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"sdetheight","coz");
       
       /* close and go */
       NXclose(&hfil);
       NXDclose(hdict,NULL);
       free(fAxis);
       free(lData);
       free(iData); 

       return 1;      
  }
/*-----------------------------------------------------------------------*/
  int WriteAmorTOF(char *file, SConnection *pCon, pHistMem pHM)
  {
       NXhandle hfil;
       NXdict hdict;
       int iRet, i, iLength;
       char pBueffel[512];
       float fVal, *fAxis = NULL, *fTime2 = NULL;
       CommandList *pCom = NULL;
       const float *fTime;
       HistInt *lData = NULL, lVal;
 

       /* open files */
       iRet = NXopen(file,NXACC_RDWR,&hfil);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open file %s for writing");
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }
       iRet = NXDinitfromfile(AMORDICT,&hdict);
       if(iRet != NX_OK)
       {
	   sprintf(pBueffel,"ERROR: cannot open dictionary file %s");
           SCWrite(pCon,pBueffel,eError);
           return 0;
       }

       /* write general detector data */
       SNXSPutVariable(pServ->pSics,pCon,hfil, hdict,"detdist",
                       "detectordist");

       /* a few motors which may or may not be useful */
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"detx","cox");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"detom","com");
       SNXSPutMotor(pServ->pSics,pCon,hfil,hdict,"detheight","coz");
       
       /* write counter related stuff */
       pCom = FindCommand(pServ->pSics,"counter");
       if(pCom)
       {
         if(pCom->pData)
	 {
            fVal = GetCountTime((pCounter)pCom->pData,pCon);
            NXDputalias(hfil,hdict,"cntime",&fVal);

            /* the assignment of monitors has to be checked once
               the Schlumpf is done
	    */
            lVal = GetMonitor((pCounter)pCom->pData,2, pCon);
            NXDputalias(hfil,hdict,"cnmon1",&lVal);
            lVal = GetMonitor((pCounter)pCom->pData,3, pCon);
            NXDputalias(hfil,hdict,"cnmon2",&lVal);
	 }       
       }
       else
       {
           SCWrite(pCon,"WARNING: failed to find counter!",eWarning);
       }

       /* write two axis */
       fAxis = (float *)malloc(DETSIZE*sizeof(float));
       if(!fAxis)
       {
         SCWrite(pCon,"ERROR: out of memory in WriteAmorTOF",eError);
         return 0;
       }
       for(i = 0; i < DETSIZE; i++)
       {
          fAxis[i] = (float)i;
       }
       NXDputalias(hfil,hdict,"detxx",fAxis);
       NXDputalias(hfil,hdict,"dety",fAxis);
       NXDaliaslink(hfil,hdict,"dana","detxx");
       NXDaliaslink(hfil,hdict,"dana","dety");
       free(fAxis);

       /* update detector size */
       sprintf(pBueffel,"%d",DETSIZE);
       NXDupdate(hdict,"detsize",pBueffel);


       /* deal with time binning */
       fTime = GetHistTimeBin(pHM,&iLength);
       fTime2 = (float *)malloc(iLength*sizeof(float));
       if(fTime2)
       {
           for(i = 0; i < iLength; i++)
           {
               fTime2[i] = fTime[i]/10.;
           }
           sprintf(pBueffel,"%d",iLength);
           NXDupdate(hdict,"timebin",pBueffel);
           NXDputalias(hfil,hdict,"dettime",fTime2);
           NXDaliaslink(hfil,hdict,"dana","dettime");
           free(fTime2);
           fTime2 = NULL;
       }
       else
       {
          SCWrite(pCon,"ERROR: out of memory while writing time binning",
                  eError);
       }

       /* finally get histogram */
       iLength = GetHistLength(pHM);
       lData = (HistInt *)malloc(iLength*sizeof(HistInt));
       if(!lData)
       {
           SCWrite(pCon,
              "ERROR: out of memory, failed to write histogram",eError);
           return 0;
       }
       memset(lData,0,iLength*sizeof(HistInt));
       GetHistogram(pHM,pCon, 0,0,iLength,lData,iLength*sizeof(HistInt));
       NXDputalias(hfil,hdict,"spinup",lData);
       NXDaliaslink(hfil,hdict,"dana","spinup");

       /* to do: add polarizing code */

       free(lData);

       NXclose(&hfil);
       NXDclose(hdict,NULL);

       return 1;      
  }
/*-----------------------------------------------------------------------*/
  static pHistMem pMeme = NULL;

  int AmorStore(SConnection *pCon, SicsInterp *pSics, void *pData, 
                int argc, char *argv[])
  {
     char pBueffel[512], *pFile = NULL;
     int iRet;
     
     pFile = SNXMakeFileName(pSics,pCon);
     sprintf(pBueffel,"Writing file %s .....",pFile);
     SCWrite(pCon,pBueffel,eWarning);
     iRet =  WriteAmorHeader(pFile,pCon);
     if(iRet)
     {
        iRet = WriteAmorTOF(pFile,pCon,pMeme);
     }
     free(pFile);
     SCSendOK(pCon);
     return iRet;
  }      
/*---------------------------------------------------------------------*/
  int AmorStoreMake(SConnection *pCon, SicsInterp *pSics, void *pData, 
                int argc, char *argv[])
  {
     CommandList *pCom = NULL;
     char pBueffel[512];
     int iRet;

     if(argc < 2)
     {
        SCWrite(pCon,
         "ERROR: insufficient number of arguments to AmorStoreMake",
          eError);
        return 0;
     }

     /* we need one parameter which is the name of the histogram memory*/
     pCom = FindCommand(pServ->pSics,argv[1]);
     if(!pCom)
     {
       sprintf(pBueffel,"ERROR: Histogram memory %s NOT found", argv[1]);
       SCWrite(pCon,pBueffel,eError);
       return 0;
     }
     if(!pCom->pData)
     {
       sprintf(pBueffel,"ERROR: Histogram memory %s NOT found", argv[1]);
       SCWrite(pCon,pBueffel,eError);
       return 0;
     }
     pMeme = (pHistMem)pCom->pData;

     /* install command */
     iRet = AddCommand(pSics,"storeamor",
                        AmorStore,NULL,NULL);
     if(!iRet)
     {
         sprintf(pBueffel,"ERROR: duplicate command amorstore NOT created");
         SCWrite(pCon,pBueffel,eError);
         return 0;
     }
     return 1;
  }      



