#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <math.h>

#include "amdmsFits.h"

#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))

static void amdmsInitFits(amdmsFITS *file);
static void amdmsFreeFits(amdmsFITS *file);

static char *gDoNotCopyKeywords[] =
{
    "SIMPLE",
    "BITPIX",
    "NAXIS",
    "NAXIS1",
    "NAXIS2",
    "NAXIS3",
    "NAXIS4",
    "EXTEND",
    "COMMENT",
    NULL
};

void amdmsReportFitsError(amdmsFITS *file, int status, int line, char *info)
{
  int    hstatus = 0;
  char   errtext[256] = "";
  int    hdunum = -1;
  char   value[82] = "???";
  
  fits_get_errstatus(status, errtext);
  if (file->fits != NULL) {
    fits_get_hdu_num(file->fits, &hdunum);
    fits_read_key(file->fits, TSTRING, "EXTNAME", &value, NULL, &hstatus);
  }
  amdmsError(__FILE__, line,
	    "cfitsio, fitsfile = %s:%s(%d), status = %d, errtext = %s::%s",
	    file->fileName, value, hdunum, status, errtext, info);
}

void amdmsInitFits(amdmsFITS *file)
{
  int                    iRow;
  int                    iCol;
  int                    iReg;
  int                    iTel;
  int                    iTrain;
  int                    i;
  amdmsIMAGING_DETECTOR  *det;
  amdmsIMAGING_DATA      *dat;

  /* initialize global FITS file members */
  file->fits = NULL;
  file->flags.content = amdmsUNKNOWN_CONTENT;
  file->flags.format = amdmsUNKNOWN_FORMAT;
  file->flags.type = amdmsUNKNOWN_TYPE;
  file->isNew = amdmsFALSE;
  file->currStateFile = amdmsUNDEFINED_STATE;
  file->currStateHDU = amdmsUNDEFINED_STATE;
  file->bitpix = 0;
  file->nAxis = 0;
  file->nTel = 0;
  file->nCols = 0;
  file->nRows = 0;
  for (iRow = 0; iRow < amdmsMAX_ROWS; iRow++) {
    for (iCol = 0; iCol < amdmsMAX_COLS; iCol++) {
      file->regions[iCol][iRow].x = 0;
      file->regions[iCol][iRow].y = 0;
      file->regions[iCol][iRow].width = 0;
      file->regions[iCol][iRow].height = 0;
      file->regions[iCol][iRow].offset = 0;
      file->regions[iCol][iRow].size = 0;
    }
  }
  file->nx = 0;
  file->ny = 0;
  file->nImages = 0;
  file->nReads = 1;
  file->nPixels = 0;
  file->exptime = 0.0;
  file->fileName = NULL;
  file->tableExt = NULL;
  file->rowIndex = NULL;
  file->hdrTable = NULL;
  file->hdrKeys = NULL;
  file->indexColType = TDOUBLE;
  file->indexColName = "INDEX";
  file->regionColType = TFLOAT;
  file->regionColName = "DATA";
  file->outNCols = 0;
  for (iCol = 0; iCol < amdmsMAX_BTBL_COLS; iCol++) {
    file->outColType[iCol] = NULL;
    file->outColForm[iCol] = NULL;
    file->outColUnit[iCol] = NULL;
  }
  /* IMAGING_DETECTOR binary table */
  det = &(file->detector);
  det->nDetectFlag = amdmsFALSE;
  det->nDetect = 0;
  det->nRegionFlag = amdmsFALSE;
  det->nRegion = 0;
  det->maxCoefFlag = amdmsFALSE;
  det->maxCoef = 0;
  det->numDimFlag = amdmsFALSE;
  det->numDim = 0;
  det->maxTelFlag = amdmsFALSE;
  det->maxTel = 0;
  det->regionFlag = amdmsFALSE;
  det->regionColNr = -1;
  det->detectorFlag = amdmsFALSE;
  det->detectorColNr = -1;
  det->portsFlag = amdmsFALSE;
  det->portsColNr = -1;
  det->correlationFlag = amdmsFALSE;
  det->correlationColNr = -1;
  det->regNameFlag = amdmsFALSE;
  det->regNameColNr = -1;
  det->cornerFlag = amdmsFALSE;
  det->cornerColNr = -1;
  det->gainFlag = amdmsFALSE;
  det->gainColNr = -1;
  det->nAxisFlag = amdmsFALSE;
  det->nAxisColNr = -1;
  det->CRValFlag = amdmsFALSE;
  det->CRValColNr = -1;
  det->CRPixFlag = amdmsFALSE;
  det->CRPixColNr = -1;
  det->cTypeFlag = amdmsFALSE;
  det->cTypeColNr = -1;
  det->CDFlag = amdmsFALSE;
  det->CDColNr = -1;
  det->DMPFlag = amdmsFALSE;
  det->DMPColNr = -1;
  det->DMCFlag = amdmsFALSE;
  det->DMCColNr = -1;
  for (iReg = 0; iReg < amdmsMAX_REGIONS; iReg++) {
    det->region[iReg] = 0;
    det->detector[iReg] = 0;
    for (iTel = 0; iTel < amdmsMAX_TELS; iTel++) {
      det->ports[iReg][iTel] = 0;
    }
    det->correlation[iReg] = 0;
    det->regName[iReg][0] = '\0';
    det->corner[iReg][0] = 0;
    det->corner[iReg][1] = 0;
    det->gain[iReg] = 0.0;
    det->nAxis[iReg][0] = 0;
    det->nAxis[iReg][1] = 0;
    det->CRVal[iReg][0] = 0.0;
    det->CRVal[iReg][1] = 0.0;
    det->CRPix[iReg][0] = 0.0;
    det->CRPix[iReg][1] = 0.0;
    det->cType[iReg][0] = '\0';
    det->CD[iReg][0] = 0.0;
    det->CD[iReg][1] = 0.0;
    det->CD[iReg][2] = 0.0;
    det->CD[iReg][3] = 0.0;
    for (i = 0; i < 256; i++) {
      det->DMP[iReg][i] = 0;
      det->DMC[iReg][i] = 0.0;
    }
  }
  /* IMAGING_DATA binary table */
  dat = &(file->data);
  dat->maxInsFlag = amdmsFALSE;
  dat->maxIns = 0;
  dat->maxStepFlag = amdmsFALSE;
  dat->maxStep = 0;
  dat->frameFlag = amdmsFALSE;
  dat->frameColNr = -1;
  dat->frame = 0;
  dat->timeFlag = amdmsFALSE;
  dat->timeColNr = -1;
  dat->time = 0.0;
  dat->exptimeFlag = amdmsFALSE;
  dat->exptimeColNr = -1;
  dat->exptime = 0.0;
  dat->optTrainFlag = amdmsFALSE;
  dat->optTrainColNr = -1;
  dat->insTrainFlag = amdmsFALSE;
  dat->insTrainColNr = -1;
  dat->referenceFlag = amdmsFALSE;
  dat->referenceColNr = -1;
  dat->reference = 0;
  dat->opdFlag = amdmsFALSE;
  dat->opdColNr = -1;
  dat->localOpdFlag = amdmsFALSE;
  dat->localOpdColNr = -1;
  dat->offsetFlag = amdmsFALSE;
  dat->offsetColNr = -1;
  dat->offset[0] = 0.0;
  dat->offset[1] = 0.0;
  dat->rotationFlag = amdmsFALSE;
  dat->rotationColNr = -1;
  dat->rotation = 0.0;
  dat->steppingPhaseFlag = amdmsFALSE;
  dat->steppingPhaseColNr = -1;
  dat->steppingPhase = 0;
  dat->targetFlag = amdmsFALSE;
  dat->tarTypFlag = amdmsFALSE;
  dat->dataFlag = amdmsFALSE;
  for (iReg = 0; iReg < amdmsMAX_REGIONS; iReg++) {
    dat->targetColNr[iReg] = -1;
    dat->target[iReg] = 0;
    dat->tarTypColNr[iReg] = -1;
    dat->tarTyp[iReg][0] = '\0';
    dat->dataColNr[iReg] = -1;
    dat->data[iReg] = NULL;
  }
  for (iTel = 0; iTel < amdmsMAX_TELS; iTel++) {
    dat->optTrain[iTel] = 0;
    dat->opd[iTel] = 0.0;
    dat->localOpd[iTel] = 0.0;
  }
  for (iTrain = 0; iTrain < amdmsMAX_TRAINS; iTrain++) {
    dat->insTrain[iTrain] = 0;
  }
}

