#include <math.h>

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


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

static amdmsCOMPL amdmsLoadAllStatistics(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsFitPixel(amdmsALGO_PROPERTIES_ENV *env,
			      int nPoints, double *x, double *y, double *ye,
			      amdmsFIT_DATA_ENV *fit,
			      int iPixel,
			      amdmsDATA *fitValues);
static void amdmsCheckFitValues(amdmsDATA *fitValues, int nValues, int iPixel);

static amdmsCOMPL amdmsCalcDarkCurrent(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsCalcFlatfield(amdmsALGO_PROPERTIES_ENV *env);

static amdmsCOMPL amdmsCalcConversionFactorAmberR(amdmsALGO_PROPERTIES_ENV *env,
						  int rX, int rY, int rW, int rH);
static amdmsCOMPL amdmsCalcConversionFactorGlobalLineR(amdmsALGO_PROPERTIES_ENV *env,
						       int rX, int rY, int rW, int rH);
static amdmsCOMPL amdmsCalcConversionFactorCentralLineR(amdmsALGO_PROPERTIES_ENV *env,
							int rX, int rY, int rW, int rH);
static amdmsCOMPL amdmsCalcConversionFactorLocalPairs(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsCalcConversionFactor(amdmsALGO_PROPERTIES_ENV *env);

static amdmsCOMPL amdmsCalcNonlinearity(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsCalcPhotonTransferCurve(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsCalcHistogramForArea(amdmsALGO_PROPERTIES_ENV *env,
					    int aoiX, int aoiY,
					    int aoiWidth, int aoiHeight,
					    int *inCount, int *outCount);
static amdmsCOMPL amdmsCalcHistogram(amdmsALGO_PROPERTIES_ENV *env);
static amdmsCOMPL amdmsCreateDarkCurrentMap(amdmsALGO_PROPERTIES_ENV *env,
					    amdmsFITS *outfile,
					    amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateFlatfieldMap(amdmsALGO_PROPERTIES_ENV *env,
					  amdmsFITS *outfile,
					  amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateFlatfieldFitMap(amdmsALGO_PROPERTIES_ENV *env,
					     amdmsFITS *outfile,
					     amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateConversionFactorMap(amdmsALGO_PROPERTIES_ENV *env,
						 amdmsFITS *outfile,
						 amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateNonlinearityMap(amdmsALGO_PROPERTIES_ENV *env,
					     amdmsFITS *outfile,
					     amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateNonlinearityFitMap(amdmsALGO_PROPERTIES_ENV *env,
						amdmsFITS *outfile,
						amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreatePhotonTransferCurveMap(amdmsALGO_PROPERTIES_ENV *env,
						    amdmsFITS *outfile,
						    amdmsFITS_FLAGS flags);
static amdmsCOMPL amdmsCreateHistogramMap(amdmsALGO_PROPERTIES_ENV *env,
					  amdmsFITS *outfile,
					  amdmsFITS_FLAGS flags);

amdmsCOMPL amdmsCreatePropertiesAlgo(amdmsALGO_PROPERTIES_ENV **env)
{
  int                       i;
  amdmsALGO_ENV             *henv = NULL;
  amdmsALGO_PROPERTIES_ENV  *hhenv = NULL;

  /* allocate the structure if neccessary */
  if (*env == NULL) {
    hhenv = (amdmsALGO_PROPERTIES_ENV*)calloc(1L, sizeof(amdmsALGO_PROPERTIES_ENV));
    if (hhenv == NULL) {
      return amdmsFAILURE;
    }
    henv = &(hhenv->env);
    /* call the initialization of a simple algorithm environment */
    if (amdmsCreateAlgo(&henv) != amdmsSUCCESS) {
      free(hhenv);
      return amdmsFAILURE;
    }
    hhenv->allocated = 1;
    *env = hhenv;
  } else {
    /* call the initialization of a simple algorithm environment */
    hhenv = *env;
    henv = &(hhenv->env);
    if (amdmsCreateAlgo(&henv) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
    hhenv->allocated = 0;
  }
  /* the default content is the pixel statistics */
  henv->inFiles.defFlags.content = amdmsPIXEL_STAT_CONTENT;
  /* initialize only the additional members */
  hhenv->nuicFlag = amdmsFALSE;
  hhenv->kADC = 8.55;
  hhenv->satFlag = amdmsFALSE;
  hhenv->satWidth = 0.0;
  hhenv->satHeight = 0.0;
  hhenv->nDelStart = 0;
  hhenv->nDelMiddle = 1;
  hhenv->nDelEnd = 0;
  hhenv->cfAlgo = amdmsCFA_CENTRAL_LINE;
  hhenv->hX.content = amdmsHC_EXPTIME;
  hhenv->hX.scale   = amdmsHS_LINEAR;
  hhenv->hX.steps   = 500;
  hhenv->hX.min     = 0.0;
  hhenv->hX.max     = 10.0;
  hhenv->hY.content = amdmsHC_MEAN;
  hhenv->hY.scale   = amdmsHS_LINEAR;
  hhenv->hY.steps   = 500;
  hhenv->hY.min     = 0.0;
  hhenv->hY.max     = 65536.0;
  hhenv->nInputs    = 0;
  hhenv->exptimes   = NULL;
  hhenv->meanPixels = NULL;
  hhenv->varPixels  = NULL;
  for (i = 0; i < amdmsFLATFIELD_FIT_NROWS; i++) {
    amdmsInitData(&(hhenv->ffFit[i]));
  }
  for (i = 0; i < amdmsCONVERSION_FACTOR_FIT_NROWS; i++) {
    amdmsInitData(&(hhenv->cfFit[i]));
  }
  for (i = 0; i < amdmsNONLINEARITY_FIT_NROWS; i++) {
    amdmsInitData(&(hhenv->nlFit[i]));
  }
  for (i = 0; i < amdmsPTC_NROWS; i++) {
    amdmsInitData(&(hhenv->ptcFit[i]));
  }
  for (i = 0; i < amdmsDARK_CURRENT_NROWS; i++) {
    amdmsInitData(&(hhenv->dcFit[i]));
  }
  amdmsInitData(&(hhenv->cfmData));
  amdmsInitData(&(hhenv->histoData));
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsDestroyPropertiesAlgo(amdmsALGO_PROPERTIES_ENV **env)
{
  int                        i;
  amdmsALGO_PROPERTIES_ENV  *henv = NULL;
  amdmsALGO_ENV             *hhenv = NULL;

  if (env == NULL) {
    return amdmsFAILURE;
  }
  if (*env == NULL) {
    return amdmsSUCCESS;
  }
  henv = *env;
  if (henv->exptimes != NULL) {
    free(henv->exptimes);
    henv->exptimes = NULL;
  }
  if (henv->meanPixels != NULL) {
    for (i = 0; i < henv->nInputs; i++) {
      amdmsFreeData(&(henv->meanPixels[i]));
    }
    free(henv->meanPixels);
    henv->meanPixels = NULL;
  }
  if (henv->varPixels != NULL) {
    for (i = 0; i < henv->nInputs; i++) {
      amdmsFreeData(&(henv->varPixels[i]));
    }
    free(henv->varPixels);
    henv->varPixels = NULL;
  }
  for (i = 0; i < amdmsFLATFIELD_FIT_NROWS; i++) {
    amdmsFreeData(&(henv->ffFit[i]));
  }
  for (i = 0; i < amdmsCONVERSION_FACTOR_FIT_NROWS; i++) {
    amdmsFreeData(&(henv->cfFit[i]));
  }
  for (i = 0; i < amdmsNONLINEARITY_FIT_NROWS; i++) {
    amdmsFreeData(&(henv->nlFit[i]));
  }
  for (i = 0; i < amdmsPTC_NROWS; i++) {
    amdmsFreeData(&(henv->ptcFit[i]));
  }
  for (i = 0; i < amdmsDARK_CURRENT_NROWS; i++) {
    amdmsFreeData(&(henv->dcFit[i]));
  }
  amdmsFreeData(&(henv->cfmData));
  amdmsFreeData(&(henv->histoData));
  hhenv = &(henv->env);
  amdmsDestroyAlgo(&hhenv);
  if (henv->allocated) {
    henv->allocated = amdmsFALSE;
    free(henv);
    *env = NULL;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsDoProperties(amdmsALGO_PROPERTIES_ENV *env)
{
  amdmsCOMPL   retVal = amdmsSUCCESS;
  amdmsFITS   *infile = NULL;
  int          iOutput;
  amdmsFITS   *outfile = NULL;
  amdmsBOOL    ffRequested = amdmsFALSE;
  amdmsBOOL    cfRequested = amdmsFALSE;
  amdmsBOOL    nlRequested = amdmsFALSE;
  amdmsBOOL    ptcRequested = amdmsFALSE;
  amdmsBOOL    dcRequested = amdmsFALSE;
  amdmsBOOL    hRequested = amdmsFALSE;
  
  amdmsDebug(__FILE__, __LINE__, "amdmsDoProperties(...)");
  if (env == NULL) {
    amdmsError(__FILE__, __LINE__, "no valid environment given!");
    return amdmsFAILURE;
  }
  if (env->env.inFiles.nNames < 2) {
    /* no filename given but we need several files */
    amdmsError(__FILE__, __LINE__, "no filename given, several input files are needed");
    return amdmsFAILURE;
  }
  if (amdmsReadAllMaps(&(env->env.calib)) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsLoadAllStatistics(env) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  amdmsRecalcStripes(&(env->env.stripes), env->meanPixels[0].nx, env->meanPixels[0].ny);
  for (iOutput = 0; iOutput < env->env.outFiles.nNames; iOutput++) {
    switch (env->env.outFiles.flags[iOutput].content) {
    case amdmsFLATFIELD_CONTENT:
    case amdmsFLATFIELD_FIT_CONTENT:
      ffRequested = amdmsTRUE;
      break;
    case amdmsCONVERSION_FACTOR_CONTENT:
      cfRequested = amdmsTRUE;
      break;
    case amdmsNONLINEARITY_CONTENT:
    case amdmsNONLINEARITY_FIT_CONTENT:
      nlRequested = amdmsTRUE;
      break;
    case amdmsPTC_CONTENT:
      ptcRequested = amdmsTRUE;
      break;
    case amdmsDARK_CURRENT_CONTENT:
      dcRequested = amdmsTRUE;
      break;
    case amdmsHISTOGRAM_CONTENT:
      hRequested = amdmsTRUE;
      break;
    default:;
    }
  }
  if (cfRequested) {
    if (amdmsCalcConversionFactor(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  if (ffRequested) {
    if (amdmsCalcFlatfield(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  if (nlRequested) {
    if (amdmsCalcNonlinearity(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  if (ptcRequested) {
    if (amdmsCalcPhotonTransferCurve(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  if (dcRequested) {
    if (amdmsCalcDarkCurrent(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  if (hRequested) {
    if (amdmsCalcHistogram(env) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  /* generate all outputs */
  if (amdmsOpenFitsFile(&infile, env->env.inFiles.names[0]) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsOpenData(infile, env->env.inFiles.flags[0], 1) != amdmsSUCCESS) {
    amdmsCloseFitsFile(&infile);
    return amdmsFAILURE;
  }
  for (iOutput = 0; (iOutput < env->env.outFiles.nNames) && (retVal == amdmsSUCCESS); iOutput++) {
    retVal = amdmsCreateFitsFile(&outfile, env->env.outFiles.names[iOutput]);
    if (retVal != amdmsSUCCESS) break;
    retVal = amdmsCopyHeader(outfile, infile);
    if (retVal != amdmsSUCCESS) {
      amdmsCloseFitsFile(&outfile);
      break;
    }
    amdmsSetRegions(outfile, infile);
    switch (env->env.outFiles.flags[iOutput].content) {
    case amdmsFLATFIELD_CONTENT:
      retVal = amdmsCreateFlatfieldMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsFLATFIELD_FIT_CONTENT:
      retVal = amdmsCreateFlatfieldFitMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsCONVERSION_FACTOR_CONTENT:
      retVal = amdmsCreateConversionFactorMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsNONLINEARITY_CONTENT:
      retVal = amdmsCreateNonlinearityMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsNONLINEARITY_FIT_CONTENT:
      retVal = amdmsCreateNonlinearityFitMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsPTC_CONTENT:
      retVal = amdmsCreatePhotonTransferCurveMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsDARK_CURRENT_CONTENT:
      retVal = amdmsCreateDarkCurrentMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    case amdmsHISTOGRAM_CONTENT:
      retVal = amdmsCreateHistogramMap(env, outfile, env->env.outFiles.flags[iOutput]);
      break;
    default:
      amdmsError(__FILE__, __LINE__,
		 "unsupported file content (%d)",
		 env->env.outFiles.flags[iOutput].content);
      retVal = amdmsFAILURE;
    }
    retVal = amdmsCloseFitsFile(&outfile);
  }
  amdmsCloseFitsFile(&infile);

  return amdmsSUCCESS;
}


amdmsCOMPL amdmsLoadAllStatistics(amdmsALGO_PROPERTIES_ENV *env)
{
  amdmsFITS   *infile = NULL;
  int         iInput;
  amdmsPIXEL   meanMean;
  amdmsPIXEL   meanVar;
  amdmsPIXEL   varMean;
  amdmsPIXEL   varVar;

  env->nInputs = env->env.inFiles.nNames;
  env->exptimes = (double*)calloc((size_t)(env->env.inFiles.nNames), sizeof(double));
  if (env->exptimes == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (exptimes)!");
    return amdmsFAILURE;
  }
  for (iInput = 0; iInput < env->env.inFiles.nNames; iInput++) {
    env->exptimes[iInput] = 0.0;
  }
  env->meanPixels = (amdmsDATA*)calloc((size_t)(env->env.inFiles.nNames), sizeof(amdmsDATA));
  if (env->meanPixels == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (meanPixels)!");
    return amdmsFAILURE;
  }
  for (iInput = 0; iInput < env->env.inFiles.nNames; iInput++) {
    amdmsInitData(&(env->meanPixels[iInput]));
  }
  env->varPixels = (amdmsDATA*)calloc((size_t)(env->env.inFiles.nNames), sizeof(amdmsDATA));
  if (env->varPixels == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (varPixels)!");
    return amdmsFAILURE;
  }
  for (iInput = 0; iInput < env->env.inFiles.nNames; iInput++) {
    amdmsInitData(&(env->varPixels[iInput]));
  }
  for (iInput = 0; iInput < env->env.inFiles.nNames; iInput++) {
    if (amdmsOpenFitsFile(&infile, env->env.inFiles.names[iInput]) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
    if (amdmsOpenData(infile, env->env.inFiles.flags[iInput], 1) != amdmsSUCCESS) {
      amdmsCloseFitsFile(&infile);
      return amdmsFAILURE;
    }
    env->exptimes[iInput] = infile->exptime;
    if (amdmsReadData(infile, &(env->meanPixels[iInput]), amdmsPIXEL_STAT_MEAN_ROW, 0) != amdmsSUCCESS) {
      amdmsError(__FILE__, __LINE__,
		"could not load the mean flux data, file=%s",
		env->env.inFiles.names[iInput]);
      amdmsCloseFitsFile(&infile);
      return amdmsFAILURE;
    }
    if (amdmsReadData(infile, &(env->varPixels[iInput]), amdmsPIXEL_STAT_VAR_ROW, 0) != amdmsSUCCESS) {
      amdmsError(__FILE__, __LINE__,
		"could not load the flux variance data, file=%s",
		env->env.inFiles.names[iInput]);
      amdmsCloseFitsFile(&infile);
      return amdmsFAILURE;
    }
    if (env->nuicFlag) {
      if (amdmsCleanUpFlatfieldSmooth(&(env->env.calib), &(env->env.stripes), &(env->meanPixels[iInput]), &(env->varPixels[iInput])) != amdmsSUCCESS) {
	amdmsCloseFitsFile(&infile);
	return amdmsFAILURE;
      }
    }
    if (env->env.filter.aoiFlag) {
      amdmsCalcStat(&(env->env.calib),
		   &(env->meanPixels[iInput]),
		   0,
		   env->env.filter.aoiX,
		   env->env.filter.aoiY,
		   env->env.filter.aoiWidth,
		   env->env.filter.aoiHeight,
		   &meanMean,
		   &meanVar);
      amdmsCalcStat(&(env->env.calib),
		   &(env->varPixels[iInput]),
		   0,
		   env->env.filter.aoiX,
		   env->env.filter.aoiY,
		   env->env.filter.aoiWidth,
		   env->env.filter.aoiHeight,
		   &varMean,
		   &varVar);
      amdmsInfo(__FILE__, __LINE__,
	       "AOI-STAT %8.4f  %12.2f  %12.2f",
	       env->exptimes[iInput],
	       meanMean,
	       varMean);
    }
    if (amdmsCloseFitsFile(&infile) != amdmsSUCCESS) {
      amdmsError(__FILE__, __LINE__, "could not close FITS file %s", env->env.inFiles.names[iInput]);
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcFlatfield(amdmsALGO_PROPERTIES_ENV *env)
{
  amdmsFIT_LINEAR_ENV       *fitRay = NULL;
  amdmsFIT_SMOOTH_DATA_ENV  *fitSmooth = NULL;
  double                   *x = NULL;
  double                   *y = NULL;
  double                   *ye = NULL;
  int                       i;
  int                       iInput, nInputs;
  int                       iPixel;
  int                       iHS;
  int                       iVS;
  int                       iX, iY;
  int                       sX, sY, sWidth, sHeight;
  amdmsPIXEL                 m, dummy;

  nInputs = env->env.inFiles.nNames;
  //nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  /* create all fit environments */
  if (amdmsCreateRayFit(&fitRay) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCreateSmoothDataFit(&fitSmooth, (amdmsFIT_ENV*)fitRay)  != amdmsSUCCESS) {
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsFLATFIELD_FIT_NROWS; i++) {
    amdmsAllocateData(&(env->ffFit[i]), env->meanPixels[0].nx, env->meanPixels[0].ny);
  }
  /* set fit parameters */
  fitSmooth->env.satFlag = env->satFlag;
  fitSmooth->env.satWidth = env->satWidth;
  fitSmooth->env.satHeight = env->satHeight;
  fitSmooth->env.nDelStart = env->nDelStart;
  fitSmooth->env.nDelMiddle = env->nDelMiddle;
  fitSmooth->env.nDelEnd = env->nDelEnd;
  fitSmooth->env.minStep = 0.2;
  /* allocate memory for the temporary data points */
  x = (double*)calloc((size_t)nInputs, sizeof(double));
  if (x == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (x)!");
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  y = (double*)calloc((size_t)nInputs, sizeof(double));
  if (y == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (y)!");
    free(x);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  ye = (double*)calloc((size_t)nInputs, sizeof(double));
  if (ye == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ye)!");
    free(x);
    free(y);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  /* initialize the flatfield */
  amdmsSetData(&(env->env.calib.ffmData), 1.0);
  /* set the x values equal to the exposure times in ms */
  for (iInput = 0; iInput < nInputs; iInput++) {
    x[iInput] = 1000.0*env->exptimes[iInput];
  }
  /* iterate over all stripes */
  for (iHS = 0; iHS < env->env.stripes.nHStripes; iHS++) {
    /* skip unused horizontal stripes */
    if ((env->env.stripes.hStripes[iHS].flags & amdmsUSE_FLATFIELD) == 0) {
      continue;
    }
    sY = env->env.stripes.hStripes[iHS].pos;
    sHeight = env->env.stripes.hStripes[iHS].size;
    for (iVS = 0; iVS < env->env.stripes.nVStripes; iVS++) {
      /* skip unused vertical stripes */
      if ((env->env.stripes.vStripes[iVS].flags & amdmsUSE_FLATFIELD) == 0) {
	continue;
      }
      sX = env->env.stripes.vStripes[iVS].pos;
      sWidth = env->env.stripes.vStripes[iVS].size;
      amdmsDebug(__FILE__, __LINE__,
		"   area (%d .. %d, %d .. %d)",
		sX, sX + sWidth - 1, sY, sY + sHeight - 1);
      for (iY = sY; iY < (sY + sHeight); iY++) {
	for (iX = sX; iX < (sX + sWidth); iX++) {
	  iPixel = env->meanPixels[0].nx*iY + iX;
	  if (env->env.calib.bpmData.data[iPixel] != amdmsGOOD_PIXEL) {
	    continue;
	  }
	  for (iInput = 0; iInput < nInputs; iInput++) {
	    y[iInput] = env->meanPixels[iInput].data[iPixel];
	    ye[iInput] = sqrt(env->varPixels[iInput].data[iPixel]);
	    if (ye[iInput] < fabs(0.1*y[iInput])) {
	      ye[iInput] = 1.0;
	    }
	  }
	  if (amdmsFitPixel(env, nInputs, x, y, ye, &(fitSmooth->env), iPixel, env->ffFit) != amdmsSUCCESS) {
	    free(x);
	    free(y);
	    free(ye);
	    amdmsDestroySmoothDataFit(&fitSmooth);
	    amdmsDestroyLinearFit(&fitRay);
	    return amdmsFAILURE;
	  }
	  amdmsCheckFitValues(env->ffFit, amdmsFLATFIELD_FIT_NROWS, iPixel);
	}
      }
      amdmsCalcStat(&(env->env.calib), &(env->ffFit[amdmsFIT_A0_ROW]), 0, sX, sY, sWidth, sHeight, &m, &dummy);
      for (iY = sY; iY < (sY + sHeight); iY++) {
	for (iX = sX; iX < (sX + sWidth); iX++) {
	  iPixel = env->meanPixels[0].nx*iY + iX;
	  if (env->env.calib.bpmData.data[iPixel] == amdmsGOOD_PIXEL) {
	    env->env.calib.ffmData.data[iPixel] = env->ffFit[amdmsFIT_A0_ROW].data[iPixel]/m;
	  }
	}
      }
    }
  }
  free(x);
  free(y);
  free(ye);
  amdmsDestroySmoothDataFit(&fitSmooth);
  amdmsDestroyLinearFit(&fitRay);
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcConversionFactorAmberR(amdmsALGO_PROPERTIES_ENV *env, int rX, int rY, int rW, int rH)
{
  int                        iInput, nInputs;
  int                        iPixel;
  amdmsFIT_LINEAR_ENV       *fitRay = NULL;
  amdmsFIT_SMOOTH_DATA_ENV  *fitSmooth = NULL;
  double                    *x = NULL;
  double                    *y = NULL;
  double                    *ye = NULL;
  int                        iX, iY;

  nInputs = env->env.inFiles.nNames;
  //nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  /* create all fit environments */
  if (amdmsCreateRayFit(&fitRay) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCreateSmoothDataFit(&fitSmooth, (amdmsFIT_ENV*)fitRay)  != amdmsSUCCESS) {
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  /* set fit parameters */
  fitSmooth->env.satFlag = env->satFlag;
  fitSmooth->env.satWidth = env->satWidth;
  fitSmooth->env.satHeight = env->satHeight;
  fitSmooth->env.nDelStart = env->nDelStart;
  fitSmooth->env.nDelMiddle = env->nDelMiddle;
  fitSmooth->env.nDelEnd = env->nDelEnd;
  fitSmooth->env.minStep = 0.2;
  /* allocate memory for the temporary data points */
  x = (double*)calloc((size_t)nInputs, sizeof(double));
  if (x == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (x)!");
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  y = (double*)calloc((size_t)nInputs, sizeof(double));
  if (y == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (y)!");
    free(x);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }
  ye = (double*)calloc((size_t)nInputs, sizeof(double));
  if (ye == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ye)!");
    free(x);
    free(y);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitRay);
    return amdmsFAILURE;
  }

  for (iY = rY; iY < (rY + rH); iY++) {
    for (iX = rX; iX < (rX + rW); iX++) {
      iPixel = env->meanPixels[0].nx*iY + iX;
      if (env->env.calib.bpmData.data[iPixel] != amdmsGOOD_PIXEL) {
	continue;
      }
      for (iInput = 0; iInput < nInputs; iInput++) {
	x[iInput] = env->meanPixels[iInput].data[iPixel];
	y[iInput] = env->varPixels[iInput].data[iPixel];
	ye[iInput] = 1.0;
      }
      if (amdmsFitPixel(env, nInputs, x, y, ye, &(fitSmooth->env), iPixel, env->cfFit) != amdmsSUCCESS) {
	return amdmsFAILURE;
      }
      amdmsCheckFitValues(env->cfFit, amdmsCONVERSION_FACTOR_FIT_NROWS, iPixel);
      if (env->cfFit[amdmsFIT_A0_ROW].data[iPixel] == 0.0) {
	env->cfmData.data[iPixel] = 1.0;
      } else {
	env->cfmData.data[iPixel] = 1.0/env->cfFit[amdmsFIT_A0_ROW].data[iPixel];
      }
      if (env->env.filter.poiFlag && (iPixel == (env->env.filter.poiX + env->env.filter.poiY*env->meanPixels[0].ny))) {
	amdmsInfo(__FILE__, __LINE__, "#conversion factor = %12.4f uV/e-",
		  env->kADC/env->cfmData.data[iPixel]);
	amdmsInfo(__FILE__, __LINE__, "#conversion factor = %12.4f e-/DU",
		  env->cfmData.data[iPixel]);
      }
    }
  }
  free(ye);
  free(y);
  free(x);
  amdmsDestroySmoothDataFit(&fitSmooth);
  amdmsDestroyLinearFit(&fitRay);
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcConversionFactorGlobalLineR(amdmsALGO_PROPERTIES_ENV *env, int rX, int rY, int rW, int rH)
{
  int          iInput, nInputs;
  int          iPixel;
  int          iX, iY;
  int          n    = 0;
  double       x, y;
  double       sx   = 0.0;
  double       sxx  = 0.0;
  double       sy   = 0.0;
  double       syy  = 0.0;
  double       sxy  = 0.0;
  double       ssxx = 0.0;
  //double       ssyy = 0.0;
  double       ssxy = 0.0;
  double       xq   = 0.0;
  double       yq   = 0.0;
  double       offset, slope;
  amdmsPIXEL   cf;

  nInputs = env->env.inFiles.nNames;
  //nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  /* calculate the conversion factor but use only the central part of the region */
  for (iY = 0; iY < rH/2; iY++) {
    iPixel = env->meanPixels[0].nx*(rY + rH/4 + iY) + rX + rW/4;
    for (iX = 0; iX < rW/2; iX++) {
      if (env->env.calib.bpmData.data[iPixel] == amdmsGOOD_PIXEL) {
	for (iInput = 0; iInput < nInputs; iInput++) {
	  x = env->meanPixels[iInput].data[iPixel];
	  y = env->varPixels[iInput].data[iPixel];
	  n++;
	  sx  += x;
	  sxx += x*x;
	  sy  += y;
	  syy += y*y;
	  sxy += x*y;
	}
      }
      iPixel++;
    }
  }
  xq = sx/(double)n;
  yq = sy/(double)n;
  ssxx = sxx - (double)n*xq*xq;
  //ssyy = syy - (double)n*yq*yq;
  ssxy = sxy - (double)n*xq*yq;
  if (ssxx == 0.0) {
    amdmsWarning(__FILE__, __LINE__, "ssxx == 0.0");
    return amdmsFAILURE;
  }
  slope = ssxy/ssxx;
  offset = yq - slope*xq;
  cf = 1.0/slope;
  amdmsSetDataR(&(env->cfmData), rX, rY, rW, rH, cf);
  amdmsInfo(__FILE__, __LINE__, "CF = %.4f + %.4f*I", offset, slope);
  amdmsInfo(__FILE__, __LINE__, "GAIN = %12.4f uV/e-", env->kADC/cf);
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcConversionFactorCentralLineR(amdmsALGO_PROPERTIES_ENV *env, int rX, int rY, int rW, int rH)
{
  amdmsFIT_LINEAR_ENV       *fitCF = NULL;
  amdmsFIT_SMOOTH_DATA_ENV  *fitSmooth = NULL;
  int                        nPoints;
  double                    *x = NULL;
  double                    *y = NULL;
  double                    *ye = NULL;
  int                        iInput, nInputs;
  int                        iPixel;
  int                        iHS, iVS;
  int                        sX, sY, sWidth, sHeight;
  int                        iX, iY;
  amdmsPIXEL                 m, v;
  double                     cfSum = 0.0;
  double                     cfCount = 0.0;
  double                     cfHSum = 0.0;
  double                     cfHCount = 0.0;
  amdmsPIXEL                 cf = 1.0;

  nInputs = env->env.inFiles.nNames;
  //nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  /* create all fit environments */
  if (amdmsCreatePolynomialFit(&fitCF, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCreateSmoothDataFit(&fitSmooth, (amdmsFIT_ENV*)fitCF)  != amdmsSUCCESS) {
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
  /* set fit parameters */
  fitSmooth->env.satFlag = env->satFlag;
  fitSmooth->env.satWidth = env->satWidth;
  fitSmooth->env.satHeight = env->satHeight;
  fitSmooth->env.nDelStart = env->nDelStart;
  fitSmooth->env.nDelMiddle = env->nDelMiddle;
  fitSmooth->env.nDelEnd = env->nDelEnd;
  fitSmooth->env.minStep = 0.2;
  /* allocate memory for the temporary data points */
  x = (double*)calloc((size_t)(nInputs*env->env.stripes.nHStripes*env->env.stripes.nVStripes), sizeof(double));
  if (x == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (x)!");
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
  y = (double*)calloc((size_t)(nInputs*env->env.stripes.nHStripes*env->env.stripes.nVStripes), sizeof(double));
  if (y == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (y)!");
    free(x);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
  ye = (double*)calloc((size_t)(nInputs*env->env.stripes.nHStripes*env->env.stripes.nVStripes), sizeof(double));
  if (ye == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ye)!");
    free(x);
    free(y);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
  nPoints = 0;
  /* First iterate over all input data */
  for (iInput = 0; iInput < nInputs; iInput++) {
    /* amdmsInfo(__FILE__, __LINE__, "input[%d]: exptime = %.3f", iInput, env->exptimes[iInput]); */
    /* iterate over all stripes */
    for (iHS = 0; iHS < env->env.stripes.nHStripes; iHS++) {
      /* skip unused horizontal stripes */
      if ((env->env.stripes.hStripes[iHS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
	continue;
      }
      if (env->env.stripes.hStripes[iHS].pos >= rY + rH) {
	continue;
      }
      if (env->env.stripes.hStripes[iHS].pos + env->env.stripes.hStripes[iHS].size <= rY) {
	continue;
      }
      /* clip the horizontal stripe by the given region and use only the central part */
      sY = MAX(env->env.stripes.hStripes[iHS].pos, rY);
      sHeight = MIN(env->env.stripes.hStripes[iHS].pos + env->env.stripes.hStripes[iHS].size, rY + rH) - sY;
      sHeight /= 2;
      sY += sHeight/2;
      for (iVS = 0; iVS < env->env.stripes.nVStripes; iVS++) {
	/* skip unused vertical stripes */
	if ((env->env.stripes.vStripes[iVS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
	  continue;
	}
	if (env->env.stripes.vStripes[iVS].pos >= rX + rW) {
	  continue;
	}
	if (env->env.stripes.vStripes[iVS].pos + env->env.stripes.vStripes[iVS].size <= rX) {
	  continue;
	}
	/* clip the vertical stripe by the given region and use only the central part */
	sX = MAX(env->env.stripes.vStripes[iVS].pos, rX);
	sWidth = MIN(env->env.stripes.vStripes[iVS].pos + env->env.stripes.vStripes[iVS].size, rX + rW) - sX;
	sWidth /= 2;
	sX += sWidth/2;
	amdmsDebug(__FILE__, __LINE__,
		   "   area (%d .. %d, %d .. %d)",
		   sX, sX + sWidth - 1, sY, sY + sHeight - 1);
	cfHSum = 0.0;
	cfHCount = 0.0;
	for (iY = sY; iY < (sY + sHeight); iY++) {
	  for (iX = sX; iX < (sX + sWidth); iX++) {
	    iPixel = env->meanPixels[0].nx*iY + iX;
	    /* check if the pixel is a good pixel */
	    if (env->env.calib.bpmData.data[iPixel] != amdmsGOOD_PIXEL) {
	      continue;
	    }
	    m = env->meanPixels[iInput].data[iPixel];
	    v = env->varPixels[iInput].data[iPixel];
	    if (v == 0.0) {
	      continue;
	    }
	    cfHSum += m/v;
	    cfHCount += 1.0;
	    cfSum += m/v;
	    cfCount += 1.0;
	  }
	}
	if (cfHCount != 0.0) {
	  cf = cfHSum/cfHCount;
	  amdmsCalcStat(&(env->env.calib), &(env->meanPixels[iInput]), 0, sX, sY, sWidth, sHeight, &m, NULL);
	  amdmsCalcStat(&(env->env.calib), &(env->varPixels[iInput]), 0, sX, sY, sWidth, sHeight, &v, NULL);
	  amdmsInfo(__FILE__, __LINE__,
		    "input[%d]: exptime = %.3f,  %.2f %.2f %.3f",
		    iInput, env->exptimes[iInput], m, v, cf);
	  x[nPoints] = v;
	  y[nPoints] = m;
	  ye[nPoints] = m/10.0;
	  nPoints++;
	}
      }
    }
  }
  if (amdmsDoFit(&(fitSmooth->env.env), nPoints, x, y, ye) != amdmsSUCCESS) {
    free(x);
    free(y);
    free(ye);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
  if (amdmsCalcFitLimits(&(fitSmooth->env.env), nPoints, x, y, ye, 0.01) != amdmsSUCCESS) {
    free(x);
    free(y);
    free(ye);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }

  if (cfCount != 0.0) {
    amdmsSetDataR(&env->cfmData, rX, rY, rW, rH, cf);
    amdmsInfo(__FILE__, __LINE__, "NDU = %.4g + %.4g*VDU [%d, %d, %d, %d]",
	      fitSmooth->env.fit->a[0],
	      fitSmooth->env.fit->a[1],
	      rX, rY, rW, rH);
    amdmsInfo(__FILE__, __LINE__, "GAIN = %12.4f uV/e-", env->kADC/cf);
    free(x);
    free(y);
    free(ye);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsSUCCESS;
  } else {
    free(x);
    free(y);
    free(ye);
    amdmsDestroySmoothDataFit(&fitSmooth);
    amdmsDestroyLinearFit(&fitCF);
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsCalcConversionFactorLocalPairs(amdmsALGO_PROPERTIES_ENV *env)
{
  int          iInput, nInputs;
  int          iPixel;
  int          iHS, iVS;
  int          sX, sY, sWidth, sHeight;
  int          iX, iY;
  amdmsPIXEL   m;
  amdmsPIXEL   m1, v1;
  amdmsPIXEL   m2, v2;
  int          iVSMin = -1;
  int          iVSMax = -1;
  amdmsPIXEL   mVSMin = 0.0;
  amdmsPIXEL   mVSMax = 0.0;
  double       cfSum = 0.0;
  double       cfCount = 0.0;
  amdmsPIXEL   cf;
  int          nVS = 0;
  int          vsWidth;
  int          vsDelta;
  int          vsX[amdmsMAX_STRIPES];
  int          vsW[amdmsMAX_STRIPES];

  nInputs = env->env.inFiles.nNames;
  //nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  if (amdmsAllocateData(&(env->cfmData), env->meanPixels[0].nx, env->meanPixels[0].ny) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  /* calculate the usable vertical stripes including a common width and appropriate x position */
  nVS = 0;
  vsWidth = 10000;
  for (iVS = 0; iVS < env->env.stripes.nVStripes; iVS++) {
    /* skip unused vertical stripes */
    if ((env->env.stripes.vStripes[iVS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
      continue;
    }
    vsX[nVS] = env->env.stripes.vStripes[iVS].pos;
    vsW[nVS] = env->env.stripes.vStripes[iVS].size;
    if (vsW[nVS] < vsWidth) {
      vsWidth = vsW[nVS];
    }
    nVS++;
  }
  /* correct the x position of the vertical stripes for a common width */
  vsWidth = 40;
  for (iVS = 0; iVS < nVS; iVS++) {
    vsX[iVS] += (vsW[iVS] - vsWidth)/2;
    vsW[iVS] = vsWidth;
  }
  /* and now calculate the conversion factor for all pixels */
  /* First iterate over all input data */
  for (iInput = 0; iInput < nInputs; iInput++) {
    /* iterate over all stripes */
    for (iHS = 0; iHS < env->env.stripes.nHStripes; iHS++) {
      /* skip unused horizontal stripes */
      if ((env->env.stripes.hStripes[iHS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
	continue;
      }
      /*
	sY = env->env.stripes.hStripes[iHS].pos;
	sHeight = env->env.stripes.hStripes[iHS].size;
      */
      sY = env->env.stripes.hStripes[iHS].pos + env->env.stripes.hStripes[iHS].size/4;
      sHeight = env->env.stripes.hStripes[iHS].size/2;
      /* calulate the vertical stripe with the highest and lowest mean value */
      for (iVS = 0; iVS < nVS; iVS++) {
	sX = vsX[iVS];
	sWidth = vsW[iVS];
	amdmsCalcStat(&(env->env.calib), &(env->meanPixels[iInput]), 0, sX, sY, sWidth, sHeight, &m, NULL);
	if (iVS == 0) {
	  iVSMin = iVS;
	  mVSMin = m;
	  iVSMax = iVS;
	  mVSMax = m;
	  continue;
	}
	if (m < mVSMin) {
	  iVSMin = iVS;
	  mVSMin = m;
	}
	if (m > mVSMax) {
	  iVSMax = iVS;
	  mVSMax = m;
	}
      }
      if (iVSMin == iVSMax) {
	continue;
      }
      vsDelta = vsX[iVSMax] - vsX[iVSMin];
      amdmsCalcStat(&(env->env.calib), &(env->meanPixels[iInput]), 0,
		    vsX[iVSMin], sY, vsW[iVSMin], sHeight,
		    &mVSMin, NULL);
      amdmsCalcStat(&(env->env.calib), &(env->meanPixels[iInput]), 0,
		    vsX[iVSMax], sY, vsW[iVSMax], sHeight,
		    &mVSMax, NULL);
      amdmsInfo(__FILE__, __LINE__, "  selected stripes: vsMin[%d] = %d (%.2f); vsMax[%d] = %d (%.2f); vsWidth = %d, vsDelta = %d",
		iVSMin, vsX[iVSMin], mVSMin,
		iVSMax, vsX[iVSMax], mVSMax,
		vsWidth, vsDelta);
      for (iY = sY; iY < (sY + sHeight); iY++) {
	for (iX = 0; iX < vsWidth; iX++) {
	  iPixel = env->meanPixels[0].nx*iY + iX + vsX[iVSMin];
	  /* check if both selected pixels are good pixels */
	  if (env->env.calib.bpmData.data[iPixel] != amdmsGOOD_PIXEL) {
	    continue;
	  }
	  if (env->env.calib.bpmData.data[iPixel + vsDelta] != amdmsGOOD_PIXEL) {
	    continue;
	  }
	  m1 = env->meanPixels[iInput].data[iPixel];
	  v1 = env->varPixels[iInput].data[iPixel];
	  m2 = env->meanPixels[iInput].data[iPixel + vsDelta];
	  v2 = env->varPixels[iInput].data[iPixel + vsDelta];
	  if ((ABS(m1 - m2) < 200.0) || (ABS(v1 - v2) < 200.0)) {
	    continue; /* difference not large enough */
	  }
	  if (m1 > m2) {
	    cfSum += (m1 - m2)/(v1 - v2);
	  } else {
	    cfSum += (m2 - m1)/(v2 - v1);
	  }
	  cfCount += 1.0;
	}
      }
    }
  }
  if (cfCount != 0.0) {
    cf = cfSum/cfCount;
    amdmsSetData(&(env->cfmData), cf);
    amdmsInfo(__FILE__, __LINE__, "CF   = %.2f/%.2f", cfSum, cfCount);
    amdmsInfo(__FILE__, __LINE__, "GAIN = %12.4f uV/e-", env->kADC/cf);
    return amdmsSUCCESS;
  } else {
    return amdmsFAILURE;
  }
}

amdmsCOMPL amdmsCalcConversionFactor(amdmsALGO_PROPERTIES_ENV *env)
{
  //int             nInputs;
  int             i;
  int             iHS, iVS;
  int             nx, ny;
  int             sX, sY, sWidth, sHeight;
  amdmsPIXEL      cf;
  amdmsCOMPL      retVal = amdmsSUCCESS;

  //nInputs = env->env.inFiles.nNames;
  nx = env->meanPixels[0].nx;
  ny = env->meanPixels[0].ny;
  for (i = 0; i < amdmsCONVERSION_FACTOR_FIT_NROWS; i++) {
    amdmsAllocateData(&(env->cfFit[i]), nx, ny);
  }
  amdmsAllocateData(&(env->cfmData), nx, ny);
  /* and now calculate the conversion factor for all pixels */
  if (env->cfAlgo == amdmsCFA_LOCAL_PAIRS) {
    retVal = amdmsCalcConversionFactorLocalPairs(env);
  } else if (env->cfAlgo == amdmsCFA_CENTRAL_LINE) {
    if (env->env.q4Flag) {
      retVal =           amdmsCalcConversionFactorCentralLineR(env,    0,   0,  nx/2, ny/2);
      retVal = retVal && amdmsCalcConversionFactorCentralLineR(env, nx/2,   0,  nx/2, ny/2);
      retVal = retVal && amdmsCalcConversionFactorCentralLineR(env,    0, ny/2, nx/2, ny/2);
      retVal = retVal && amdmsCalcConversionFactorCentralLineR(env, nx/2, ny/2, nx/2, ny/2);
    } else {
      retVal = amdmsCalcConversionFactorCentralLineR(env, 0, 0, nx, ny);
    }
  }
  /* iterate over all stripes */
  for (iHS = 0; iHS < env->env.stripes.nHStripes; iHS++) {
    /* skip unused horizontal stripes */
    if ((env->env.stripes.hStripes[iHS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
      continue;
    }
    sY = env->env.stripes.hStripes[iHS].pos;
    sHeight = env->env.stripes.hStripes[iHS].size;
    for (iVS = 0; iVS < env->env.stripes.nVStripes; iVS++) {
      /* skip unused vertical stripes */
      if ((env->env.stripes.vStripes[iVS].flags & amdmsUSE_CONVERSION_FACTOR) == 0) {
	continue;
      }
      sX = env->env.stripes.vStripes[iVS].pos;
      sWidth = env->env.stripes.vStripes[iVS].size;
      switch (env->cfAlgo) {
      case amdmsCFA_AMBER:
	retVal = amdmsCalcConversionFactorAmberR(env, sX, sY, sWidth, sHeight);
	break;
      case amdmsCFA_GLOBAL_LINE:
	retVal = amdmsCalcConversionFactorGlobalLineR(env, sX, sY, sWidth, sHeight);
	break;
      case amdmsCFA_CENTRAL_LINE:
	break;
      case amdmsCFA_LOCAL_PAIRS:
	break;
      default:
	return amdmsFAILURE;
      }
      if (retVal != amdmsSUCCESS) return amdmsFAILURE;
      amdmsCalcStat(&(env->env.calib),
		    &(env->cfmData),
		    0, sX + sWidth/4, sY + sHeight/4, sWidth/2, sHeight/2,
		    &cf, NULL);
      amdmsInfo(__FILE__, __LINE__, "GAIN = %12.4f e-/DU [%4d, %4d, %4d, %4d]",
		cf, sX + sWidth/4, sY + sHeight/4, sWidth/2, sHeight/2);
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcNonlinearity(amdmsALGO_PROPERTIES_ENV *env)
{
  return amdmsFAILURE;
}

amdmsCOMPL amdmsCalcPhotonTransferCurve(amdmsALGO_PROPERTIES_ENV *env)
{
  return amdmsFAILURE;
}

amdmsCOMPL amdmsCalcDarkCurrent(amdmsALGO_PROPERTIES_ENV *env)
{
  amdmsFIT_ENV              *fitLine = NULL;
  /*amdmsFIT_SMOOTH_DATA_ENV  *fitSmooth = NULL; */
  amdmsFIT_DATA_ENV  *fitSmooth = NULL;
  double                   *x = NULL;
  double                   *y = NULL;
  double                   *ye = NULL;
  int                       i;
  int                       iInput, nInputs;
  int                       iPixel, nPixels;

  nInputs = env->env.inFiles.nNames;
  nPixels = env->meanPixels[0].nx*env->meanPixels[0].ny;
  /* create all fit environments */
  if (amdmsCreateStraightLineFit(&fitLine) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  /*if (amdmsCreateSmoothDataFit(&fitSmooth, fitLine)  != amdmsSUCCESS) { */
  if (amdmsCreateDataFit(&fitSmooth, fitLine)  != amdmsSUCCESS) {
    amdmsDestroyFit(&fitLine);
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsDARK_CURRENT_NROWS; i++) {
    amdmsAllocateData(&(env->dcFit[i]), env->meanPixels[0].nx, env->meanPixels[0].ny);
  }
  /* initialize the flatfield */
  amdmsSetData(&(env->dcFit[amdmsFIT_DATA_BIAS_ROW]),       0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_DATA_SATURATION_ROW]), 0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_CHI_SQR_ROW]),         0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_ABS_DIST_SQR_ROW]),    0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_REL_DIST_SQR_ROW]),    0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_LOWER_LIMIT_ROW]),     0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_UPPER_LIMIT_ROW]),     0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_A0_ROW]),              0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_A0_STDEV_ROW]),        0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_A1_ROW]),              0.0);
  amdmsSetData(&(env->dcFit[amdmsFIT_A1_STDEV_ROW]),        0.0);
  /* set fit parameters */
  /*
  fitSmooth->env.satFlag = env->satFlag;
  fitSmooth->env.satWidth = env->satWidth;
  fitSmooth->env.satHeight = env->satHeight;
  fitSmooth->env.nDelStart = env->nDelStart;
  fitSmooth->env.nDelMiddle = env->nDelMiddle;
  fitSmooth->env.nDelEnd = env->nDelEnd;
  fitSmooth->env.minStep = 0.2;
  */
  fitSmooth->satFlag = env->satFlag;
  fitSmooth->satWidth = env->satWidth;
  fitSmooth->satHeight = env->satHeight;
  fitSmooth->nDelStart = env->nDelStart;
  fitSmooth->nDelMiddle = env->nDelMiddle;
  fitSmooth->nDelEnd = env->nDelEnd;
  fitSmooth->minStep = 0.2;
  /* allocate memory for the temporary data points */
  x = (double*)calloc((size_t)nInputs, sizeof(double));
  if (x == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (x)!");
    amdmsDestroyDataFit(&fitSmooth);
    amdmsDestroyFit(&fitLine);
    return amdmsFAILURE;
  }
  y = (double*)calloc((size_t)nInputs, sizeof(double));
  if (y == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (y)!");
    free(x);
    amdmsDestroyDataFit(&fitSmooth);
    amdmsDestroyFit(&fitLine);
    return amdmsFAILURE;
  }
  ye = (double*)calloc((size_t)nInputs, sizeof(double));
  if (ye == NULL) {
    amdmsFatal(__FILE__, __LINE__, "memory allocation failure (ye)!");
    free(x);
    free(y);
    amdmsDestroyDataFit(&fitSmooth);
    amdmsDestroyFit(&fitLine);
    return amdmsFAILURE;
  }
  /* set the x values equal to the exposure times in ms */
  for (iInput = 0; iInput < nInputs; iInput++) {
    x[iInput] = env->exptimes[iInput];
  }
  for (iPixel = 0; iPixel < nPixels; iPixel++) {
    if (env->env.calib.bpmData.data[iPixel] != amdmsGOOD_PIXEL) {
      continue;
    }
    for (iInput = 0; iInput < nInputs; iInput++) {
      y[iInput] = env->meanPixels[iInput].data[iPixel];
      ye[iInput] = sqrt(env->varPixels[iInput].data[iPixel]);
      if (ye[iInput] < fabs(0.1*y[iInput])) {
	ye[iInput] = 1.0;
      }
    }
    /*if (amdmsFitPixel(env, nInputs, x, y, ye, &(fitSmooth->env), iPixel, env->dcFit) != amdmsSUCCESS) */
    if (amdmsFitPixel(env, nInputs, x, y, ye, fitSmooth, iPixel, env->dcFit) != amdmsSUCCESS) {
      free(x);
      free(y);
      free(ye);
      amdmsDestroyDataFit(&fitSmooth);
      amdmsDestroyFit(&fitLine);
      return amdmsFAILURE;
    }
    amdmsCheckFitValues(env->dcFit, amdmsDARK_CURRENT_NROWS, iPixel);
  }
  free(x);
  free(y);
  free(ye);
  amdmsDestroyDataFit(&fitSmooth);
  amdmsDestroyFit(&fitLine);
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCalcHistogramForArea(amdmsALGO_PROPERTIES_ENV *env, int aoiX, int aoiY, int aoiWidth, int aoiHeight, int *inCount, int *outCount)
{
  int          nx, ny, nz;
  int          iPixel;
  int          i, j, k;
  double       x, y;
  int          ix, iy;

  nz = env->env.inFiles.nNames;
  nx = env->meanPixels[0].nx;
  ny = env->meanPixels[0].ny;
  //nPixels = nx*ny;
  for (j = 0; j < aoiHeight; j++) {
    iPixel = (j + aoiY)*nx + aoiX;
    for (i = 0; i < aoiWidth; i++) {
      for (k = 0; k < nz; k++) {
	switch (env->hX.content) {
	case amdmsHC_EXPTIME:
	  x = env->exptimes[k];
	  break;
	case amdmsHC_MEAN:
	  x = env->meanPixels[k].data[iPixel];
	  break;
	case amdmsHC_VARIANCE:
	  x = env->varPixels[k].data[iPixel];
	  break;
	case amdmsHC_NOISE:
	  x = sqrt(env->varPixels[k].data[iPixel]);
	  break;
	default:
	  return amdmsFAILURE;
	}
	if ((env->hX.min >= 0.0) && (x < 0.0)) x = 0.0;
	switch (env->hX.scale) {
	case amdmsHS_LINEAR:
	  break;
	case amdmsHS_SQRT:
	  x = sqrt(fabs(x));
	  break;
	case amdmsHS_LOG:
	  if (x > 0.0)
	    x = log(x);
	  else
	    x = 0.0;
	  break;
	default:
	  return amdmsFAILURE;
	}
	ix = (int)((x - env->hX.min)/(env->hX.max - env->hX.min)*(double)(env->hX.steps));
	if (ix < 0) {
	  (*outCount)++;
	  continue;
	}
	if (ix >= env->hX.steps) {
	  (*outCount)++;
	  continue;
	}
	switch (env->hY.content) {
	case amdmsHC_EXPTIME:
	  y = env->exptimes[k];
	  break;
	case amdmsHC_MEAN:
	  y = env->meanPixels[k].data[iPixel];
	  break;
	case amdmsHC_VARIANCE:
	  y = env->varPixels[k].data[iPixel];
	  break;
	case amdmsHC_NOISE:
	  y = sqrt(env->varPixels[k].data[iPixel]);
	  break;
	default:
	  return amdmsFAILURE;
	}
	if ((env->hY.min >= 0.0) && (y < 0.0)) y = 0.0;
	switch (env->hY.scale) {
	case amdmsHS_LINEAR:
	  break;
	case amdmsHS_SQRT:
	  y = sqrt(fabs(x));
	  break;
	case amdmsHS_LOG:
	  if (y > 0.0)
	    y = log(y);
	  else
	    y = 0.0;
	  break;
	default:
	  return amdmsFAILURE;
	}
	iy = (int)((y - env->hY.min)/(env->hY.max - env->hY.min)*(double)(env->hY.steps));
	if (iy < 0) {
	  (*outCount)++;
	  continue;
	}
	if (iy >= env->hY.steps) {
	  (*outCount)++;
	  continue;
	}
	(*inCount)++;
	env->histoData.data[iy*env->hX.steps + ix] += 1.0;
      }
      iPixel++;
    }
  }
  return amdmsSUCCESS;
}


amdmsCOMPL amdmsCalcHistogram(amdmsALGO_PROPERTIES_ENV *env)
{
  amdmsCOMPL   retValue = amdmsSUCCESS;
  int          inCount = 0;
  int          outCount = 0;
  int          iHS, iVS;

  amdmsAllocateData(&(env->histoData), env->hX.steps, env->hY.steps);
  amdmsSetData(&(env->histoData), 0.0);
  /* iterate over all pixels inside the area of interest */
  if (env->env.stripes.nHStripes == 0) {
    retValue = amdmsCalcHistogramForArea(env,
					 env->env.filter.aoiX,
					 env->env.filter.aoiY,
					 env->env.filter.aoiWidth,
					 env->env.filter.aoiHeight,
					 &inCount,
					 &outCount);
  } else {
    for (iHS = 0; iHS < env->env.stripes.nHStripes; iHS++) {
      /* skip unused horizontal stripes */
      if (env->env.stripes.hStripes[iHS].flags == 0) continue;
      for (iVS = 0; iVS < env->env.stripes.nVStripes; iVS++) {
	/* skip unused vertical stripes */
	if (env->env.stripes.vStripes[iVS].flags == 0) continue;
	retValue = retValue && amdmsCalcHistogramForArea(env,
							 env->env.stripes.vStripes[iVS].pos,
							 env->env.stripes.hStripes[iHS].pos,
							 env->env.stripes.vStripes[iVS].size,
							 env->env.stripes.hStripes[iHS].size,
							 &inCount,
							 &outCount);
      }
    }
  }
  amdmsInfo(__FILE__, __LINE__, "%d values outside, %d values inside histogram", outCount, inCount);
  return retValue;
}

amdmsCOMPL amdmsFitPixel(amdmsALGO_PROPERTIES_ENV *env,
		       int nPoints, double *x, double *y, double *ye,
		       amdmsFIT_DATA_ENV *fit,
		       int iPixel,
		       amdmsDATA *fitValues)
{
  amdmsBOOL  infoFlag = amdmsFALSE;
  int       nCoeffs;
  int       iCoeff;
  int       iPoint;

  infoFlag = (env->env.filter.poiFlag && (iPixel == (env->env.filter.poiX + env->env.filter.poiY*env->meanPixels[0].nx)));
  nCoeffs = fit->env.nCoefficients;
  if (infoFlag) {
    amdmsInfo(__FILE__, __LINE__, "#********************************");
    amdmsInfo(__FILE__, __LINE__, "# model fitting for pixel %d, %d (%d)",
	     iPixel%env->meanPixels[0].nx, iPixel/env->meanPixels[0].nx, iPixel);
    amdmsInfo(__FILE__, __LINE__, "#--------------------------------");
    amdmsInfo(__FILE__, __LINE__, "#  data points (x, y):");
    for (iPoint = 0; iPoint < nPoints; iPoint++) {
      amdmsInfo(__FILE__, __LINE__, "    %12.4f   %12.4f  %12.4f", x[iPoint], y[iPoint], ye[iPoint]);
    }
  }
/*   cpl_msg_info(cpl_func,"nPoints:%d, x:%g, y:%g, ye:%g",nPoints, x, y, ye); */
  if (amdmsDoFit(&(fit->env), nPoints, x, y, ye) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCalcFitLimits(&(fit->env), nPoints, x, y, ye, 0.01) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  fitValues[amdmsFIT_DATA_BIAS_ROW].data[iPixel] = amdmsEvalFit(&(fit->env), x[0]);
  if(fit->satDP>=0){
  fitValues[amdmsFIT_DATA_SATURATION_ROW].data[iPixel] = amdmsEvalFit(&(fit->env), x[fit->satDP]);
  }
  fitValues[amdmsFIT_CHI_SQR_ROW].data[iPixel] = fit->env.chi2;
  fitValues[amdmsFIT_ABS_DIST_SQR_ROW].data[iPixel] = fit->env.absDist2;
  fitValues[amdmsFIT_REL_DIST_SQR_ROW].data[iPixel] = fit->env.relDist2;
  fitValues[amdmsFIT_LOWER_LIMIT_ROW].data[iPixel] = fit->env.fitLowerLimit;
  fitValues[amdmsFIT_UPPER_LIMIT_ROW].data[iPixel] = fit->env.fitUpperLimit;
  for (iCoeff = 0; iCoeff < nCoeffs; iCoeff++) {
    fitValues[amdmsFIT_A0_ROW + 2*iCoeff].data[iPixel]       = fit->fit->a[iCoeff];
    fitValues[amdmsFIT_A0_STDEV_ROW + 2*iCoeff].data[iPixel] = fit->fit->ae[iCoeff];
  }
  if (infoFlag) {
    amdmsInfo(__FILE__, __LINE__, "#--------------------------------");
    amdmsInfo(__FILE__, __LINE__, "#  model with %d coefficients:", nCoeffs);
    amdmsInfo(__FILE__, __LINE__, "#    chi^2            = %12.4f",
	     fitValues[amdmsFIT_CHI_SQR_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    absDist^2        = %12.8f",
	     fitValues[amdmsFIT_ABS_DIST_SQR_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    relDist^2        = %12.8f",
	     fitValues[amdmsFIT_REL_DIST_SQR_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    data bias        = %12.4f",
	     fitValues[amdmsFIT_DATA_BIAS_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    data saturation  = %12.4f",
	     fitValues[amdmsFIT_DATA_SATURATION_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    fit lower limit  = %12.4f",
	     fitValues[amdmsFIT_LOWER_LIMIT_ROW].data[iPixel]);
    amdmsInfo(__FILE__, __LINE__, "#    fit upper limit  = %12.4f",
	     fitValues[amdmsFIT_UPPER_LIMIT_ROW].data[iPixel]);
    for (iCoeff = 0; iCoeff < nCoeffs; iCoeff++) {
      amdmsInfo(__FILE__, __LINE__, "#    a%d = %.4e, stdev a%d = %.4e",
	       iCoeff, fitValues[amdmsFIT_A0_ROW + 2*iCoeff].data[iPixel],
	       iCoeff, fitValues[amdmsFIT_A0_STDEV_ROW + 2*iCoeff].data[iPixel]);
    }
  }
  return amdmsSUCCESS;
}

void amdmsCheckFitValues(amdmsDATA *fitValues, int nValues, int iPixel)
{
  int    iValue;

  for (iValue = 0; iValue < nValues; iValue++) {
    if (isnan(fitValues[iValue].data[iPixel])) {
      amdmsWarning(__FILE__, __LINE__, "fit value %d of pixel %d is NAN", iValue, iPixel);
    }
    if (isinf(fitValues[iValue].data[iPixel])) {
      amdmsWarning(__FILE__, __LINE__, "fit value %d of pixel %d is INF", iValue, iPixel);
    }
  }
}

amdmsCOMPL amdmsCreateFlatfieldMap(amdmsALGO_PROPERTIES_ENV *env,
				 amdmsFITS *outfile,
				 amdmsFITS_FLAGS flags)
{
  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR CATG", "CALIB", "Observation category")  != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR TECH", "INTERFEROMETRY", "Observation technique") != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR TYPE", "FLATFIELD", "Observation type") != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCreateData(outfile, flags, amdmsFLATFIELD_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->env.calib.ffmData.index = 1.0;
  if (amdmsWriteData(outfile, &(env->env.calib.ffmData), amdmsFLATFIELD_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateFlatfieldFitMap(amdmsALGO_PROPERTIES_ENV *env,
				    amdmsFITS *outfile,
				    amdmsFITS_FLAGS flags)
{
  int     i;

  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR CATG", "CALIB", "Observation category")  != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR TECH", "INTERFEROMETRY", "Observation technique") != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsUpdateKeywordString(outfile, "HIERARCH ESO DPR TYPE", "FLATFIELD", "Observation type") != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  if (amdmsCreateData(outfile, flags, amdmsFLATFIELD_FIT_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsFLATFIELD_FIT_NROWS; i++) {
    env->ffFit[i].index = (double)(i + 1);
    if (amdmsWriteData(outfile, &(env->ffFit[i]), i, 0) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateConversionFactorMap(amdmsALGO_PROPERTIES_ENV *env,
					amdmsFITS *outfile,
					amdmsFITS_FLAGS flags)
{
  amdmsPIXEL   cf, dummy;
  int          nx, ny;

  if (env->env.filter.aoiFlag) {
    amdmsCalcStat(&(env->env.calib), &(env->cfmData), 0,
		 env->env.filter.aoiX,
		 env->env.filter.aoiY,
		 env->env.filter.aoiWidth,
		 env->env.filter.aoiHeight,
		 &cf, &dummy);
    amdmsUpdateKeywordDouble(outfile, "GAIN", cf, 5, "CF in [e-/ADU]");
  }
  if (env->env.q4Flag) {
    nx = env->cfmData.nx;
    ny = env->cfmData.ny;
    amdmsCalcStat(&(env->env.calib), &(env->cfmData), 0, 0, 0, nx/2, ny/2, &cf, &dummy);
    amdmsUpdateKeywordDouble(outfile, "GAIN1", cf, 5, "CF Q1 in [e-/ADU]");
    amdmsCalcStat(&(env->env.calib), &(env->cfmData), 0, nx/2, 0, nx/2, ny/2, &cf, &dummy);
    amdmsUpdateKeywordDouble(outfile, "GAIN2", cf, 5, "CF Q2 in [e-/ADU]");
    amdmsCalcStat(&(env->env.calib), &(env->cfmData), 0, 0, ny/2, nx/2, ny/2, &cf, &dummy);
    amdmsUpdateKeywordDouble(outfile, "GAIN3", cf, 5, "CF Q3 in [e-/ADU]");
    amdmsCalcStat(&(env->env.calib), &(env->cfmData), 0, nx/2, ny/2, nx/2, ny/2, &cf, &dummy);
    amdmsUpdateKeywordDouble(outfile, "GAIN4", cf, 5, "CF Q4 in [e-/ADU]");
  }

  if (amdmsCreateData(outfile, flags, amdmsCONVERSION_FACTOR_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->cfmData.index = 1.0;
  if (amdmsWriteData(outfile, &(env->cfmData), amdmsCONVERSION_FACTOR_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateNonlinearityMap(amdmsALGO_PROPERTIES_ENV *env,
				    amdmsFITS *outfile,
				    amdmsFITS_FLAGS flags)
{
  if (amdmsCreateData(outfile, flags, amdmsNONLINEARITY_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->env.calib.nlmDataLimit.index = 1.0;
  if (amdmsWriteData(outfile, &(env->env.calib.nlmDataLimit), amdmsNONLINEARITY_LIMIT_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->env.calib.nlmDataA0.index = 2.0;
  if (amdmsWriteData(outfile, &(env->env.calib.nlmDataA0), amdmsNONLINEARITY_A0_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->env.calib.nlmDataA1.index = 3.0;
  if (amdmsWriteData(outfile, &(env->env.calib.nlmDataA1), amdmsNONLINEARITY_A1_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateNonlinearityFitMap(amdmsALGO_PROPERTIES_ENV *env,
				       amdmsFITS *outfile,
				       amdmsFITS_FLAGS flags)
{
  int     i;

  if (amdmsCreateData(outfile, flags, amdmsNONLINEARITY_FIT_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsNONLINEARITY_FIT_NROWS; i++) {
    env->nlFit[i].index = (double)(i + 1);
    if (amdmsWriteData(outfile, &(env->nlFit[i]), i, 0) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreatePhotonTransferCurveMap(amdmsALGO_PROPERTIES_ENV *env,
					   amdmsFITS *outfile,
					   amdmsFITS_FLAGS flags)
{
  int     i;

  if (amdmsCreateData(outfile, flags, amdmsPTC_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsPTC_NROWS; i++) {
    env->ptcFit[i].index = (double)(i + 1);
    if (amdmsWriteData(outfile, &(env->ptcFit[i]), i, 0) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateDarkCurrentMap(amdmsALGO_PROPERTIES_ENV *env,
				   amdmsFITS *outfile,
				   amdmsFITS_FLAGS flags)
{
  int     i;

  if (amdmsCreateData(outfile, flags, amdmsDARK_CURRENT_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  for (i = 0; i < amdmsDARK_CURRENT_NROWS; i++) {
    env->dcFit[i].index = (double)(i + 1);
    if (amdmsWriteData(outfile, &(env->dcFit[i]), i, 0) != amdmsSUCCESS) {
      return amdmsFAILURE;
    }
  }
  return amdmsSUCCESS;
}

amdmsCOMPL amdmsCreateHistogramMap(amdmsALGO_PROPERTIES_ENV *env,
				   amdmsFITS *outfile,
				   amdmsFITS_FLAGS flags)
{
  if (amdmsCreateSimpleData(outfile, flags, env->hX.steps, env->hY.steps, amdmsHISTOGRAM_NROWS, 1) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  env->histoData.index = 1.0;
  if (amdmsWriteData(outfile, &(env->histoData), amdmsHISTOGRAM_ROW, 0) != amdmsSUCCESS) {
    return amdmsFAILURE;
  }
  return amdmsSUCCESS;
}

