/*------------------------------------------------------------------------
  This is a little helper module for SICS which implements some
  status display support functions.

  COPYRIGHT: see file COPYRIGHT
 
  Extracted from nextrics 

  Mark Koennecke, August 2004
--------------------------------------------------------------------------*/
#include "tricssupport.h"
#include "napi.h"
#include "HistMem.h"


#define NEWFRAME 1166
/*------------------------------------------------------------------------
  a data structure plus support functions
  ------------------------------------------------------------------------*/
typedef struct {
  pObjectDescriptor pDes;
  pICallBack pCall;
} TricsSupport, *pTricsSupport;
/*----------------------------------------------------------------------*/
static pTricsSupport CreateTricsSupport()
{
  pTricsSupport pNew = NULL;

  pNew = (pTricsSupport) malloc(sizeof(TricsSupport));
  if (pNew == NULL) {
    return NULL;
  }
  memset(pNew, 0, sizeof(TricsSupport));

  pNew->pDes = CreateDescriptor("TricsSupport");
  pNew->pCall = CreateCallBackInterface();
  if (pNew->pDes == NULL || pNew->pCall == NULL) {
    free(pNew);
    return NULL;
  }
  return pNew;
}

/*--------------------------------------------------------------------*/
static void KillTricsSupport(void *pData)
{
  pTricsSupport pNew = (pTricsSupport) pData;

  if (pNew != NULL) {
    if (pNew->pDes != NULL) {
      DeleteDescriptor(pNew->pDes);
      pNew->pDes = NULL;
    }
    if (pNew->pCall != NULL) {
      DeleteCallBackInterface(pNew->pCall);
      pNew->pCall = NULL;
    }
  }
}

/*=====================================================================*/
static int FrameSupInterest(int iEvent, void *pEvent, void *pUser)
{
  SConnection *pCon = NULL;
  int *iFrame;
  char pBueffel[512];

  pCon = (SConnection *) pUser;
  /* check kill condition */
  if (pCon == NULL || !SCisConnected(pCon)) {
    return -1;
  }

  if (iEvent != NEWFRAME) {
    return 0;
  }

  iFrame = (int *) pEvent;
  assert(pCon);
  sprintf(pBueffel, "framenumber = %d", *iFrame);
  SCWrite(pCon, pBueffel, eWarning);
  return 1;
}

/*======================================================================
  NexusGetFrame opens a TRICS NeXus file and retrieves a frame for the
  specified detector from it. This is a special feature for the 
  status display.
  ---------------------------------------------------------------------------*/
static int NexusGetFrameSup(SConnection * pCon, char *filename,
                            int iDetector, int iFrame)
{
  char pBueffel[132];
  int iRet, type, rank, iDim[2];
  NXhandle hfil;
  HistInt *lData = NULL;

  /* do a few range checks */
  if (iDetector < 1 || iDetector > 3) {
    sprintf(pBueffel, "ERROR: unknown detector %d requested", iDetector);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* open file */
  iRet = NXopen(filename, NXACC_READ, &hfil);
  if (iRet != NX_OK) {
    sprintf(pBueffel, "ERROR: cannot open %s", filename);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  snprintf(pBueffel, 131, "/frame%4.4d/detector%1.1d/counts", iFrame,
           iDetector);
  iRet = NXopenpath(hfil, pBueffel);
  if (iRet != NX_OK) {
    sprintf(pBueffel, "ERROR: frame %d, detector %d not found in %s",
            iFrame, iDetector, filename);
    SCWrite(pCon, pBueffel, eError);
    NXclose(&hfil);
    return 0;
  }
  NXgetinfo(hfil, &rank, iDim, &type);

  lData = (HistInt *) malloc(iDim[0] * iDim[1] * sizeof(HistInt));
  if (!lData) {
    SCWrite(pCon, "ERROR: out of memory in NexusGetFrame", eError);
    NXclose(&hfil);
    return 0;
  }
  memset(lData, 0, iDim[0] * iDim[1] * sizeof(HistInt));
  iRet = NXgetdata(hfil, lData);
  if (iRet != NX_OK) {
    SCWrite(pCon, "ERROR: failed to read data", eError);
    NXclose(&hfil);
    return 0;
  }

  /* send it */
  sprintf(pBueffel, "detector%1.1d", iDetector);
  SCWriteZipped(pCon, pBueffel, lData,
                iDim[0] * iDim[1] * sizeof(HistInt));

  /* clean up */
  NXclose(&hfil);
  free(lData);
  return 1;
}

/*----------------------------------------------------------------------*/
int MakeTricsSupport(SConnection * pCon, SicsInterp * pSics, void *pData,
                     int argc, char *argv[])
{
  pTricsSupport pNew = NULL;

  pNew = CreateTricsSupport();
  if (!pNew) {
    SCWrite(pCon, "ERROR: out of memory creating TricsSupport", eError);
    return 0;
  }
  if (AddCommand
      (pSics, "tricssupport", TricsSupportAction, KillTricsSupport,
       pNew) != 1) {
    SCWrite(pCon, "ERROR: duplicate command tricssupport not created",
            eError);
    return 0;
  }
  SCSendOK(pCon);
  return 1;
}

/*-----------------------------------------------------------------------*/
int TricsSupportAction(SConnection * pCon, SicsInterp * pSics, void *pData,
                       int argc, char *argv[])
{
  int iDet, iFrame, iRet;
  long lID;
  char pBueffel[131];
  pTricsSupport self = (pTricsSupport) pData;

  assert(self);

  /* check for subcommands */
  if (argc < 2) {
    snprintf(pBueffel, 130, "ERROR: no subcommand found for %s", argv[0]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  strtolower(argv[1]);
  if (strcmp(argv[1], "oldframe") == 0) {
    if (argc < 5) {
      SCWrite(pCon, "ERROR: insufficient number of arguments to oldframe",
              eError);
      return 0;
    }
    iDet = atoi(argv[3]);
    iFrame = atoi(argv[4]);
    iRet = NexusGetFrameSup(pCon, argv[2], iDet, iFrame);
    return 1;
  } else if (strcmp(argv[1], "interest") == 0) {
    lID = RegisterCallback(self->pCall,
                           NEWFRAME, FrameSupInterest,
                           SCCopyConnection(pCon), SCDeleteConnection);
    SCSendOK(pCon);
    return 1;
  } else if (strcmp(argv[1], "newframe") == 0) {
    if (argc < 3) {
      SCWrite(pCon,
              "ERROR: insufficient number of arguments to tricsupport newframe",
              eError);
      return 0;
    }
    iFrame = atoi(argv[2]);
    InvokeCallBack(self->pCall, NEWFRAME, &iFrame);
    SCSendOK(pCon);
    return 1;
  } else {
    SCWrite(pCon, "ERROR: subcommand to tricssuport not understood",
            eError);
    return 0;
  }
}
