/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Functions to handle dark current values.
 *
 * The dark maps are needed in order to compensate for detector effects.
 * These maps are made by calculating an average image out of a sequence of raw
 * uncalibrated data. A dark map is only validate for a specific subwindow
 * setup and exposure time. Normally a sequence of SKY images are used to
 * generate a dark map, but AMBER DARK files (not on sky, but on lab 
 * temperature shutters) may be used as well.  
 */

#define _POSIX_SOURCE 1

/* 
 * System Headers
 */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fitsio.h"

/* 
 * Local Headers
 */
#include "amdlib.h"
#include "amdlibProtected.h"

/* 
 * Local function declaration 
 */
static void amdlibInitDarkData(amdlibDARK_DATA *dark);
static amdlibCOMPL_STAT amdlibAllocateDarkData
                                (amdlibRAW_DATA  *rawData,
                                 amdlibDARK_DATA *dark);
static void amdlibFreeDarkData(amdlibDARK_DATA *dark);

/* 
 * Public functions 
 */
/**
 * Release memory allocated to store dark 
 *
 * @param dark structure where dark (or sky) is stored
 */
void amdlibReleaseDarkData(amdlibDARK_DATA *dark)
{
    amdlibLogTrace("amdlibReleaseDarkData()");
    
    amdlibFreeDarkData(dark);
    memset (dark, '\0', sizeof(amdlibDARK_DATA));    
}

/**
 * Save dark structure on disk to save time in case it is needed more than once
 *
 */