void amdmsFreeFits(amdmsFITS *file)
{
  int   iCol;
  int   iReg;

  if (file == NULL) {
    return;
  }
  /* free all allocated memory */
  if (file->rowIndex != NULL) {
    free(file->rowIndex);
    file->rowIndex = NULL;
  }
  for (iCol = 0; iCol < amdmsMAX_BTBL_COLS; iCol++) {
    if (file->outColType[iCol] != NULL) {
      free(file->outColType[iCol]);
      file->outColType[iCol] = NULL;
    }
    if (file->outColForm[iCol] != NULL) {
      free(file->outColForm[iCol]);
      file->outColForm[iCol] = NULL;
    }
    if (file->outColUnit[iCol] != NULL) {
      free(file->outColUnit[iCol]);
      file->outColUnit[iCol] = NULL;
    }
  }
  for (iReg = 0; iReg < amdmsMAX_REGIONS; iReg++) {
    if (file->data.data[iReg] != NULL) {
      free(file->data.data[iReg]);
      file->data.data[iReg] = NULL;
    }
  }
}

amdmsCOMPL amdmsCreateFitsFile(amdmsFITS **file, char *fileName)
{
  amdmsFITS *hfile = NULL;
  int       status = 0;

  amdmsDebug(__FILE__, __LINE__, "amdmsCreateFitsFile(.., %s)", fileName);
  if (*file == NULL) {
    hfile = (amdmsFITS*)calloc(1L, sizeof(amdmsFITS));
    if (hfile == NULL) {
      return amdmsFAILURE;
    }
    hfile->allocated = amdmsTRUE;
    *file = hfile;
  } else {
    hfile = *file;
    hfile->allocated = amdmsFALSE;
  }
  amdmsInitFits(hfile);
  if (fileName == NULL) {
    amdmsError(__FILE__, __LINE__, "fileName == NULL!");
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "fileName = %s", fileName);
  /*
  switch (hfile->currStateFile) {
  case amdmsFILE_OPENED_STATE:
   amdmsError(__FILE__, __LINE__, "file %s is already open!", this->fileName);
    return amdmsFAILURE;
  case amdmsFILE_CREATED_STATE:
    amdmsError(__FILE__, __LINE__, "file %s is already created!", this->fileName);
    return amdmsFAILURE;
  default:
  }
  */
  hfile->fileName = fileName;
  remove(fileName);
  if (fits_create_file(&(hfile->fits), fileName, &status) != 0) {
    amdmsReportFitsError(hfile, status,  __LINE__, fileName);
    return amdmsFAILURE;
  }
  hfile->isNew = amdmsTRUE;
  hfile->currStateFile = amdmsFILE_CREATED_STATE;
  hfile->currStateHDU = amdmsUNDEFINED_STATE;
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsOpenFitsFile(amdmsFITS **file, char *fileName)
{
  amdmsFITS *hfile = NULL;
  int       status = 0;
  int       hdutype;

  amdmsDebug(__FILE__, __LINE__, "amdmsOpenFitsFile(.., %s)", fileName);
  if (*file == NULL) {
    hfile = (amdmsFITS*)calloc((size_t)1, sizeof(amdmsFITS));
    if (hfile == NULL) {
      return amdmsFAILURE;
    }
    hfile->allocated = amdmsTRUE;
    *file = hfile;
  } else {
    hfile = *file;
    hfile->allocated = amdmsFALSE;
  }
  amdmsInitFits(hfile);
  if (fileName == NULL) {
    amdmsError(__FILE__, __LINE__, "fileName == NULL!");
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "fileName = %s", fileName);
  /*
  switch (currStateFile) {
  case amdmsFILE_OPENED_STATE:
    amdmsError(__FILE__, __LINE__, "file %s is already open!", this->fileName);
    return amdmsFAILURE;
  case amdmsFILE_CREATED_STATE:
    amdmsError(__FILE__, __LINE__, "file %s is already created!", this->fileName);
    return amdmsFAILURE;
  default:
  }
  */
  hfile->fileName = fileName;
  if (fits_open_file(&(hfile->fits), fileName, READONLY, &status) != 0) {
    amdmsReportFitsError(hfile, status, __LINE__, fileName);
    return amdmsFAILURE;
  }
  hfile->isNew = amdmsFALSE;
  if(fits_movabs_hdu(hfile->fits, 1, &hdutype, &status) != 0) {
    amdmsReportFitsError(hfile, status,__LINE__, NULL);
    return amdmsFAILURE;
  }
  hfile->currStateFile = amdmsFILE_OPENED_STATE;
  hfile->currStateHDU = amdmsUNDEFINED_STATE;
  /* read some information from the primary HDU */
  hfile->exptime = 0.0;
  amdmsReadKeywordFloat(hfile, "EXPTIME", &(hfile->exptime), NULL);
  amdmsReadImagingDetectorFromHeader(hfile);
  return amdmsReadImagingDetectorFromTable(*file);
}

amdmsCOMPL amdmsCloseFitsFile(amdmsFITS **file)
{
  int status = 0;

  amdmsDebug(__FILE__, __LINE__, "amdmsCloseFitsFile(%s)", (*file)->fileName);
  switch ((*file)->currStateFile) {
  case amdmsFILE_OPENED_STATE:
  case amdmsFILE_CREATED_STATE:
    if (fits_close_file((*file)->fits, &status) != 0) {
      amdmsReportFitsError(*file, status, __LINE__, (*file)->fileName);
      return amdmsFAILURE;
    }
    (*file)->isNew = 0;
    (*file)->fits = NULL;
    (*file)->currStateFile = amdmsUNDEFINED_STATE;
    (*file)->currStateHDU = amdmsUNDEFINED_STATE;
    amdmsFreeFits(*file);
    if ((*file)->allocated) {
      free(*file);
      *file = NULL;
    }
    return amdmsSUCCESS;
  default:
    amdmsError(__FILE__, __LINE__, "no file open or created!");
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsDeleteFitsFile(amdmsFITS **file)
{
  int status = 0;

  /* amdmsVB(amdmsVERBOSE_DEBUG_CALL, ("amdmsFITS::DeleteFile()\n")); */
  switch ((*file)->currStateFile) {
  case amdmsFILE_OPENED_STATE:
  case amdmsFILE_CREATED_STATE:
    if (fits_delete_file((*file)->fits, &status) != 0) {
      amdmsReportFitsError(*file, status, __LINE__, (*file)->fileName);
      return amdmsFAILURE;
    }
    (*file)->isNew = 0;
    (*file)->fits = NULL;
    (*file)->currStateFile = amdmsUNDEFINED_STATE;
    (*file)->currStateHDU = amdmsUNDEFINED_STATE;
    amdmsFreeFits(*file);
    if ((*file)->allocated) {
      free(*file);
      *file = NULL;
    }
    return amdmsSUCCESS;
  default:
    amdmsError(__FILE__, __LINE__, "no file open or created!");
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsMoveToExtension(amdmsFITS *file, char* extName, int hduType, amdmsBOOL force)
{
  int status = 0;
  int nHdus;
  int iHdu;
  int currHduType;
  char value[82] = "";
  
  if (file == NULL) {
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "amdmsMoveToExtension(.., %s, %d, %d)", extName, hduType, force);
  if ((file->currStateFile != amdmsFILE_OPENED_STATE) && (file->currStateFile != amdmsFILE_CREATED_STATE)) {
    amdmsError(__FILE__, __LINE__, "amdmsMoveToExtension(%s, %d), no open file!", extName, hduType);
    return amdmsFAILURE;
  }
  if (extName == NULL) {
    if(fits_movabs_hdu(file->fits, 1, &hduType, &status) != 0) {
      amdmsReportFitsError(file, status, __LINE__, NULL);
      return amdmsFAILURE;
    }
    return amdmsSUCCESS;
  }
  fits_get_num_hdus(file->fits, &nHdus, &status);
  /* ???
  if (nHdus < 1) {
    return amdmsFAILURE;
  }
  */
  for (iHdu = 1; iHdu <= nHdus; iHdu++) {
    status = 0;
    if(fits_movabs_hdu(file->fits, iHdu, &currHduType, &status) != 0) {
      amdmsReportFitsError(file, status, __LINE__, NULL);
      return amdmsFAILURE;
    }
    if (hduType != currHduType) {
      continue;
    }
    if (fits_read_key(file->fits, TSTRING, "EXTNAME", &value, NULL, &status) == 0) {
      amdmsDebug(__FILE__, __LINE__, "  HDU %d is extension %s", iHdu, value);
      if (strcmp(value, extName) == 0) {
	return amdmsSUCCESS;
      }
    } else if ((status != KEY_NO_EXIST) && (status != 0)) {
      amdmsReportFitsError(file, status, __LINE__, NULL);
      return amdmsFAILURE;
    }
  }
  if (force) {
    amdmsReportFitsError(file, status, __LINE__, "Extension does not exists.");
    return amdmsFAILURE;
  } else {
    /* go at least to the primary HDU */
    status = 0;
    if(fits_movabs_hdu(file->fits, 1, &hduType, &status) != 0) {
      amdmsReportFitsError(file, status, __LINE__, NULL);
      return amdmsFAILURE;
    }
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsMoveToHDU(amdmsFITS *file, int hduNum)
{
  int status = 0;
  int currHduType;

  if (file == NULL) {
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "amdmsMoveToHDU(%s, %d) called", file->fileName, hduNum);
  if ((file->currStateFile != amdmsFILE_OPENED_STATE) && (file->currStateFile != amdmsFILE_CREATED_STATE)) {
    amdmsError(__FILE__, __LINE__,
	      "amdmsMoveToHDU(%s, %d), no open file!\n", file->fileName, hduNum);
    return amdmsFAILURE;
  }
  if(fits_movabs_hdu(file->fits, hduNum, &currHduType, &status) != 0) {
    amdmsReportFitsError(file, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsMoveToLastHDU(amdmsFITS *file)
{
  int status = 0;
  int nHdus;
  int currHduType;
  
  if (file == NULL) {
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "amdmsMoveToLastHDU()");
  if ((file->currStateFile != amdmsFILE_OPENED_STATE) && (file->currStateFile != amdmsFILE_CREATED_STATE)) {
    amdmsError(__FILE__, __LINE__, "no open file!");
    return amdmsFAILURE;
  }
  if (fits_movabs_hdu(file->fits, 1, &currHduType, &status) != 0) {
    amdmsReportFitsError(file, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  if (fits_get_num_hdus(file->fits, &nHdus, &status) != 0) {
    amdmsReportFitsError(file, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "fits_get_num_hdus -> %d", nHdus);
  if (nHdus == 0) {
    nHdus = 1;
  }
  if (fits_movabs_hdu(file->fits, nHdus, &currHduType, &status) != 0) {
    amdmsReportFitsError(file, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCopyHeader(amdmsFITS *dst, amdmsFITS *src)
{
  int status = 0;
  int hduType;
  int oldSrcHdu = 1;
  int oldDstHdu = 1;
  int srcKeyCount;
  int iKey;
  int i;
  int found;
  char keyName[256];
  char keyValue[256];
  char card[256];

  if ((dst == NULL) || (dst->currStateFile != amdmsFILE_CREATED_STATE)) {
    amdmsError(__FILE__, __LINE__, "amdmsCopyHeader(.., ..): no destination FITS file!");
    return amdmsFAILURE;
  }
  if ((src == NULL) || (src->currStateFile != amdmsFILE_OPENED_STATE)) {
    amdmsWarning(__FILE__, __LINE__, "amdmsCopyHeader(.., ..): no source FITS file!");
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "amdmsCopyHeader(%s, %s)", dst->fileName, src->fileName);
  if (dst->currStateHDU == amdmsUNDEFINED_STATE) {
    /* cannot Copy header without an image */
    amdmsDebug(__FILE__, __LINE__, "amdmsCopyHeader(.., ..), waiting for image");
    dst->hdrTable = src;
    return amdmsSUCCESS;
  }
  fits_get_hdu_num(dst->fits, &oldDstHdu);
  fits_get_hdu_num(src->fits, &oldSrcHdu);
  if (fits_movabs_hdu(dst->fits, 1, &hduType, &status) != 0) {
    amdmsReportFitsError(dst, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  if (fits_movabs_hdu(src->fits, 1, &hduType, &status) != 0) {
    amdmsReportFitsError(src, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  if (fits_get_hdrspace(src->fits, &srcKeyCount, NULL, &status) != 0) {
    amdmsReportFitsError(src, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  for (iKey = 1; iKey <= srcKeyCount; iKey++) {
    if (fits_read_keyn(src->fits, iKey, keyName, keyValue, NULL, &status) != 0) {
      amdmsReportFitsError(src, status, __LINE__, NULL);
      return amdmsFAILURE;
    }
    found = 0;
    for (i = 0; gDoNotCopyKeywords[i] != NULL; i++) {
      if (strcmp(gDoNotCopyKeywords[i], keyName) == 0) {
	found = 1;
	break;
      }
    }
    if (!found) {
      if (fits_read_record(src->fits, iKey, card, &status) != 0) {
	amdmsReportFitsError(src, status, __LINE__, NULL);
	return amdmsFAILURE;
      }
      if (fits_update_card(dst->fits, keyName, card, &status) != 0) {
	amdmsReportFitsError(dst, status, __LINE__, NULL);
	return amdmsFAILURE;
      }
    }
  }
/* hier werden auch keywords kopiert, die erhalten bleiben sollten!
  if (fits_copy_header(src->fits, dst->fits, &status) != 0) {
    amdmsReportFitsError(dst, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
*/
  if (fits_movabs_hdu(dst->fits, oldDstHdu, &hduType, &status) != 0) {
    amdmsReportFitsError(dst, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  if (fits_movabs_hdu(src->fits, oldSrcHdu, &hduType, &status) != 0) {
    amdmsReportFitsError(src, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  dst->hdrTable = NULL;
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCopyExtension(amdmsFITS *dst, amdmsFITS *src, char *extName, amdmsBOOL force)
{
  int status = 0;

  if (dst->currStateFile != amdmsFILE_CREATED_STATE) {
    amdmsError(__FILE__, __LINE__, "no destination file!");
    return amdmsFAILURE;
  }
  if ((src == NULL) || (src->currStateFile != amdmsFILE_OPENED_STATE)) {
    amdmsError(__FILE__, __LINE__, "no source file!");
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "amdmsCopyExtension(%s, %s, %s, %s, %d)",
	    dst->fileName, src->fileName, extName, force);
  if (amdmsMoveToExtension(src, extName, BINARY_TBL, force) != amdmsSUCCESS) {
    if (force) {
      amdmsError(__FILE__, __LINE__, "extension %s not found!", extName);
    }
    return amdmsFAILURE;
  }
  amdmsDebug(__FILE__, __LINE__, "copy extension!");
  if (fits_copy_hdu(src->fits, dst->fits, 0, &status) != 0) {
    amdmsReportFitsError(dst, status, __LINE__, NULL);
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

void amdmsSetRegion(amdmsFITS *file, int col, int row, int x, int y, int width, int height)
{
  amdmsDebug(__FILE__, __LINE__,
	    "amdmsSetRegion(%s, %d, %d, %d, %d, %d, %d)",
	    file->fileName, col, row, x, y, width, height);
  amdmsSetRow(file, row, y, height);
  amdmsSetCol(file, col, x, width);
}

void amdmsSetCol(amdmsFITS *file, int col, int x, int width)
{
  int iRow;

  if (col >= file->nCols) {
    file->nCols = col + 1;
  }
  for (iRow = 0; iRow < amdmsMAX_ROWS; iRow++) {
    file->regions[col][iRow].x = x;
    file->regions[col][iRow].width = width;
  }
  amdmsRecalcRegions(file);
}

void amdmsSetRow(amdmsFITS *file, int row, int y, int height)
{
  int iCol;

  if (row >= file->nRows) {
    file->nRows = row + 1;
  }
  for (iCol = 0; iCol < amdmsMAX_COLS; iCol++) {
    file->regions[iCol][row].y = y;
    file->regions[iCol][row].height = height;
  }
  amdmsRecalcRegions(file);
}

void amdmsSetRegions(amdmsFITS *dst, amdmsFITS *src)
{
  int  iRow;
  int  iCol;

  dst->nRows = 0;
  dst->nCols = 0;
  for (iRow = 0; iRow < src->nRows; iRow++) {
    for (iCol = 0; iCol < src->nCols; iCol++) {
      amdmsSetRegion(dst, iCol, iRow,
		    src->regions[iCol][iRow].x,
		    src->regions[iCol][iRow].y,
		    src->regions[iCol][iRow].width,
		    src->regions[iCol][iRow].height);
    }
  }
}

void amdmsRecalcRegions(amdmsFITS *file)
{
  int iRow;
  int iCol;
  int offset;

  file->nx = 0;
  for (iCol = 0; iCol < file->nCols; iCol++) {
    file->nx += file->regions[iCol][0].width;
  }
  file->ny = 0;
  for (iRow = 0; iRow < file->nRows; iRow++) {
    file->ny += file->regions[0][iRow].height;
  }
  file->nPixels = file->nx*file->ny;
  offset = 0;
  for (iRow = 0; iRow < file->nRows; iRow++) {
    for (iCol = 0; iCol < file->nCols; iCol++) {
      file->regions[iCol][iRow].size = file->regions[iCol][iRow].width*file->regions[iCol][iRow].height;
      file->regions[iCol][iRow].offset = offset;
      offset += file->regions[iCol][iRow].size;
    }
  }
}

amdmsCOMPL amdmsAdjustDataFilterSetup(amdmsDATA_FILTER_SETUP *setup, amdmsFITS *file)
{
  if ((setup == NULL) || (file == NULL)) {
    return amdmsFAILURE;
  }
  if ((file->currStateFile != amdmsFILE_OPENED_STATE)
      ||
      ((file->currStateHDU != amdmsCUBE_OPENED_STATE)
       &&
       (file->currStateHDU != amdmsTABLE_OPENED_STATE))) {
    return amdmsFAILURE;
  }
  /* update images of interest */
  if (setup->ioiFlag) {
    setup->ioiFrom = MAX(setup->ioiFrom, 0);
    setup->ioiFrom = MIN(setup->ioiFrom, file->nImages - 1);
    setup->ioiTo = MAX(setup->ioiTo, setup->ioiFrom);
    setup->ioiTo = MIN(setup->ioiTo, file->nImages - 1);
  } else {
    setup->ioiFrom = 0;
    setup->ioiTo = file->nImages - 1;
  }
  /* update area of interest */
  if (setup->aoiFlag) {
    setup->aoiWidth = MIN(setup->aoiWidth, file->nx);
    setup->aoiHeight = MIN(setup->aoiHeight, file->ny);
    setup->aoiX = MAX(setup->aoiX, 0);
    setup->aoiX = MIN(setup->aoiX, file->nx - setup->aoiWidth);
    setup->aoiY = MAX(setup->aoiY, 0);
    setup->aoiY = MIN(setup->aoiY, file->ny - setup->aoiHeight);
  } else {
    setup->aoiX = 0;
    setup->aoiY = 0;
    setup->aoiWidth = file->nx;
    setup->aoiHeight = file->ny;
  }
  /* update pixel of interest */
  if (setup->poiFlag) {
    setup->poiX = MAX(setup->poiX, 0);
    setup->poiX = MIN(setup->poiX, file->nx - 1);
    setup->poiY = MAX(setup->poiY, 0);
    setup->poiY = MIN(setup->poiY, file->ny - 1);
  }
  return amdmsSUCCESS;
}

