#include <math.h>
#include <string.h>

#include "amdms.h"
#include "amdmsFit.h"
#include "amdmsFits.h"

#define amdmsCU_NROWS     20
#define amdmsCU_ROW       100

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

static amdmsCOMPL amdmsReadMap(amdmsCALIBRATION_SETUP *setup,
			       char *fileName,
			       amdmsFITS_FLAGS flags,
			       int mapRow,
			       amdmsDATA *map,
			       amdmsPIXEL defValue);

static amdmsCOMPL amdmsReadMultiMap(amdmsCALIBRATION_SETUP *setup,
				    char *fileName,
				    amdmsFITS_FLAGS flags,
				    int mapFirstRow, int mapNRows,
				    amdmsDATA **map,
				    amdmsPIXEL *defValues);

static amdmsCOMPL amdmsCompensatePixelBias(amdmsCALIBRATION_SETUP *setup,
					   amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateEBiasByMap(amdmsCALIBRATION_SETUP *setup,
					    amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateEBiasBySimpleExp1D(amdmsCALIBRATION_SETUP *setup,
						    amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateEBias(amdmsCALIBRATION_SETUP *setup,
				       amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateGlobalBias(amdmsCALIBRATION_SETUP *setup,
					    amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateFNoise(amdmsCALIBRATION_SETUP *setup,
					amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateNonlinearity(amdmsCALIBRATION_SETUP *setup,
					      amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateFlatfield(amdmsCALIBRATION_SETUP *setup,
					   amdmsDATA *calData);
static amdmsCOMPL amdmsCompensateBadPixel(amdmsCALIBRATION_SETUP *setup,
					  amdmsDATA *calData);

amdmsCOMPL amdmsInitCalibrationSetup(amdmsCALIBRATION_SETUP *setup)
{
  amdmsFITS_FLAGS  flags = {amdmsIMAGING_DATA_CONTENT, amdmsDEFAULT_MAP_FORMAT, amdmsFLOAT_TYPE};

  if (setup == NULL) {
    return amdmsFAILURE;
  }
  setup->detNX = 512;
  setup->detNY = 512;
  setup->corrFlag = amdmsNO_CORRECTION;
  amdmsInitFileList(&(setup->mapFiles), flags);
  setup->saWinX = 0;
  setup->saWinWidth = 16;
  setup->saWinY = 0;
  setup->saWinHeight = 0;
  setup->saGaussHeight = 16;
  setup->saGaussSigma = 8.0;
  setup->bpiRadius = 4;
  setup->bpiSigma = 2;
  setup->bpiWeights = NULL;
  amdmsInitData(&(setup->bpmData));
  setup->nGoodPixels = 0;
  amdmsInitData(&(setup->ebmData));
  amdmsInitData(&(setup->pbmData));
  amdmsInitData(&(setup->ffmData));
  amdmsInitData(&(setup->nlmDataLimit));
  amdmsInitData(&(setup->nlmDataA0));
  amdmsInitData(&(setup->nlmDataA1));
  amdmsInitData(&(setup->pemDataFI));
  amdmsInitData(&(setup->pemDataLI));
  setup->globalBias = 0.0;
  setup->rowOffsets = NULL;
  setup->rowGood = NULL;
  setup->rowWeight = NULL;
  setup->rowSum = NULL;
  setup->ebAlgo = amdmsEBA_MAP;
  setup->ebX = NULL;
  setup->ebY = NULL;
  setup->ebYe = NULL;
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsFreeCalibrationSetup(amdmsCALIBRATION_SETUP *setup)
{
  if (setup == NULL) {
    return amdmsFAILURE;
  }
  setup->detNX = 512;
  setup->detNY = 512;
  setup->corrFlag = amdmsNO_CORRECTION;
  amdmsFreeFileList(&(setup->mapFiles));
  setup->saWinX = 0;
  setup->saWinWidth = 16;
  setup->saWinY = 0;
  setup->saWinHeight = 0;
  setup->saGaussHeight = 16;
  setup->saGaussSigma = 8.0;
  setup->bpiRadius = 4;
  setup->bpiSigma = 2;
  if (setup->bpiWeights != NULL) {
    free(setup->bpiWeights);
    setup->bpiWeights = NULL;
  }
  amdmsFreeData(&(setup->bpmData));
  setup->nGoodPixels = 0;
  amdmsFreeData(&(setup->ebmData));
  amdmsFreeData(&(setup->pbmData));
  amdmsFreeData(&(setup->ffmData));
  amdmsFreeData(&(setup->nlmDataLimit));
  amdmsFreeData(&(setup->nlmDataA0));
  amdmsFreeData(&(setup->nlmDataA1));
  amdmsFreeData(&(setup->pemDataFI));
  amdmsFreeData(&(setup->pemDataLI));
  setup->globalBias = 0.0;
  if (setup->rowOffsets != NULL) {
    free(setup->rowOffsets);
    setup->rowOffsets = NULL;
  }
  if (setup->rowGood != NULL) {
    free(setup->rowGood);
    setup->rowGood = NULL;
  }
  if (setup->rowWeight != NULL) {
    free(setup->rowWeight);
    setup->rowWeight = NULL;
  }
  if (setup->rowSum != NULL) {
    free(setup->rowSum);
    setup->rowSum = NULL;
  }
  if (setup->ebX != NULL) {
    free(setup->ebX);
    setup->ebX = NULL;
  }
  if (setup->ebY != NULL) {
    free(setup->ebY);
    setup->ebY = NULL;
  }
  if (setup->ebYe != NULL) {
    free(setup->ebYe);
    setup->ebYe = NULL;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsReadMap(amdmsCALIBRATION_SETUP *setup,
			char *fileName,
			amdmsFITS_FLAGS flags,
			int mapRow,
			amdmsDATA *map,
			amdmsPIXEL defValue)
{
  amdmsFITS        *file = NULL;
  int               iPixel;

  if ((fileName == NULL) || (fileName[0] == '\0')) {
    /* no filename given, create default map with default content */
    if (amdmsAllocateData(map, setup->detNX, setup->detNY) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
    for (iPixel = 0; iPixel < setup->detNX*setup->detNY; iPixel++) {
      map->data[iPixel] = defValue;
    }
    return amdmsSUCCESS;
  }
  if (amdmsOpenFitsFile(&file, fileName) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsOpenData(file, flags, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsReadData(file, map, mapRow, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsCloseFitsFile(&file);
}

amdmsCOMPL amdmsReadMultiMap(amdmsCALIBRATION_SETUP *setup,
			     char *fileName,
			     amdmsFITS_FLAGS flags,
			     int mapFirstRow,
			     int mapNRows,
			     amdmsDATA **map,
			     amdmsPIXEL *defValues)
{
  amdmsFITS        *file = NULL;
  int               iRow;
  int               iPixel;

  if ((fileName == NULL) || (fileName[0] == '\0')) {
    /* no filename given, create default map with default content */
    for (iRow = 0; iRow < mapNRows; iRow++) {
      if (amdmsAllocateData(map[iRow], setup->detNX, setup->detNY) != amdmsSUCCESS) {
	return amdmsFAILURE;
      }
      for (iPixel = 0; iPixel < setup->detNX*setup->detNY; iPixel++) {
	map[iRow]->data[iPixel] = defValues[iRow];
      }
    }
    return amdmsSUCCESS;
  }
  if (amdmsOpenFitsFile(&file, fileName) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsOpenData(file, flags, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  for (iRow = 0; iRow < mapNRows; iRow++) {
    if (amdmsReadData(file, map[iRow], mapFirstRow + iRow, 0) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  return amdmsCloseFitsFile(&file);
}

amdmsCOMPL amdmsReadAllMaps(amdmsCALIBRATION_SETUP *setup)
{
  int               iFile;
  char             *fileName = NULL;
  amdmsFITS_FLAGS   flags;
  amdmsDATA        *nlm[3];
  amdmsPIXEL        nlmDefaults[3];
  amdmsDATA        *pem[2];
  amdmsPIXEL        pemDefaults[2];
  int               iPixel;
  int               iX;
  int               iY;

  amdmsDebug(__FILE__, __LINE__, "amdmsReadAllMaps()");
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsELECTRONIC_BIAS_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMap(setup, fileName, flags, amdmsELECTRONIC_BIAS_ROW,
		  &(setup->ebmData), 0.0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsPIXEL_BIAS_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMap(setup, fileName, flags, amdmsPIXEL_BIAS_ROW,
		  &(setup->pbmData), 0.0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsBAD_PIXEL_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMap(setup, fileName, flags, amdmsBAD_PIXEL_ROW,
		  &(setup->bpmData), amdmsGOOD_PIXEL) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  setup->nGoodPixels = setup->bpmData.nx*setup->bpmData.ny;
  for (iPixel = 0; iPixel < setup->bpmData.nx*setup->bpmData.ny; iPixel++) {
    if (setup->bpmData.data[iPixel] == amdmsBAD_PIXEL) {
      setup->nGoodPixels--;
    }
  }
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsFLATFIELD_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMap(setup, fileName, flags, amdmsFLATFIELD_ROW,
		  &(setup->ffmData), 1.0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  nlm[0] = &(setup->nlmDataLimit);
  nlm[1] = &(setup->nlmDataA0);
  nlm[2] = &(setup->nlmDataA1);
  nlmDefaults[0] = 1.0e6;
  nlmDefaults[1] = 1.0;
  nlmDefaults[2] = 0.0;
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsNONLINEARITY_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMultiMap(setup, fileName, flags,
		       amdmsNONLINEARITY_LIMIT_ROW, amdmsNONLINEARITY_NROWS,
		       nlm, nlmDefaults) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  pem[0] = &(setup->pemDataFI);
  pem[1] = &(setup->pemDataLI);
  pemDefaults[0] = -1.0;
  pemDefaults[1] = -1.0;
  fileName = NULL;
  for (iFile = 0; iFile < setup->mapFiles.nNames; iFile++) {
    if (setup->mapFiles.flags[iFile].content == amdmsPARTICLE_EVENT_CONTENT) {
      fileName = setup->mapFiles.names[iFile];
      flags = setup->mapFiles.flags[iFile];
      break;
    }
  }
  if (amdmsReadMultiMap(setup, fileName, flags,
		       amdmsPARTICLE_EVENT_FIRST_IMG_ROW, amdmsPARTICLE_EVENT_NROWS,
		       pem, pemDefaults) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (setup->bpiWeights == NULL) {
    setup->bpiWeights = (amdmsPIXEL *)calloc((size_t)(setup->bpiRadius*setup->bpiRadius),
					  sizeof(amdmsPIXEL));
    if (setup->bpiWeights == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (bpiWeights)!");
      return amdmsFAILURE;
    }
    for (iX = 0; iX < setup->bpiRadius; iX++) {
      for (iY = 0; iY < setup->bpiRadius; iY++) {
	setup->bpiWeights[iY*setup->bpiRadius + iX] =
	  exp(-((amdmsPIXEL)(iX*iX + iY*iY))/2.0/setup->bpiSigma/setup->bpiSigma);
      }
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensatePixelBias(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  int          nPixels;
  int          iPixel;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsPIXEL_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  nPixels = calData->nx*calData->ny;
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    calData->data[iPixel] -= setup->pbmData.data[iPixel];
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateEBiasByMap(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  int          nPixels;
  int          iPixel;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsELECTRONIC_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  nPixels = calData->nx*calData->ny;
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    calData->data[iPixel] -= setup->ebmData.data[iPixel];
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateEBiasBySimpleExp1D(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  //int                     nPixels;
  int                     iPixel;
  int                     iX, iY;
  int                     nDP;
  amdmsFIT_NONLINEAR_ENV *ebFitEnv = NULL;  /* fit environment for electronic bias */

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsELECTRONIC_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  if (amdmsCreateGeneralExpFit(&ebFitEnv) != amdmsSUCCESS) {
    amdmsFatal(__FILE__, __LINE__, "could not create fit for electronic bias!");
    return amdmsFAILURE;
  }
  //nPixels = calData->nx*calData->ny;
  if (setup->rowOffsets == NULL) {
    setup->rowOffsets = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowOffsets == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowOffsets)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  if (setup->rowGood == NULL) {
    setup->rowGood = (int *)calloc((size_t)setup->detNY, sizeof(int));
    if (setup->rowGood == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowGood)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  if (setup->rowWeight == NULL) {
    setup->rowWeight = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowWeight == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowWeight)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  if (setup->ebX == NULL) {
    setup->ebX = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebX == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebX)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  if (setup->ebY == NULL) {
    setup->ebY = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebY == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebY)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  if (setup->ebYe == NULL) {
    setup->ebYe = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebYe == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebYe)!");
      amdmsDestroyNonLinearFit(&ebFitEnv);
      return amdmsFAILURE;
    }
  }
  nDP = 0;
  for (iY = 8; iY < calData->ny - 8 ; iY++) {
    setup->rowOffsets[iY] = 0.0;
    setup->rowGood[iY] = 0;
    for (iX = setup->saWinX; iX < (setup->saWinX + setup->saWinWidth); iX++) {
      iPixel = calData->nx*iY + iX;
      if (setup->bpmData.data[iPixel] == amdmsGOOD_PIXEL) {
	setup->rowOffsets[iY] += calData->data[iPixel];
	setup->rowGood[iY]++;
      }
    }
    if (setup->rowGood[iY] != 0) {
      setup->ebX[nDP] = (double)iY/(double)calData->ny;
      setup->ebY[nDP] = setup->rowOffsets[iY]/(amdmsPIXEL)setup->rowGood[iY];
      setup->ebYe[nDP] = 1.0;
      if (calData->index == 31) {
	amdmsInfo(__FILE__, __LINE__, "ROM %d = %.2f", iY, setup->ebY[nDP]);
      }
      nDP++;
    }
  }
  if (amdmsDoFit((amdmsFIT_ENV*)ebFitEnv, nDP, setup->ebX, setup->ebY, setup->ebYe) == amdmsSUCCESS) {
    /*
    amdmsInfo(__FILE__, __LINE__,
	     "EB%d(x) = %f + %f * exp(%f*x)",
	     (int)(calData->index),
	     ebFitEnv->env.a[0],
	     ebFitEnv->env.a[1],
	     ebFitEnv->env.a[2]);
    */
    for (iY = 0; iY < calData->ny ; iY++) {
      setup->rowOffsets[iY] = (amdmsPIXEL)amdmsEvalFit((amdmsFIT_ENV*)ebFitEnv, (double)iY/(double)calData->ny);
      if (calData->index == 31) {
	amdmsInfo(__FILE__, __LINE__, "ROS %d = %.4f", iY, setup->rowOffsets[iY]);
      }
      for (iX = 0; iX < calData->nx; iX++) {
	iPixel = calData->nx*iY + iX;
	calData->data[iPixel] -= setup->rowOffsets[iY];
      }
    }
  } else {
    amdmsWarning(__FILE__, __LINE__, "fit of the electronic bias does not work!");
  }
  return amdmsDestroyNonLinearFit(&ebFitEnv);
}

amdmsCOMPL amdmsCompensateEBiasBySimpleSmooth1D(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  //int          nPixels;
  int          iPixel;
  int          iX, iY;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsELECTRONIC_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  //nPixels = calData->nx*calData->ny;
  if (setup->rowOffsets == NULL) {
    setup->rowOffsets = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowOffsets == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowOffsets)!");
      return amdmsFAILURE;
    }
  }
  if (setup->rowGood == NULL) {
    setup->rowGood = (int *)calloc((size_t)setup->detNY, sizeof(int));
    if (setup->rowGood == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowGood)!");
      return amdmsFAILURE;
    }
  }
  if (setup->ebX == NULL) {
    setup->ebX = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebX == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebX)!");
      return amdmsFAILURE;
    }
  }
  if (setup->ebY == NULL) {
    setup->ebY = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebY == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebY)!");
      return amdmsFAILURE;
    }
  }
  if (setup->ebYe == NULL) {
    setup->ebYe = (double *)calloc((size_t)setup->detNY, sizeof(double));
    if (setup->ebYe == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ebYe)!");
      return amdmsFAILURE;
    }
  }
  for (iY = 0; iY < calData->ny; iY++) {
    setup->rowOffsets[iY] = 0.0;
    setup->rowGood[iY] = 0;
    for (iX = setup->saWinX; iX < (setup->saWinX + setup->saWinWidth); iX++) {
      iPixel = calData->nx*iY + iX;
      if (setup->bpmData.data[iPixel] == amdmsGOOD_PIXEL) {
	setup->rowOffsets[iY] += calData->data[iPixel];
	setup->rowGood[iY]++;
      }
    }
    if (setup->rowGood[iY] != 0) {
      setup->ebX[iY] = 1.0;
      setup->ebY[iY] = setup->rowOffsets[iY]/(amdmsPIXEL)setup->rowGood[iY];
    } else {
      setup->ebX[iY] = 0.0;
      setup->ebY[iY] = 0.0;
    }
    if (iY == 0) {
      setup->ebX[iY] = 0.0;
    }
    if (calData->index == 31) {
      amdmsInfo(__FILE__, __LINE__, "ROM %d = %.2f", iY, setup->ebY[iY]);
    }
  }
  if (amdmsSmoothDataByFiniteDiff2W(setup->ebX, setup->ebY, setup->ebYe, 1000.0, calData->ny) == amdmsSUCCESS) {
    for (iY = 0; iY < calData->ny ; iY++) {
      if (calData->index == 31) {
	amdmsInfo(__FILE__, __LINE__, "ROS %d = %.2f", iY, setup->ebYe[iY]);
      }
      setup->rowOffsets[iY] = (amdmsPIXEL)(setup->ebYe[iY]);
      for (iX = 0; iX < calData->nx; iX++) {
	iPixel = calData->nx*iY + iX;
	calData->data[iPixel] -= setup->rowOffsets[iY];
      }
    }
  } else {
    amdmsWarning(__FILE__, __LINE__, "smoothing of the electronic bias does not work!");
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateEBias(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsELECTRONIC_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  switch (setup->ebAlgo) {
  case amdmsEBA_MAP: return amdmsCompensateEBiasByMap(setup, calData);
  case amdmsEBA_SIMPLE_EXP1D: return amdmsCompensateEBiasBySimpleExp1D(setup, calData);
  case amdmsEBA_SIMPLE_SMOOTH1D: return amdmsCompensateEBiasBySimpleSmooth1D(setup, calData);
  default:
    amdmsError(__FILE__, __LINE__,
	      "unsupported electronic bias compensation algorithm (%d)",
	      setup->ebAlgo);
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsCompensateGlobalBias(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  int           nPixels;
  int           iPixel;
  amdmsPIXEL    m;
  amdmsPIXEL    v;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsGLOBAL_BIAS_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  nPixels = calData->nx*calData->ny;
  if (amdmsCalcStat(setup, calData, 0, setup->saWinX, setup->saWinY, setup->saWinWidth, setup->saWinHeight, &m, &v) != amdmsFAILURE) {
    setup->globalBias = m;
  } else {
    setup->globalBias = 0.0;
  }
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    calData->data[iPixel] -= setup->globalBias;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateFNoise(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  //int          nPixels;
  int          iPixel;
  int          iX, iY;
  int          iW;
  float        w;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsROW_OFFSET_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  //nPixels = calData->nx*calData->ny;
  if (setup->rowOffsets == NULL) {
    setup->rowOffsets = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowOffsets == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowOffsets)!");
      return amdmsFAILURE;
    }
  }
  if (setup->rowGood == NULL) {
    setup->rowGood = (int *)calloc((size_t)setup->detNY, sizeof(int));
    if (setup->rowGood == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowGood)!");
      return amdmsFAILURE;
    }
  }
  if (setup->rowWeight == NULL) {
    setup->rowWeight = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowWeight == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowWeight)!");
      return amdmsFAILURE;
    }
  }
  if (setup->rowSum == NULL) {
    setup->rowSum = (amdmsPIXEL *)calloc((size_t)setup->detNY, sizeof(amdmsPIXEL));
    if (setup->rowSum == NULL) {
      amdmsFatal(__FILE__, __LINE__, "memory allocation failure (rowSum)!");
      return amdmsFAILURE;
    }
  }
  for (iY = 0; iY < setup->detNY; iY++) {
    setup->rowOffsets[iY] = 0.0;
    setup->rowGood[iY] = 0;
  }
  for (iY = 0; iY < calData->ny ; iY++) {
    for (iX = setup->saWinX; iX < (setup->saWinX + setup->saWinWidth); iX++) {
      iPixel = calData->nx*iY + iX;
      if (setup->bpmData.data[iPixel] == amdmsGOOD_PIXEL) {
	setup->rowOffsets[iY] += calData->data[iPixel];
	setup->rowGood[iY]++;
      }
    }
    if (setup->rowGood[iY] == 0) {
      setup->rowOffsets[iY] = 0.0;
    } else {
      setup->rowOffsets[iY] /= (amdmsPIXEL)setup->rowGood[iY];
      /*if (calData->index == 0) {*/
      amdmsDebug(__FILE__, __LINE__, "RO %f %d = %.2f", calData->index, iY, setup->rowOffsets[iY]);
      /*}*/
    }
  }
  if (setup->saGaussHeight != 0) {
    /* use several rows for 1/f noise compensation */
    for (iY = 0; iY < setup->detNY; iY++) {
      setup->rowWeight[iY] = 0.0;
      setup->rowSum[iY] = 0.0;
    }
    for (iW = -setup->saGaussHeight;iW < setup->saGaussHeight + 1; iW++) {
      w = exp(-iW*iW/2/setup->saGaussSigma/setup->saGaussSigma);
      for (iY = MAX(0, -iW); iY < MIN(calData->ny, calData->ny - iW); iY++) {
	setup->rowSum[iY] += w*setup->rowOffsets[iY + iW]*setup->rowGood[iY + iW];
	setup->rowWeight[iY] += w*setup->rowGood[iY + iW];
      }
    }
    for (iY = 0; iY < calData->ny; iY++) {
      if (setup->rowWeight[iY] != 0.0) {
	setup->rowOffsets[iY] = setup->rowSum[iY]/setup->rowWeight[iY];
      }
    }
  }
  for (iY = 0; iY < calData->ny ; iY++) {
    for (iX = 0; iX < calData->nx; iX++) {
      iPixel = calData->nx*iY + iX;
      calData->data[iPixel] -= setup->rowOffsets[iY];
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateNonlinearity(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  int          nPixels;
  int          iPixel;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsNONLINEARITY_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  nPixels = calData->nx*calData->ny;
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    calData->data[iPixel] = calData->data[iPixel]/(setup->nlmDataA0.data[iPixel] - setup->nlmDataA1.data[iPixel]*calData->data[iPixel]);
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateFlatfield(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  int          nPixels;
  int          iPixel;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsFLATFIELD_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  nPixels = calData->nx*calData->ny;
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    calData->data[iPixel] /= setup->ffmData.data[iPixel];
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCompensateBadPixel(amdmsCALIBRATION_SETUP *setup, amdmsDATA *calData)
{
  //int          nPixels;
  int          iPixel;
  int          hPixel;
  int          iX, iY;
  int          hX, hY;
  float        w;

  if ((setup == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  if ((setup->corrFlag & amdmsBAD_PIXEL_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  //nPixels = calData->nx*calData->ny;
  for (iY = 0; iY < calData->ny ; iY++) {
    for (iX = 0; iX < calData->nx; iX++) {
      iPixel = calData->nx*iY + iX;
      if (setup->bpmData.data[iPixel] == amdmsGOOD_PIXEL) continue;
      w = 0.0;
      calData->data[iPixel] = 0.0;
      for (hX = -setup->bpiRadius + 1; hX < setup->bpiRadius; hX++){
	if (iX + hX < 0) continue;
	if (iX + hX >= calData->nx) continue;
	for (hY = -setup->bpiRadius + 1; hY < setup->bpiRadius; hY++) {
	  if (iY + hY < 0) continue;
	  if (iY + hY >= calData->ny) continue;
	  hPixel = iPixel + calData->nx*hY + hX;
	  if (setup->bpmData.data[hPixel] == amdmsBAD_PIXEL) continue; /* ignore bad pixels */
	  calData->data[iPixel] += calData->data[hPixel]*setup->bpiWeights[setup->bpiRadius*ABS(hY) + ABS(hX)];
	  w += setup->bpiWeights[setup->bpiRadius*ABS(hY) + ABS(hX)];
	}
      }
      calData->data[iPixel] /= w;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalibrateData(amdmsCALIBRATION_SETUP *setup, amdmsDATA *rawData, amdmsDATA *calData)
{
  //int          nPixels;

  if ((setup == NULL) || (rawData == NULL) || (calData == NULL)) {
    return amdmsFAILURE;
  }
  //nPixels = rawData->nx*rawData->ny;
  if (amdmsAllocateData(calData, rawData->nx, rawData->ny) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  /* copy raw data into calibrated data */
  amdmsCopyData(calData, rawData);
  if (setup->corrFlag == amdmsNO_CORRECTION) {
    /* no calibration requested */
    return amdmsSUCCESS;
  }
  if (amdmsCompensatePixelBias(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateEBias(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateGlobalBias(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateFNoise(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateNonlinearity(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateFlatfield(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCompensateBadPixel(setup, calData) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsBOOL amdmsIsPixelValid(amdmsCALIBRATION_SETUP *setup, int iImage, int iPixel)
{
  if (setup->bpmData.data[iPixel] == amdmsBAD_PIXEL) {
    return amdmsFALSE;
  }
  if (iImage < (int)(setup->pemDataFI.data[iPixel])) {
    return amdmsTRUE;
  }
  if (iImage >=  (int)(setup->pemDataLI.data[iPixel])) {
    return amdmsTRUE;
  }
  return amdmsFALSE;
}

amdmsBOOL amdmsIsPixelAffectedByPE(amdmsCALIBRATION_SETUP *setup, int iImage, int iPixel)
{
  if (iImage >=  (int)(setup->pemDataLI.data[iPixel])) {
    return amdmsFALSE;
  }
  if (iImage < (int)(setup->pemDataFI.data[iPixel])) {
    return amdmsFALSE;
  }
  return amdmsTRUE;
}

amdmsCOMPL amdmsCalcStat(amdmsCALIBRATION_SETUP *setup,
			 amdmsDATA *data,
			 int iImage, int x, int y, int width, int height,
			 amdmsPIXEL *mean, amdmsPIXEL *var)
{
  double       m = 0.0;          /* sum or average value */
  double       v = 0.0;          /* variance             */
  int          n = 0;            /* number of values     */
  int          nx = data->nx;    /* width of the data array */
  amdmsPIXEL   imgNr = (amdmsPIXEL)iImage;  /* the image number as a pixel value */ 
  int          iX;
  int          iY;
  amdmsPIXEL  *datPtr = NULL;    /* pointer into the data array */
  amdmsPIXEL  *bpmPtr = NULL;    /* pointer into the bad pixel map */
  amdmsPIXEL  *fiPtr  = NULL;    /* pointer to the first image number array of the particle event map */
  amdmsPIXEL  *liPtr  = NULL;    /* pointer to the last image number array of the particle event map  */
  /* first calculate the mean value */
  for (iY = 0; iY < height; iY++) {
    int offset = nx*(y + iY) + x;
    datPtr = data->data + offset;
    bpmPtr = setup->bpmData.data + offset;
    fiPtr  = setup->pemDataFI.data + offset;
    liPtr  = setup->pemDataLI.data + offset;
    for (iX = 0; iX < width; iX++) {
      if ((*bpmPtr == amdmsGOOD_PIXEL) &&
	  ((*liPtr <= imgNr) || (*fiPtr > imgNr))) {
	m += *datPtr;
	n++;
      }
      datPtr++;
      bpmPtr++;
      fiPtr++;
      liPtr++;
    }
  }
  if (n == 0) {
    m = 0.0;
  } else {
    m = m/(double)n;
  }
  if (mean != NULL) {
    *mean = (amdmsPIXEL)m;
  }
  if (var == NULL) {
    /* variance is not requested */
    return amdmsSUCCESS;
  }
  /* and now calculate the variance */
  if (n == 0) {
    *var = 1.0;
    return amdmsSUCCESS;
  }
  for (iY = 0; iY < height; iY++) {
    int offset = nx*(y + iY) + x;
    datPtr = data->data + offset;
    bpmPtr = setup->bpmData.data + offset;
    fiPtr  = setup->pemDataFI.data + offset;
    liPtr  = setup->pemDataLI.data + offset;
    for (iX = 0; iX < width; iX++) {
      if ((*bpmPtr == amdmsGOOD_PIXEL) &&
	  ((*liPtr <= imgNr) || (*fiPtr > imgNr))) {
	v += (*datPtr - m)*(*datPtr - m);
	n++;
      }
      datPtr++;
      bpmPtr++;
      fiPtr++;
      liPtr++;
    }
  }
  v = v/(double)(n - 1);
  *var = (amdmsPIXEL)v;
  return amdmsSUCCESS;
}

/*
amdmsCOMPL amdmsCalcStat(amdmsCALIBRATION_SETUP *setup, amdmsDATA *data, int iImage, int x, int y, int width, int height, amdmsPIXEL *mean, amdmsPIXEL *var)
{
  int           iX;
  int           iY;
  int           iPixel;
  int           goodPixels;
  double        m;
  double        v;
  amdmsPIXEL   *bpm = NULL;
  amdmsPIXEL   *pemFI = NULL;
  amdmsPIXEL   *pemLI = NULL;

  amdmsDebug(__FILE__, __LINE__, "amdmsCalcStat(.., .., %d, %d, %d, %d, %d, .., ..)",
	    iImage, x, y, width, height);
  if ((setup == NULL) || (data == NULL)) {
    return amdmsFAILURE;
  }
  m = 0.0;
  goodPixels = 0;
  bpm = setup->bpmData.data;
  pemFI = setup->pemDataFI.data;
  pemLI = setup->pemDataLI.data;
  for (iY = y; iY < (y + height); iY++) {
    iPixel = data->nx*iY + x;
    for (iX = 0; iX < width; iX++) {
      if (bpm[iPixel] != amdmsGOOD_PIXEL) continue;
      if ((iImage >= (int)(pemFI[iPixel])) && (iImage <  (int)(pemLI[iPixel]))) continue;
      m += data->data[iPixel];
      goodPixels++;
      iPixel++;
    }
  }
  if (goodPixels == 0) {
    m = 0.0;
  } else {
    m = m/goodPixels;
  }
  if (mean != NULL) {
    *mean = (amdmsPIXEL)m;
  }
  if (var == NULL) {
    return amdmsSUCCESS;
  }
  if (goodPixels == 0) {
    *var = 1.0;
  }
  v = 0.0;
  for (iY = y; iY < (y + height); iY++) {
    iPixel = data->nx*iY + x;
    for (iX = x; iX < (x + width); iX++) {
      if (bpm[iPixel] != amdmsGOOD_PIXEL) continue;
      if ((iImage >= (int)(pemFI[iPixel])) && (iImage <  (int)(pemLI[iPixel]))) continue;
      v += (data->data[iPixel] - m)*(data->data[iPixel] - m);
      iPixel++;
    }
  }
  v = v/(goodPixels - 1);
  *var = (amdmsPIXEL)v;
  return amdmsSUCCESS;
}
*/

amdmsCOMPL amdmsCalcStatBox(amdmsCALIBRATION_SETUP *setup, amdmsDATA *data, int iImage,
			    int xLimit, int yLimit, int widthLimit, int heightLimit,
			    int xCenter, int yCenter, int innerSize, int outerSize,
			    amdmsPIXEL *mean, amdmsPIXEL *var)
{
  int           iX;
  int           iY;
  int           iPixel;
  int           goodPixels;
  int           oLeft, oRight, oBottom, oTop;
  int           iLeft, iRight, iBottom, iTop;
  double        m;
  double        v;
  //amdmsPIXEL   *bpm = NULL;
  //amdmsPIXEL   *pemFI = NULL;
  //amdmsPIXEL   *pemLI = NULL;

  /*
  amdmsDebug(__FILE__, __LINE__, "amdmsCalcStatBox(.., .., %d, %d, %d, %d, %d,, %d, %d, %d, %d .., ..)",
	    iImage, xLimit, yLimit, widthLimit, heightLimit, xCenter, yCenter, innerSize, outerSize);
  */
  if ((setup == NULL) || (data == NULL)) {
    return amdmsFAILURE;
  }
  oLeft   = MAX(xLimit, xCenter - outerSize);
  oRight  = MIN(xLimit + widthLimit - 1, xCenter + outerSize);
  oBottom = MAX(yLimit, yCenter - outerSize);
  oTop    = MIN(yLimit + heightLimit - 1, yCenter + outerSize);

  iLeft   = MAX(xLimit, xCenter - innerSize);
  iRight  = MIN(xLimit + widthLimit - 1, xCenter + innerSize);
  iBottom = MAX(yLimit, yCenter - innerSize);
  iTop    = MIN(yLimit + heightLimit - 1, yCenter + innerSize);

  m = 0.0;
  goodPixels = 0;
  //bpm = setup->bpmData.data;
  //pemFI = setup->pemDataFI.data;
  //pemLI = setup->pemDataLI.data;
  for (iX = oLeft; iX <= oRight; iX++) {
    for (iY = oBottom; iY <= oTop; iY++) {
      if ((iX >= iLeft) && (iX <= iRight) && (iY >= iBottom) && (iY <= iTop)) continue;
      iPixel = data->nx*iY + iX;
      if (!amdmsIsPixelValid(setup, iImage, iPixel)) continue;
      m += data->data[iPixel];
      goodPixels++;
    }
  }
  m = m/goodPixels;
  if (mean != NULL) {
    *mean = (amdmsPIXEL)m;
  }
  if (var == NULL) {
    /* variance is not requested */
    return amdmsSUCCESS;
  }
  v = 0.0;
  for (iX = oLeft; iX <= oRight; iX++) {
    for (iY = oBottom; iY <= oTop; iY++) {
      if ((iX >= iLeft) && (iX <= iRight) && (iY >= iBottom) && (iY <= iTop)) continue;
      iPixel = data->nx*iY + iX;
      if (!amdmsIsPixelValid(setup, iImage, iPixel)) continue;
      v += (data->data[iPixel] - m)*(data->data[iPixel] - m);
    }
  }
  v = v/(goodPixels - 1);
  *var = (amdmsPIXEL)v;
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsSmoothData(amdmsCALIBRATION_SETUP *calib, amdmsDATA *src, amdmsDATA *dst)
{
  //int          nPixels;
  int          iPixel;
  int          hPixel;
  int          iX, iY;
  int          hX, hY;
  float        w;

  /* copy raw data into calibrated dataCheck arguments */
  if ((calib == NULL) || (src == NULL) || (dst == NULL)) {
    return amdmsFAILURE;
  }
  if ((calib->corrFlag & amdmsBAD_PIXEL_CORRECTION) == 0) {
    return amdmsSUCCESS;
  }
  //nPixels = src->nx*src->ny;
  for (iY = 0; iY < src->ny ; iY++) {
    for (iX = 0; iX < src->nx; iX++) {
      iPixel = src->nx*iY + iX;
      if (calib->bpmData.data[iPixel] == amdmsGOOD_PIXEL) continue;
      w = 0.0;
      dst->data[iPixel] = 0.0;
      for (hX = -calib->bpiRadius + 1; hX < calib->bpiRadius; hX++){
	if (iX + hX < 0) continue;
	if (iX + hX >= src->nx) continue;
	for (hY = -calib->bpiRadius + 1; hY < calib->bpiRadius; hY++) {
	  if (iY + hY < 0) continue;
	  if (iY + hY >= src->ny) continue;
	  hPixel = iPixel + src->nx*hY + hX;
	  if (calib->bpmData.data[hPixel] == amdmsBAD_PIXEL) continue; /* ignore bad pixels */
	  dst->data[iPixel] += src->data[hPixel]*calib->bpiWeights[calib->bpiRadius*ABS(hY) + ABS(hX)];
	  w += calib->bpiWeights[calib->bpiRadius*ABS(hY) + ABS(hX)];
	}
      }
      dst->data[iPixel] /= w;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCleanUpFlatfield(amdmsCALIBRATION_SETUP *calib, amdmsSTRIPE_SETUP *stripes, amdmsDATA *mean, amdmsDATA *var)
{
  int          x, width;
  int          iX, iY;
  int          iVS;
  int          idx;
  amdmsPIXEL  *cuData = NULL;
  amdmsPIXEL   m0, a;
  amdmsPIXEL   m1;
  amdmsPIXEL   m2;
  amdmsPIXEL   f;

  cuData = (amdmsPIXEL*)calloc((size_t)(calib->detNX), sizeof(amdmsPIXEL));
  if (cuData == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (cuData)!");
    return amdmsFAILURE;
  }
  /* compensate for vertical ramp */
  for (iX = 0; iX < mean->nx; iX++) {
    amdmsCalcStat(calib, mean, 0, iX, amdmsCU_ROW - amdmsCU_NROWS, 1, 2*amdmsCU_NROWS, &m1, NULL);
    amdmsCalcStat(calib, mean, 0, iX, mean->ny - amdmsCU_ROW - amdmsCU_NROWS, 1, 2*amdmsCU_NROWS, &m2, NULL);
    if ((m1 == 0.0) && (m2 == 0.0)) {
      cuData[iX] = 0.0;
      continue;
    }
    m0 = 0.5*(m1 + m2);
    cuData[iX] = m0;
    a = (m2 - m1)/(float)(mean->ny - 2*amdmsCU_ROW);
    for (iY = 0; iY < mean->ny; iY++) {
      idx = mean->nx*iY + iX;
      f = m0/(m1 + a*(float)(iY - amdmsCU_ROW));
      mean->data[idx] *= f;
      if (var == NULL) continue;
      var->data[idx] *= f*f;
    }
  }
  /* compensate for horizontal profile */
  if (stripes == NULL) {
    x = 0;
    width = mean->nx;
    m0 = 0.0;
    for (iX = x; iX < x + width; iX++) {
      m0 += cuData[iX];
    }
    m0 /= (float)width;
    for (iX = x; iX < x + width; iX++) {
      f = m0/cuData[iX];
      for (iY = 0; iY < mean->ny; iY++) {
	idx = mean->nx*iY + iX;
	mean->data[idx] *= f;
	if (var == NULL) continue;
	var->data[idx] *= f*f;
      }
    }
  } else {
    for (iVS = 0; iVS < stripes->nVStripes; iVS++) {
      x = stripes->vStripes[iVS].pos;
      width = stripes->vStripes[iVS].size;
      if (stripes->vStripes[iVS].flags == amdmsUSE_NOTHING) {
	continue;
      }
      m0 = 0.0;
      for (iX = x; iX < x + width; iX++) {
	m0 += cuData[iX];
      }
      m0 /= (float)width;
      for (iX = x; iX < x + width; iX++) {
	f = m0/cuData[iX];
	for (iY = 0; iY < mean->ny; iY++) {
	  idx = mean->nx*iY + iX;
	  mean->data[idx] *= f;
	  if (var == NULL) continue;
	  var->data[idx] *= f*f;
	}
      }
    }
  }
  free(cuData);
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCleanUpFlatfieldSmooth(amdmsCALIBRATION_SETUP *calib, amdmsSTRIPE_SETUP *stripes, amdmsDATA *mean, amdmsDATA *var)
{
  int          x, width;
  int          iX, iY;
  int          iVS;
  int          idx;
  double      *lowerProfile = NULL;
  double      *upperProfile = NULL;
  double      *smoothProfile = NULL;
  amdmsPIXEL   m0, a;
  amdmsPIXEL   m1;
  amdmsPIXEL   m2;
  amdmsPIXEL   f;

  lowerProfile = (double*)calloc((size_t)(mean->nx), sizeof(double));
  if (lowerProfile == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (lowerProfile)!");
    return amdmsFAILURE;
  }
  upperProfile = (double*)calloc((size_t)(mean->nx), sizeof(double));
  if (upperProfile == NULL) {
    free(lowerProfile);
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (upperProfile)!");
    return amdmsFAILURE;
  }
  smoothProfile = (double*)calloc((size_t)(mean->nx), sizeof(double));
  if (smoothProfile == NULL) {
    free(lowerProfile);
    free(upperProfile);
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (smoothProfile)!");
    return amdmsFAILURE;
  }
  /* calculate a profile on the lower and upper side of the detector */
  for (iX = 0; iX < mean->nx; iX++) {
    amdmsCalcStat(calib, mean, 0, iX, amdmsCU_ROW - amdmsCU_NROWS, 1, 2*amdmsCU_NROWS, &m1, NULL);
    lowerProfile[iX] = m1;
    amdmsCalcStat(calib, mean, 0, iX, mean->ny - amdmsCU_ROW - amdmsCU_NROWS, 1, 2*amdmsCU_NROWS, &m2, NULL);
    upperProfile[iX] = m2;
  }
  if (amdmsSmoothDataByFiniteDiff1(lowerProfile, smoothProfile, 20.0, mean->nx) == amdmsSUCCESS) {
    memcpy(lowerProfile, smoothProfile, mean->nx*sizeof(double));
  }
  if (amdmsSmoothDataByFiniteDiff1(upperProfile, smoothProfile, 20.0, mean->nx) == amdmsSUCCESS) {
    memcpy(upperProfile, smoothProfile, mean->nx*sizeof(double));
  }
  /* calculate the center profile */
  for (iX = 0; iX < mean->nx; iX++) {
    smoothProfile[iX] = 0.5*(lowerProfile[iX] + upperProfile[iX]);
  }
  /* compensate for vertical ramp */
  for (iX = 0; iX < mean->nx; iX++) {
    m1 = lowerProfile[iX];
    m2 = upperProfile[iX];
    m0 = smoothProfile[iX];
    /* amdmsDebug(__FILE__, __LINE__, "  vertical ramp at %d => %.1f - %.1f - %.1f", iX, m2, m0, m1); */
    if ((m1 == 0.0) && (m2 == 0.0)) {
      continue;
    }
    a = (m2 - m1)/(float)(mean->ny - 2*amdmsCU_ROW);
    for (iY = 0; iY < mean->ny; iY++) {
      idx = mean->nx*iY + iX;
      f = m0/(m1 + a*(float)(iY - amdmsCU_ROW));
      mean->data[idx] *= f;
      if (var == NULL) continue;
      var->data[idx] *= f*f;
    }
  }
  /* compensate for horizontal profile */
  if (stripes == NULL) {
    x = 0;
    width = mean->nx;
    m0 = 0.0;
    for (iX = x; iX < x + width; iX++) {
      m0 += smoothProfile[iX];
    }
    m0 /= (float)width;
    for (iX = x; iX < x + width; iX++) {
      f = m0/smoothProfile[iX];
      for (iY = 0; iY < mean->ny; iY++) {
	idx = mean->nx*iY + iX;
	mean->data[idx] *= f;
	if (var == NULL) continue;
	var->data[idx] *= f*f;
      }
    }
  } else {
    for (iVS = 0; iVS < stripes->nVStripes; iVS++) {
      if (stripes->vStripes[iVS].flags == amdmsUSE_NOTHING) {
	continue;
      }
      x = stripes->vStripes[iVS].pos;
      width = stripes->vStripes[iVS].size;
      m0 = 0.0;
      for (iX = x; iX < x + width; iX++) {
	m0 += smoothProfile[iX];
      }
      m0 /= (float)width;
      amdmsDebug(__FILE__, __LINE__, "  stripe mean = %f", m0);
      for (iX = x; iX < x + width; iX++) {
	f = m0/smoothProfile[iX];
	for (iY = 0; iY < mean->ny; iY++) {
	  idx = mean->nx*iY + iX;
	  mean->data[idx] *= f;
	  if (var == NULL) continue;
	  var->data[idx] *= f*f;
	}
      }
    }
  }
  free(lowerProfile);
  free(upperProfile);
  free(smoothProfile);
  return amdmsSUCCESS;
}