amdlibCOMPL_STAT amdlibSaveDarkDataOnFile(amdlibDARK_DATA *dark,
                                          FILE *fp)
{
    int  iRegion;
    char version[32];

    amdlibLogTrace("amdlibSaveDarkDataOnFile()");
    
    if (dark->thisPtr != dark)
    {
        return amdlibFAILURE;
    }
    /*tag with version*/
    amdlibGetVersion(version);
    if ( fwrite(version,sizeof(char), 32,fp) != 32 )
    {
        return amdlibFAILURE;
    }
    /* write otfBadIsPresent */
    if ( fwrite(&dark->otfBadIsPresent,sizeof(amdlibBOOLEAN), 1,fp) != 1 )
    {
        return amdlibFAILURE;
    }
    for (iRegion = 0; iRegion < dark->nbRegions; iRegion++)
    {
        int nbOfElements;
        nbOfElements = dark->region[iRegion].dimAxis[0] *
            dark->region[iRegion].dimAxis[1];

        /* write data */
        if ( fwrite(dark->region[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp) != nbOfElements )
        {
            return amdlibFAILURE;
        }
        if ( fwrite(dark->noise[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp) != nbOfElements )
        {
            return amdlibFAILURE;
        }
        if (dark->otfBadIsPresent==amdlibTRUE)
        {
            if ( fwrite(dark->otfBad[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp) != nbOfElements )
            {
                return amdlibFAILURE;
            }
        }
    }
    return amdlibSUCCESS;
}

amdlibCOMPL_STAT amdlibRetrieveSavedDarkDataOnFile(amdlibDARK_DATA *dark,
                                                   FILE *fp)
{
    int  iRegion;
    char version[32],fileVersion[32];

    amdlibLogTrace("amdlibRetrieveSavedDarkDataOnFile()");
    
    if (dark->thisPtr != dark)
    {
        return amdlibFAILURE;
    }
    /*check tag with version*/
    amdlibGetVersion(version);
    if ( fread(fileVersion,sizeof(char), 32,fp) != 32 )
    {
        return amdlibFAILURE;
    }
    if(strcmp(version,fileVersion)!=0)
    {
        amdlibLogWarning("saved bad pixel file version %s differs from amdlib version %s, will recompute.",fileVersion,version);
        return amdlibFAILURE;
    } 

    /* read otfBadIsPresent */
    if ( fread(&dark->otfBadIsPresent,sizeof(amdlibBOOLEAN), 1,fp) != 1 )
    {
        return amdlibFAILURE;
    }
    for (iRegion = 0; iRegion < dark->nbRegions; iRegion++)
    {
        int nbOfElements;
        nbOfElements = dark->region[iRegion].dimAxis[0] *
            dark->region[iRegion].dimAxis[1];

        /* read data */
        if ( fread(dark->region[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp)!= nbOfElements )
        {
            return amdlibFAILURE;
        }
        if ( fread(dark->noise[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp)!= nbOfElements )
        {
            return amdlibFAILURE;
        }
        if (dark->otfBadIsPresent==amdlibTRUE)
        {
            if ( fread(dark->otfBad[iRegion].data,sizeof(amdlibDOUBLE), nbOfElements,fp)!= nbOfElements )
            {
                return amdlibFAILURE;
            }
        }
    }
    return amdlibSUCCESS;
}

/**
 * Set dark map to the given value. 
 *
 * This function allocates a dark structure corresponding to the raw data
 * structure (i.e. having same regions), and generate the dark map by
 * computing the mean pixel value on all frames. 
 *
 * @param input rawData structure (that measured the dark)
 * @param output dark structure
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
#define amdlibMAX_REGIONS 15
#define amdlibMIN_FRAMES_FOR_BADPIXEL_STATISTICS 400             
amdlibCOMPL_STAT amdlibGenerateDarkData(amdlibRAW_DATA  *rawData,
                                        amdlibDARK_DATA *dark,
                                        amdlibERROR_MSG errMsg)
{
    int     iRegion;
    int     iRow, iCol;
    int     iPixel;
    int     iFrame, iFirstFrame;
    int     nbFrames=rawData->region[0].dimAxis[2];
    int     nbGoodFrames;
    double  sum, sigma2det;
    int startPixelX,x,nx,startPixelY,y,ny;
    int iFFskip=0;
    double **otfBadPtr;
    int do_removeGlobalBias=0;
    int compute=0;
    double *lineMean;
    int i;
    int     regionSize; 
#define MGNLEN 512
    char magicName[MGNLEN],tmp[MGNLEN];
    double obsDate=-1.0;
    FILE *fp = NULL;
    char *darkMemoryDir=NULL;
    int useDarkRepository=0;

    amdlibLogTrace("amdlibGenerateDarkData()");
    
    /* Check raw data structure contains data */
    if (rawData->dataLoaded == amdlibFALSE)
    {
        amdlibSetErrMsg("The raw data structure does "
                        "not contain data. Check call to amdlibLoadRawData()");
        return amdlibFAILURE;
    }

    /* Check frame type */
    if (rawData->frameType != amdlibDARK_FRAME &&
        rawData->frameType != amdlibSKY_FRAME) 
    {
        amdlibLogWarning("Use of a non-dark frame type: proceed at your own "
                         "risks");
    }

    if (amdlibAllocateDarkData(rawData, dark) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for the dark map");
        return amdlibFAILURE;
    }

    /* Get DATE keyword to retrieve eventual save of this dark */
    for (i=0; i < rawData->insCfg.nbKeywords; i++)
    {
        if ( strncmp(rawData->insCfg.keywords[i].name, "MJD-OBS", 7) == 0)
        {
            sscanf(rawData->insCfg.keywords[i].value, "%lf", &obsDate);
            /* Convert MJD to minutes from 01/01/2000 */
            /* Note that MJD is given from 17/11/1858, and 51544 is the number
             * of days between 01/01/2000 and this date.*/
            obsDate = (obsDate-51544.0)*1440;
            sprintf(magicName,"amdlibDarkSave.%d",(int)obsDate);
            break;
        }
    }

    if (amdlibGetUserPref(amdlibDROP).set==amdlibTRUE)
        iFFskip=amdlibGetUserPref(amdlibDROP).value;
    sprintf(tmp,".%d",iFFskip);
    strncat(magicName,tmp,MGNLEN);

    do_removeGlobalBias=0; 
    if (!(amdlibGetUserPref(amdlibNO_BIAS).set==amdlibTRUE))
    {
        do_removeGlobalBias=1;
    }
    sprintf(tmp,".%1d",do_removeGlobalBias);
    strncat(magicName,tmp,MGNLEN);


    /* If an env is set to define the dir where dark memory files are 
     * stored, then use this facility. */
    if ((darkMemoryDir=getenv("AMDLIB_DARK_REPOSITORY"))!=NULL)
    {
        strncpy(tmp,magicName,MGNLEN);
        sprintf(magicName,"%s/",darkMemoryDir);
        strncat(magicName,tmp,MGNLEN);
        useDarkRepository=1;
    }

    /* is there a memory file present ?*/
    if (useDarkRepository==1)
    {
        fp = fopen(magicName, "r");
        if (fp==NULL) 
        {
            compute=1;
        }
        else
        {
            compute=0;
            /* if obsDate was absent, force compute (unnecessary precaution?) */
            if (obsDate==-1.0) 
            {
                fclose(fp);
                compute=1;
            }
            else
            {
                if ( amdlibRetrieveSavedDarkDataOnFile(dark,fp) != amdlibSUCCESS)
                {
                    compute=1; /*will overwrite bad file */
                }
                else
                {
                    amdlibLogInfo("Successfully Retrieved saved dark file %s",magicName);
                }
                fclose(fp);
            }
        }
    }
    else
    {
        compute=1;
    }
    /* depending whether the saved dark has otfBad or not, we are able to use it if amdlibAUTO_BADPIXEL is set*/
    if (compute==0)
    {
        if (amdlibGetUserPref(amdlibAUTO_BADPIXEL).set==amdlibTRUE)
        {
            if (dark->otfBadIsPresent==amdlibTRUE) /* ok we can, the saved dark for some reason has them */
            {
                amdlibLogInfo("Saved Dark has already the requested otf bad pixel values");
                /* Then Update Bad Pixel Map with the otfBad values */
                for (iRow = 0; iRow < dark->nbRows; iRow++)
                {
                    for (iCol = 1; iCol < dark->nbCols; iCol++)
                    {
                        iRegion  = iRow * dark->nbCols + iCol;
                        nx = dark->region[iRegion].dimAxis[0];
                        ny = dark->region[iRegion].dimAxis[1];
                        startPixelX = dark->region[iRegion].corner[0] - 1;
                        startPixelY = dark->region[iRegion].corner[1] - 1;
                        otfBadPtr = amdlibWrap2DArrayDouble(dark->otfBad[iRegion].data,nx, ny, errMsg); 
                        if (amdlibUpdateBadPixelMap(startPixelX, startPixelY, nx, ny, otfBadPtr, errMsg) != amdlibSUCCESS)
                        {
                            amdlibFree2DArrayDoubleWrapping(otfBadPtr);
                            return amdlibFAILURE;
                        }
                        amdlibFree2DArrayDoubleWrapping(otfBadPtr);
                    }
                }
                return amdlibSUCCESS;
            }
            else /* saved dark has no bad. But this may be normal in view of the number of frames */
            {
                if (nbFrames>=amdlibMIN_FRAMES_FOR_BADPIXEL_STATISTICS)
                {
                    amdlibLogInfo("Saved Dark exists, but has no otf bad pixel estimate. Will recompute dark");
                    compute=1; /* will compute otf */
                }
            }
        }
    }
    
    if (compute)
    {
        dark->otfBadIsPresent=amdlibFALSE;
        if (amdlibGetUserPref(amdlibAUTO_BADPIXEL).set==amdlibTRUE)
        {
            if (nbFrames>=amdlibMIN_FRAMES_FOR_BADPIXEL_STATISTICS)
            {
                dark->otfBadIsPresent=amdlibTRUE;
            }
            else
            {
                amdlibLogWarning("Number of frames (%d) for dark is too low to enable on-the-fly bad pixel estimate.",nbFrames);
            }
        }

        /* /\* This is a dark, not a Bias. Remove the Bias from the Dark first. *\/ */
        /* /\* pixel bias does not exist yet. Here is where it should be removed: *\/ */
        /* /\* Subtract Pixel Bias Map directly on raw data *\/ */
        /* if (amdlibSubtractPixelBias(pixelBias, rawData, errMsg) != amdlibSUCCESS) */
        /* { */
        /*     return amdlibFAILURE; */
        /* } */

        /* For the moment we use another way: estimate bias from the masked pixels */
        /* Calculate and Remove Global Bias (until we have cold shutter bias maps) */
        
        if (do_removeGlobalBias)
        {
            if (amdlibRemoveGlobalBias(rawData, errMsg) != amdlibSUCCESS)
            {
                return amdlibFAILURE;
            }
        }
        /* Calculate the dark: mean of all valid frames (i.e., non-null, 
         * dropping iFFskip first frames) */
        for (iRow = 0; iRow < rawData->nbRows; iRow++)
        {
            for (iCol = 0; iCol < rawData->nbCols; iCol++)
            {
                iRegion  = iRow * rawData->nbCols + iCol;
                nx = rawData->region[iRegion].dimAxis[0];
                ny = rawData->region[iRegion].dimAxis[1];
                startPixelX = rawData->region[iRegion].corner[0] - 1;
                startPixelY = rawData->region[iRegion].corner[1] - 1;
                regionSize   = nx * ny;
                nbFrames     = rawData->region[iRegion].dimAxis[2];
                iFirstFrame=iFFskip;
                for (iPixel = 0; iPixel < regionSize; iPixel++)
                {
                    sum = 0.0;
                    nbGoodFrames=0;
                    for (iFrame = iFirstFrame; iFrame < nbFrames; iFrame++)
                    {
                        if ( rawData->timeTag[iFrame] != 0.0 )
                        {
                            nbGoodFrames++;
                            sum += rawData->region[iRegion].data[iFrame*regionSize 
                                                                 + iPixel];
                        }
                    }
                    sum /= (amdlibDOUBLE)nbGoodFrames;
                    dark->region[iRegion].data[iPixel] = sum;
                    /* compute the noise of this pixel */
                    sigma2det = 0.0;
                    nbGoodFrames=0;
                    for (iFrame =  iFirstFrame; iFrame < nbFrames; iFrame++)
                    {
                        if ( rawData->timeTag[iFrame] != 0.0 )
                        {
                            nbGoodFrames++;
                            sigma2det += amdlibPow2(rawData->region[iRegion].data[iFrame*regionSize
                                                                                  +  iPixel]-sum);
                        }
                    }
                    sigma2det /= (amdlibDOUBLE)nbGoodFrames;
                    dark->noise[iRegion].data[iPixel] = sigma2det;
                }
            
                /* Global Averages */
                sum = 0.0;
                sigma2det = 0.0;
                for (iPixel = 0; iPixel < regionSize; iPixel++)
                {
                    sum += dark->region[iRegion].data[iPixel];
                    sigma2det += dark->noise[iRegion].data[iPixel];
                }
                sum /=  (amdlibDOUBLE) regionSize ;
                sigma2det /=  (amdlibDOUBLE) regionSize;
                amdlibLogTest("Dark region %d: average value=%f (adu), average ron=%f (adu)",iRegion,sum,sqrt(sigma2det));
                if (dark->otfBadIsPresent && iCol!=0)
                {
                    /*Compute a list of bad pixels in this region:
                     * 1) all the pixels whose rms is > amdlibCLIP_RON,
                     * 2) all the pixels whose mean value is markedly different
                     * from the others. */
                    otfBadPtr = amdlibWrap2DArrayDouble(dark->otfBad[iRegion].data,nx, ny, errMsg); 
                    lineMean = calloc(ny, sizeof(*lineMean));
                    for (iPixel = 0, y = 0; y < ny; y++)
                    {
                        lineMean[y]=amdlibQuickSelectDble(&(dark->region[iRegion].data[iPixel]),nx);
                        iPixel+=nx; 
                    }
                    /*use otfBadPtr as workspace */
                    for (iPixel = 0, y = 0; y < ny; y++)
                    {
                        for (x = 0; x < nx; x++)
                        {
                            otfBadPtr[y][x]=dark->region[iRegion].data[iPixel];
                            otfBadPtr[y][x]-=lineMean[y];
                            iPixel++;
                        }
                    }
#define amdlibBIASCLIPFACTOR 8.0 /*8 sigma */
#define amdlibRONCLIPFACTOR 3.0 /*3 sigma */
                    /*flag 1 or 0 according to the 2 requirements */
                    for (iPixel = 0, y = 0; y < ny; y++)
                    {
                        for (x = 0; x < nx; x++)
                        {
                            /* cut everything above some normal variation due to noise, represented by typical ron */
                            if(fabs(otfBadPtr[y][x])>amdlibBIASCLIPFACTOR*sqrt(sigma2det/nbFrames))
                            {
                                otfBadPtr[y][x]=0.0; 
                            }
                            /* variance limited to amdlibRONCLIPFACTOR the typical variance*/
                            else if (dark->noise[iRegion].data[iPixel] > (amdlibRONCLIPFACTOR*amdlibRONCLIPFACTOR)*sigma2det)
                            {
                                otfBadPtr[y][x]=0.0; 
                            }
                            else
                            {
                                otfBadPtr[y][x]=1.0;
                            }
                            iPixel++;
                        }
                    }
                    if (amdlibUpdateBadPixelMap(startPixelX, startPixelY, nx, ny, otfBadPtr, errMsg) != amdlibSUCCESS)
                    {
                        amdlibFree2DArrayDoubleWrapping(otfBadPtr);
                        free(lineMean);
                        return amdlibFAILURE;
                    }
                    amdlibFree2DArrayDoubleWrapping(otfBadPtr);
                    free(lineMean);
                }
            }
        }
        if (useDarkRepository==1)
        {
            fp = fopen(magicName, "w");
            if (fp!=NULL)
            {
                if (amdlibSaveDarkDataOnFile(dark,fp) != amdlibSUCCESS)
                {
                    amdlibLogTrace("Problem saving dark values snaphsot on disk.");
                }
                fclose(fp);
            }
        }
    }
    return amdlibSUCCESS;
}

/**
 * Set dark to a given value (in ADU). 
 *
 * This function allocates a dark structure corresponding to the raw data
 * structure (i.e. having same regions), and set all value of the dark
 * to the given value. 
 *
 * @param input rawData structure (used as template for regions)
 * @param output dark structure 
 * @param value dark value to store GIVEN IN ELECTRONS (for compatibility!).
 * @param ron readout noise value to store GIVEN IN ELECTRONS (for compatibility!).
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibSetDarkData (amdlibRAW_DATA  *rawData,
                                    amdlibDARK_DATA *dark,
                                    amdlibDOUBLE    value,
                                    amdlibDOUBLE    ron,
                                    amdlibERROR_MSG errMsg)
{
    int     iRegion;
    int     iRow, iCol;
    int     iPixel;
    int     regionSize;
    double sum,sigma2det;
    amdlibDOUBLE   gain;
    amdlibLogTrace("amdlibSetDarkData()");
    
    /* Check raw data structure contains data */
    if (rawData->dataLoaded == amdlibFALSE)
    {
        amdlibSetErrMsg("The raw data structure does "
                        "not contain data. Check call to amdlibLoadRawData()");
        return amdlibFAILURE;
    }

    /* Allocate memory for data */
    if (amdlibAllocateDarkData(rawData, dark) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for the dark");
        return amdlibFAILURE;
    }

    /* Set the dark */
    for (iRow = 0; iRow < rawData->nbRows; iRow++)
    {
        for (iCol = 0; iCol < rawData->nbCols; iCol++)
        {
            iRegion      = iRow * rawData->nbCols + iCol;
            gain = rawData->region[iRegion].gain;
            regionSize   = rawData->region[iRegion].dimAxis[0] *
                rawData->region[iRegion].dimAxis[1];
            for (iPixel = 0; iPixel < regionSize; iPixel++)
            {
                dark->region[iRegion].data[iPixel] = value/gain;
                dark->noise[iRegion].data[iPixel] = (ron*ron)/(gain*gain);
            }
        }
    }

    for (iRow = 0; iRow < rawData->nbRows; iRow++)
    {
        for (iCol = 0; iCol < rawData->nbCols; iCol++)
        {
            iRegion      = iRow * rawData->nbCols + iCol;
            regionSize   = rawData->region[iRegion].dimAxis[0] *
            rawData->region[iRegion].dimAxis[1];
            sum = 0.0;
            sigma2det = 0.0;
            for (iPixel = 0; iPixel < regionSize; iPixel++)
            {
                sum += dark->region[iRegion].data[iPixel];
                sigma2det += dark->noise[iRegion].data[iPixel];
            }
            sum /=  (amdlibDOUBLE) regionSize ;
            sigma2det /=  (amdlibDOUBLE) regionSize;
            amdlibLogTrace("Dark region %d: fixed value=%f (adu), fixed ron=%f (adu)",iRegion,sum,sqrt(sigma2det));
        }
    }

    return amdlibSUCCESS;
}

/*
 * Local functions
 */
/**
 * Initialize dark structure.
 *
 * @param dark pointer to dark structure
 */
void amdlibInitDarkData(amdlibDARK_DATA *dark)
{
    amdlibLogTrace("amdlibInitDarkData()");
    
    /* Initialize data structure */
    memset (dark, '\0', sizeof(amdlibDARK_DATA));
    dark->thisPtr = dark;
}

/**
 * Allocate memory for dark structure.
 *
 * This function allocates memory for dark which will result from
 * computation of the given raw data. 
 * 
 * @param rawData structure (used as template for region size, etc)
 * @param output dark structure to be initialized
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibAllocateDarkData(amdlibRAW_DATA        *rawData,
                                             amdlibDARK_DATA *dark)
{
    int  iRegion;

    amdlibLogTrace("amdlibAllocateDarkData()");
    
    if (rawData->thisPtr != rawData)
    {
        return amdlibFAILURE;
    }

    /* If dark data structure is not initialized, do it */
    if (dark->thisPtr != dark)
    {
        amdlibInitDarkData(dark);
    }

    amdlibFreeDarkData(dark);
    dark->expTime = rawData->expTime;
    dark->nbRows = rawData->nbRows;
    dark->nbCols = rawData->nbCols;
    dark->imagingDetector = rawData->imagingDetector;
    dark->nbRegions = rawData->nbRegions;
    dark->otfBadIsPresent= amdlibFALSE;
    /* Allocate memory for storing information of dark regions */
    if (amdlibAllocateRegions(&dark->region, 
                              dark->nbRegions) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }
    /* Allocate memory for storing information of noise regions */
    if (amdlibAllocateRegions(&dark->noise, 
                              dark->nbRegions) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Allocate memory for storing information of noise regions */
    if (amdlibAllocateRegions(&dark->otfBad, 
                              dark->nbRegions) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Allocate memory for storing dark region data */
    for (iRegion = 0; iRegion < dark->nbRegions; iRegion++)
    {
        int nbOfElements;
        
        memcpy(&dark->region[iRegion],
               &rawData->region[iRegion],sizeof(amdlibREGION));
        dark->region[iRegion].dimAxis[2] = 1;
        dark->region[iRegion].dmp = NULL;
        dark->region[iRegion].dmc = NULL;
        nbOfElements = rawData->region[iRegion].dimAxis[0] *
            rawData->region[iRegion].dimAxis[1];
        
        /* Allocate memory for data */
        dark->region[iRegion].data = calloc(sizeof(amdlibDOUBLE), nbOfElements);
        if (dark->region[iRegion].data == NULL)
        {
            return amdlibFAILURE;
        }
        /* the same for noise */
        memcpy(&dark->noise[iRegion],
               &rawData->region[iRegion],sizeof(amdlibREGION));
        dark->noise[iRegion].dimAxis[2] = 1;
        dark->noise[iRegion].dmp = NULL;
        dark->noise[iRegion].dmc = NULL;
        nbOfElements = rawData->region[iRegion].dimAxis[0] *
            rawData->region[iRegion].dimAxis[1];
        
        /* Allocate memory for data */
        dark->noise[iRegion].data = calloc(sizeof(amdlibDOUBLE), nbOfElements);
        if (dark->noise[iRegion].data == NULL)
        {
            return amdlibFAILURE;
        }

        /* the same for otfBad */
        memcpy(&dark->otfBad[iRegion],
               &rawData->region[iRegion],sizeof(amdlibREGION));
        dark->otfBad[iRegion].dimAxis[2] = 1;
        dark->otfBad[iRegion].dmp = NULL;
        dark->otfBad[iRegion].dmc = NULL;
        nbOfElements = rawData->region[iRegion].dimAxis[0] *
            rawData->region[iRegion].dimAxis[1];
        
        /* Allocate memory for data */
        dark->otfBad[iRegion].data = calloc(sizeof(amdlibDOUBLE), nbOfElements);
        if (dark->otfBad[iRegion].data == NULL)
        {
            return amdlibFAILURE;
        }
    }
    return amdlibSUCCESS;
}

/**
 * Free memory allocated memory of dark structure.
 *
 * This function frees previously allocated memory (if any) where dark has
 * been stored. 
 * 
 * @param dark structure to be freed
 */
void amdlibFreeDarkData(amdlibDARK_DATA *dark)
{
    amdlibLogTrace("amdlibFreeDarkData()");
    
    if (dark->thisPtr != dark)
    {
        amdlibInitDarkData(dark);
        return;
    }

    amdlibFreeRegions(&dark->region, dark->nbRegions);
    amdlibFreeRegions(&dark->noise, dark->nbRegions);
    amdlibFreeRegions(&dark->otfBad, dark->nbRegions);
}

/*___oOo___*/
