/*-------------------------------------------------------------------------
  A module implementing functionality for reading single time frames
  from PSD time-of-flight datasets. This can be done either from
  SINQHM histogram memories or from old data files visible from the
  SICS server. The result is sent to the demanding client in UUencoded
  format.
  
  copyright: see file COPYRIGHT
  
  Mark Koennecke, February-March 2003
---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include "fortify.h"
#include "sics.h"
#include "stringdict.h"
#include "counter.h"
#include "HistMem.h"
#include "HistMem.i"
#include "HistDriv.i"
#include "hardsup/sinqhm.h"
#include "sinqhmdriv.i"
#include "nxdict.h"
#include "frame.h"
/*======================================================================*/
static int readHMFrame(SConnection * pCon, pHistMem pHM, int nFrame)
{
  HistInt *buffer = NULL;
  int iDim[MAXDIM], rank, length, status, i, noTimeBins;
  pSINQHM pHist;
  SinqHMDriv *pTata;
  const float *timeBin;

  /*
     find dimensions and allocate data
   */
  GetHistDim(pHM, iDim, &rank);
  timeBin = GetHistTimeBin(pHM, &noTimeBins);

  if (rank < 2) {
    SCWrite(pCon, "ERROR: no PSD data present, cannot send frame", eError);
    return 0;
  }
  length = iDim[0] * iDim[1];
  buffer = (HistInt *) malloc((length + 2) * sizeof(HistInt));
  if (!buffer) {
    SCWrite(pCon, "ERROR: out of memory in readHMFrame", eError);
    return 0;
  }
  memset(buffer, 0, (length + 2) * sizeof(HistInt));

  /*
     first two values are dimensions
   */
  buffer[0] = htonl(iDim[0]);
  buffer[1] = htonl(iDim[1]);

  if (isSINQHMDriv(pHM->pDriv) && noTimeBins > 2) {
    /*
       read from HM. The 5 is PROJECT__FRAME in Sinqhm_def.h
       Again: be friendly: fix out of range frames
     */
    if (nFrame < 0) {
      nFrame = 0;
    }
    if (nFrame >= noTimeBins) {
      nFrame = noTimeBins - 1;
    }
    pTata = (SinqHMDriv *) pHM->pDriv->pPriv;
    pHist = (pSINQHM) pTata->pMaster;
    status = SINQHMProject(pHist, 0x0005, 0, nFrame,
                           0, iDim[1], buffer + 2,
                           length * sizeof(HistInt));
    if (status != 1) {
      SCWrite(pCon, "ERROR: SINQHM refused to deliver frame", eError);
      free(buffer);
      return 0;
    }
  } else {
    /*
       be friendly, just read the 2D data which is there
     */
    status =
        GetHistogram(pHM, pCon, 0, 0, length, buffer + 2,
                     length * sizeof(HistInt));
    if (!status) {
      free(buffer);
      return status;
    }
  }

  /*
     enforce network byte order
   */
  for (i = 0; i < length; i++) {
    buffer[i + 2] = htonl(buffer[i + 2]);
  }

  SCWriteUUencoded(pCon, "framedata", buffer,
                   (length + 2) * sizeof(HistInt));
  free(buffer);

  return 1;
}

/*=======================================================================*/
static int readFileFrame(SConnection * pCon,
                         char *file, char *dictFile,
                         char *alias, int nFrame)
{
  int status, iDim[NX_MAXRANK], rank = 0, type = 0;
  int iStart[3], iSize[3], length, i;
  int *buffer = NULL;
  NXhandle fileHandle;
  NXdict dictHandle;
  char error[512];

  status = NXopen(file, NXACC_READ, &fileHandle);
  if (status != NX_OK) {
    snprintf(error,511, "ERROR: failed to open %s", file);
    SCWrite(pCon, error, eError);
    return 0;
  }
  status = NXDinitfromfile(dictFile, &dictHandle);
  if (status != NX_OK) {
    snprintf(error,511, "ERROR: failed to open dictionary %s", dictFile);
    NXclose(&fileHandle);
    SCWrite(pCon, error, eError);
    return 0;
  }

  status = NXDopenalias(fileHandle, dictHandle, alias);
  if (status != NX_OK) {
    snprintf(error,511, "ERROR: failed to open alias %s", alias);
    NXclose(&fileHandle);
    NXDclose(dictHandle, NULL);
    SCWrite(pCon, error, eError);
    return 0;
  }

  status = NXgetinfo(fileHandle, &rank, iDim, &type);
  if (type != NX_INT32 && type != NX_UINT32)
    type = -1;
  if (status != NX_OK || rank < 2 || type < 0) {
    sprintf(error, "ERROR: Dataset does not match!");
    NXclose(&fileHandle);
    NXDclose(dictHandle, NULL);
    SCWrite(pCon, error, eError);
    return 0;
  }

  /*
     allocate space
   */
  length = iDim[0] * iDim[1];
  buffer = (int *) malloc((length + 2) * sizeof(int));
  if (!buffer) {
    NXclose(&fileHandle);
    NXDclose(dictHandle, NULL);
    SCWrite(pCon, "ERROR: out of memory in readFrameFromFile", eError);
    return 0;
  }
  memset(buffer, 0, (length + 2) * sizeof(int));

  /*
     first two values: dimensions
   */
  buffer[0] = htonl(iDim[0]);
  buffer[1] = htonl(iDim[1]);

  if (rank == 2) {
    /*
       be friendly
     */
    status = NXgetdata(fileHandle, buffer + 2);
  } else {
    iStart[0] = iStart[1] = 0;
    if (nFrame > iDim[2] - 2) {
      nFrame = iDim[2] - 2;
    }
    iStart[2] = nFrame;
    iSize[0] = iDim[0];
    iSize[1] = iDim[1];
    iSize[2] = 1;
    status = NXgetslab(fileHandle, buffer + 2, iStart, iSize);
  }
  if (status != NX_OK) {
    NXclose(&fileHandle);
    NXDclose(dictHandle, NULL);
    free(buffer);
    SCWrite(pCon, "ERROR: failed to read data", eError);
    return 0;
  }

  /*
     enforce network byte order
   */
  for (i = 0; i < length; i++) {
    buffer[2 + i] = htonl(buffer[2 + i]);
  }

  SCWriteUUencoded(pCon, "framedata", buffer, (length + 2) * sizeof(int));
  NXclose(&fileHandle);
  NXDclose(dictHandle, NULL);
  free(buffer);
  return 1;
}

/*=======================================================================*/
int PSDFrameAction(SConnection * pCon, SicsInterp * pSics, void *pData,
                   int argc, char *argv[])
{
  pHistMem pHM;
  int nFrame;

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

  strtolower(argv[1]);
  if (strcmp(argv[1], "hm") == 0) {
    if (argc < 4) {
      SCWrite(pCon, "ERROR: Insufficient number of arguments to PSDFrame",
              eError);
      return 0;
    }
    pHM = (pHistMem) FindCommandData(pSics, argv[2], "HistMem");
    if (pHM == NULL) {
      SCWrite(pCon, "ERROR: Did not find histogram memory", eError);
      return 0;
    }
    nFrame = atoi(argv[3]);
    return readHMFrame(pCon, pHM, nFrame);
  } else if (strcmp(argv[1], "file") == 0) {
    if (argc < 6) {
      SCWrite(pCon,
              "ERROR: Insufficient number of arguments to PSDframe file",
              eError);
      return 0;
    }
    nFrame = atoi(argv[5]);
    return readFileFrame(pCon, argv[2], argv[3], argv[4], nFrame);
  } else {
    SCWrite(pCon, "ERROR: subcommand to PSDframe not recognised", eError);
    return 0;
  }
}

/*======================================================================*/
int MakeFrameFunc(SConnection * pCon, SicsInterp * pSics, void *pData,
                  int argc, char *argv[])
{
  return AddCommand(pSics, "PSDframe", PSDFrameAction, NULL, NULL);
}
