/*                                                                            *
 *   This file is part of the ESO X-shooter Pipeline                          *
 *   Copyright (C) 2006 European Southern Observatory                         *
 *                                                                            *
 *   This library is free software; you can redistribute it and/or modify     *
 *   it under the terms of the GNU General Public License as published by     *
 *   the Free Software Foundation; either version 2 of the License, or        *
 *   (at your option) any later version.                                      *
 *                                                                            *
 *   This program is distributed in the hope that it will be useful,          *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 *   GNU General Public License for more details.                             *
 *                                                                            *
 *   You should have received a copy of the GNU General Public License        *
 *   along with this program; if not, write to the Free Software              *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA     *
 *                                                                            */

/*
 * $Author: amodigli $
 * $Date: 2013-07-24 12:15:12 $
 * $Revision: 1.83 $
 * $Name: not supported by cvs2svn $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/*-------------------------------------------------------------------------*/
/**
 * @defgroup xsh_tools Xsh Specific Tools
 *
 */
/**
 * @defgroup xsh_badpixelmap  XSH Specific Bad Pixel Mask Functions
 * @ingroup xsh_tools
 *
 * This module contains functions used to handle Xshooter version of
 * Bad pixel mask (each pixel is marked with a set of bits describing why the
 * pixel has been considered BAD).
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*-----------------------------------------------------------------------------
  	                           Includes
 -----------------------------------------------------------------------------*/

#include <xsh_dfs.h>
#include <xsh_utils.h>
#include <xsh_data_pre.h>
#include <xsh_qc_definition.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_ksigma_clip.h>
#include <cpl.h>
#include <string.h>

#include <xsh_badpixelmap.h>
#include <xsh_detmon.h>
#include <xsh_pfits.h>

/*-----------------------------------------------------------------------------
  	                           Function prototypes
 -----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
  	                           Functions
 -----------------------------------------------------------------------------*/

/** 
 * Make an "OR" between 2 bad QUAL images.
 * 
 * @param self Inout bad pixel map (modified)
 * @param right Secondary bpmap (unchanged)
 */
void xsh_badpixelmap_or( xsh_pre *self, const xsh_pre *right )
{
  int * qual0 = NULL,
    * qual1 = NULL ;
  int i ;

  XSH_ASSURE_NOT_NULL( self ) ;
  XSH_ASSURE_NOT_NULL( right ) ;
  check(qual0 = cpl_image_get_data_int(right->qual));
  check(qual1 = cpl_image_get_data_int(self->qual));
  XSH_ASSURE_NOT_ILLEGAL(right->nx == self->nx);
  XSH_ASSURE_NOT_ILLEGAL(right->ny == self->ny );

  for(i=0;i<self->nx*self->ny;i++) {
    qual1[i] |= qual0[i] ;
  }

 cleanup:
  return ;
}

/** 
 * Create the master bad pixel map (cpl format) from a Bad Pixel Map image
 * 
 * @param bpmap XSH format bad pixel map
 * @param inst XSH instrumnent (arm/ifu) setting

 */
cpl_frame*
xsh_badpixelmap_crea_master_from_bpmap(cpl_frame* bpmap, xsh_instrument* inst){

   const char* fname=NULL;
   cpl_propertylist* plist=NULL;
   cpl_image* image=NULL;
   char bname[40];
   char btag[40];
   cpl_frame* master=NULL;
   master=cpl_frame_duplicate(bpmap);
   fname=cpl_frame_get_filename(bpmap);
   plist=cpl_propertylist_load(fname,0);
   image=cpl_image_load(fname,CPL_TYPE_INT,0,0);
   sprintf(btag,"%s_%s",XSH_MASTER_BP_MAP_FLAT,xsh_instrument_arm_tostring(inst));
   sprintf(bname,"%s.fits",btag);
   xsh_pfits_set_pcatg(plist,btag);
   cpl_image_save(image,bname,XSH_PRE_QUAL_BPP,plist,CPL_IO_DEFAULT);
   cpl_frame_set_filename(master,bname);
   cpl_frame_set_tag(master,btag);
   xsh_add_temporary_file(bname);

   xsh_free_propertylist(&plist);
   xsh_free_image(&image);
   return master;
}


static cpl_error_code xsh_flag_ima_bad_pix(cpl_image** image, int* bppix,const int decode_bp) {
  int iy = 0;
  int ix = 0;
  int nx = 0;
  int ny = 0;
  int iy_nx = 0;

  nx = cpl_image_get_size_x(*image);
  ny = cpl_image_get_size_y(*image);

  /* Set the bad pixel map array for the image */
  for (iy = 0; iy < ny; iy++) {
     iy_nx=iy*nx;
    for (ix = 0; ix < nx; ix++) {
      int j = ix + iy_nx;
      if ((bppix[j] & decode_bp) > 0 ) {
        cpl_image_reject(*image, ix + 1, iy + 1);
      }
    }
  }
  return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief Allows the growing and shrinking of bad pixel masks. It can be used to
  e.g. set pixels to bad if the pixel is surrounded by other bad pixels.
  @param input_mask input mask
  @param kernel_nx  size in x-direction of the filtering kernel
  @param kernel_ny  size in y-direction of the filtering kernel
  @param filter     filter modes as defined in cpl - see below
  Supported modes:
  CPL_FILTER_EROSION, CPL_FILTER_DILATION, CPL_FILTER_OPENING,
  CPL_FILTER_CLOSING

  The returned mask must be deallocated using cpl_mask_delete().
  The algorithm assumes, that all pixels outside the mask are good, i.e. it
  enlarges the mask by the kernel size and marks this border as good. It
  applies on the enlarged mask the operation and extract the original-size
  mask at the very end.
 */
/*----------------------------------------------------------------------------*/
cpl_mask * xsh_bpm_filter(
        const cpl_mask    *   input_mask,
        cpl_size        kernel_nx,
        cpl_size        kernel_ny,
        cpl_filter_mode filter)
{
    cpl_mask * kernel = NULL;
    cpl_mask * filtered_mask = NULL;
    cpl_mask * expanded_mask = NULL;
    cpl_mask * expanded_filtered_mask = NULL;

    /* Check Entries */
    cpl_ensure(input_mask != NULL, CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(kernel_nx >= 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
    cpl_ensure(kernel_ny >= 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
    cpl_ensure(filter == CPL_FILTER_EROSION || filter == CPL_FILTER_DILATION ||
            filter == CPL_FILTER_OPENING || filter == CPL_FILTER_CLOSING,
            CPL_ERROR_ILLEGAL_INPUT, NULL);

    /* Only odd-sized masks allowed */
    cpl_ensure((kernel_nx&1) == 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
    cpl_ensure((kernel_ny&1) == 1, CPL_ERROR_ILLEGAL_INPUT, NULL);

    kernel = cpl_mask_new(kernel_nx, kernel_ny);
    cpl_mask_not(kernel); /* All values set to unity*/

    /* Enlarge the original mask with the kernel size and assume that outside
     * all pixels are good */
    expanded_mask = cpl_mask_new(
            cpl_mask_get_size_x(input_mask) + 2 * kernel_nx,
            cpl_mask_get_size_y(input_mask) + 2 * kernel_ny);

    cpl_mask_copy(expanded_mask, input_mask, kernel_nx + 1, kernel_ny +1 );

    expanded_filtered_mask = cpl_mask_new(cpl_mask_get_size_x(expanded_mask),
            cpl_mask_get_size_y(expanded_mask));

    if(cpl_mask_filter(expanded_filtered_mask, expanded_mask, kernel, filter,
                    CPL_BORDER_ZERO) != CPL_ERROR_NONE) {

        cpl_mask_delete(kernel);
        cpl_mask_delete(expanded_filtered_mask);
        cpl_mask_delete(expanded_mask);
        return NULL;
    }

    /* Extract the original mask from the expanded mask */
    filtered_mask = cpl_mask_extract(expanded_filtered_mask,
            kernel_nx+1, kernel_ny + 1,
            cpl_mask_get_size_x(input_mask) + kernel_nx,
            cpl_mask_get_size_y(input_mask) + kernel_ny);


    /* Free memory */
    cpl_mask_delete(kernel);
    cpl_mask_delete(expanded_filtered_mask);
    cpl_mask_delete(expanded_mask);

    return filtered_mask;
}

/**
 * Create the bad pixel map (cpl format) attached to the XSH format
 * Bad Pixel Map image
 *
 * @param image the image on which attaches the cpl bpmap
 * @param bpmap XSH format bad pixel map
 * @param decode_bp bad pixel code to be rejected
 */
cpl_mask*
xsh_code_is_in_qual(cpl_image * qual, const int code) {
  cpl_mask *mask = NULL;

  assure( qual != NULL, CPL_ERROR_NULL_INPUT,
      "QUAL input is NULL pointer" );
  cpl_size nx=cpl_image_get_size_x(qual);
  cpl_size ny=cpl_image_get_size_y(qual);

  mask=cpl_mask_new(nx,ny);
  int* pqual=cpl_image_get_data_int(qual);
  cpl_binary* pmask=cpl_mask_get_data(mask);
  int size=nx*ny;
  for(int i=0;i<size;i++){
      if( (pqual[i] & code) ==0 ) {
          /* if pixel is bad flag it in table */
          pmask[i]=CPL_BINARY_1;
      }
  }
  cleanup:
  return mask;
}

/**
 * Create the bad pixel map (cpl format) attached to the XSH format
 * Bad Pixel Map image
 *
 * @param image the image on which attaches the cpl bpmap
 * @param bpmap XSH format bad pixel map
 * @param decode_bp bad pixel code to be rejected
 */
cpl_mask*
xsh_qual_to_cpl_mask(cpl_image * qual, const int decode_bp) {
  cpl_mask *mask = NULL;

  assure( qual != NULL, CPL_ERROR_NULL_INPUT,
      "QUAL input is NULL pointer" );
  cpl_size nx=cpl_image_get_size_x(qual);
  cpl_size ny=cpl_image_get_size_y(qual);

  mask=cpl_mask_new(nx,ny);
  int* pqual=cpl_image_get_data_int(qual);
  cpl_binary* pmask=cpl_mask_get_data(mask);
  int size=nx*ny;
  for(int i=0;i<size;i++){
      if( (pqual[i] & decode_bp) >0 ) {
          /* if pixel is bad flag it in table */
          pmask[i]=CPL_BINARY_1;
      }
  }
  cleanup:
  return mask;
}


/** 
 * Create the bad pixel map (cpl format) attached to the XSH format
 * Bad Pixel Map image
 * 
 * @param image the image on which attaches the cpl bpmap
 * @param bpmap XSH format bad pixel map
 * @param decode_bp bad pixel code to be rejected
 */
void xsh_set_image_cpl_bpmap(cpl_image * image, cpl_image * bpmap,const int decode_bp) {
  int *bppix = NULL;

  assure( bpmap != NULL, CPL_ERROR_NULL_INPUT,
      "BpMap is NULL pointer" );

  check(bppix = cpl_image_get_data_int (bpmap));
  /* Set the bad pixel map array for the image */
  xsh_flag_ima_bad_pix(&image,bppix,decode_bp);

  cleanup: return;
}

void xsh_image_flag_bp(cpl_image * image,cpl_image * bpmap, xsh_instrument* inst)
{
   cpl_mask* mask=NULL;
   int nx=0;
   int ny=0;
   int j_nx=0;
   int pix=0;
   int i=0;
   int j=0;


   cpl_binary* pmsk=0;
   int* pmap=0;

   /* TODO use decode_bp to flag only certain pixels */
   nx=cpl_image_get_size_x(image);
   ny=cpl_image_get_size_y(image);
   mask=cpl_mask_new(nx,ny);
   pmsk=cpl_mask_get_data(mask);
   pmap=cpl_image_get_data_int(bpmap);
   for(j=0;j<ny;j++) {
      j_nx=j*nx;
      for(i=0;i<nx;i++) {
         pix=j_nx+i;
         if( (inst->decode_bp & pmap[pix]) > 0) {
            pmsk[pix]=CPL_BINARY_1;
         }
      }
   }

   cpl_image_reject_from_mask(image,mask);

   xsh_free_mask(&mask);
   return;
}

void xsh_bpmap_bitwise_to_flag(cpl_image * bpmap,int flag )
{
  float* data = NULL;
  int nx=0;
  int ny=0;
  int i=0;

  check(nx = cpl_image_get_size_x(bpmap));
  check(ny = cpl_image_get_size_y(bpmap));

  check(data = cpl_image_get_data_float(bpmap));

  for(i=0;i<nx*ny;i++){
    if(data[i]){
      data[i] = flag;
    }
  }

 cleanup:
  return;


}


void xsh_bpmap_mask_bad_pixel(cpl_image * bpmap, cpl_mask* mask,
  int flag )
{
  int* data = NULL;
  cpl_mask* old = NULL;
  cpl_binary* maskdata = NULL;
  int i = 0, nx = 0, ny = 0;

  /* check parameters */
  assure(bpmap != NULL, CPL_ERROR_NULL_INPUT,"BpMap is NULL pointer" ) ;
  assure(mask != NULL, CPL_ERROR_NULL_INPUT,"mask is NULL pointer" ) ;
  
  /* get size */
  check(nx = cpl_image_get_size_x(bpmap));
  check(ny = cpl_image_get_size_y(bpmap));
  assure(nx == cpl_mask_get_size_x(mask),CPL_ERROR_ILLEGAL_INPUT,
    "mask %" CPL_SIZE_FORMAT " and image %d must have same size in x",
    cpl_mask_get_size_x(mask),nx);
  assure(ny == cpl_mask_get_size_y(mask),CPL_ERROR_ILLEGAL_INPUT,
    "mask %" CPL_SIZE_FORMAT " and image %d must have same size in y",
    cpl_mask_get_size_y(mask),ny);

  /* get bpmap mask */
  check( old = cpl_image_get_bpm(bpmap));

  /* get mask data */
  check(maskdata = cpl_mask_get_data(mask));

  /* get bpmap data */
  check(data = cpl_image_get_data(bpmap));
  /* apply mask to image */
  for(i=0;i<nx*ny;i++){
    if(maskdata[i]){
      data[i] |= flag;
    }
  }
  
  /* merge the two mask with OR*/
  check(cpl_mask_or(old,mask));

  cleanup:
    return;
}

void
xsh_bpmap_set_bad_pixel (cpl_image * bpmap, int ix, int iy, int flag)
{
  cpl_image_set (bpmap, ix, iy, (double) flag);
  cpl_image_reject (bpmap, ix, iy);
}

/** 
 * Create a bad pixel map image by a union of all the bpmap images
 * of the liste.
 * 
 * @param liste List of bpmap images
 * @param decode_bp  bad pixel code
 * @return an image, the union of all the bpmap images in the list
 */
cpl_image* xsh_bpmap_collapse_bpmap_create (cpl_imagelist * liste,const int decode_bp)
{
  // from a list of bpmap images, create one image, the union of all the bpmap
  // images

  cpl_image *bpmap = NULL;   /**< first image is the result */
  int* bppix = NULL;
  int nx, ny, npix;
  int nimg, i;
  cpl_image *result = NULL;

  xsh_msg( "---> xsh_bpmap_collapse_bpmap_create" ) ;
  bpmap = cpl_image_duplicate (cpl_imagelist_get (liste, 0));
  assure (bpmap != NULL, cpl_error_get_code (), "Cant duplicate first bpmap");

  bppix = cpl_image_get_data_int (bpmap);
  assure (bppix != NULL, cpl_error_get_code (), "Cant get data int");

  nimg = cpl_imagelist_get_size (liste);

  nx = cpl_image_get_size_x (bpmap);
  ny = cpl_image_get_size_y (bpmap);
  npix = nx * ny;
  xsh_msg_dbg_low ("Nb of bpmap: %d, nx: %d, ny: %d [%d]", nimg, nx, ny, npix);

  for (i = 1; i < nimg; i++) {
    int j = 0;
    cpl_image *current = NULL;
    int *curpix = NULL;

    current = cpl_imagelist_get (liste, i);
    assure (current != NULL, cpl_error_get_code (), "Cant get bpmap #%d", i);

    curpix = cpl_image_get_data_int (current);
    assure (curpix != NULL, cpl_error_get_code (),
	    "Cant get data int for bpmap #%d", i);

    for (j = 0; j < npix; j++)
      bppix[j] |= curpix[j];
  }

  result = cpl_image_wrap_int (nx, ny, bppix);
  assure (result != NULL, cpl_error_get_code (), "Cant wrap final bpmap");
  /* Set the bad pixel map array for the image */
  xsh_flag_ima_bad_pix(&result,bppix,decode_bp);

  return result;

cleanup:
  return NULL;
}

/** 
 * Count the number of bad pixels in a bad pixel map image.
 * 
 * @param bpmap Bad pixel map image
 * @param nx Number of pixels along X axis
 * @param ny Number of pixels along Y axis
 * 
 * @return Tne number of bad pixels
 */
int
xsh_bpmap_count (cpl_image * bpmap, int nx, int ny)
{
  int *pixbuf;
  int i, count = 0, npix = nx * ny;

  pixbuf = cpl_image_get_data_int (bpmap);
  assure (pixbuf != NULL, cpl_error_get_code (), "Cant get pixel buffer");

  for (i = 0; i < npix; i++) {
    if (*(pixbuf + i) != 0)
      count++;
  }

cleanup:
  return count;
}

/** 
 * Create a median image from a list of images, taking into account 
 * a mask given pixel to compute
 * 
 * @param median the result median of the images collapse
 * @param list List of images to collapse
 * @param bpmap mask given pixels to compute 
 *  CPL_BINARY_0 ==> compute the median for this pixel
 */
void xsh_bpmap_collapse_median(cpl_image* median, cpl_imagelist * list, 
  cpl_mask * bpmap)
{
  int nx, ny, nimg, i;
  cpl_image **pimg = NULL;
  cpl_vector *pixvector = NULL;
  double* data = NULL;
  cpl_binary* mask_data = NULL; 
  int ix, iy, k;

  XSH_ASSURE_NOT_NULL(median);
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_NULL(bpmap);

  check(nx = cpl_image_get_size_x(median));
  check(ny = cpl_image_get_size_y(median));
  check(nimg = cpl_imagelist_get_size(list));

  /* create the array of pointers to images in list */
  XSH_CALLOC(pimg, cpl_image*, nimg);

  /* Populate images pointer array */
  for (i = 0; i < nimg; i++){
    *(pimg + i) = cpl_imagelist_get (list, i);
    assure(cpl_image_get_size_x(*(pimg + i)) == nx,CPL_ERROR_ILLEGAL_INPUT,
      " data list x size is not equal to median frame");
    assure(cpl_image_get_size_y(*(pimg + i)) == ny,CPL_ERROR_ILLEGAL_INPUT,
      " data list y size is not equal to median frame");
  }

  /* create pixvector Vector */
  XSH_MALLOC(data, double, nimg);
  check(mask_data = cpl_mask_get_data(bpmap));
  /* Loop over all pixels */
  for (iy = 1; iy <= ny; iy++) {
//    xsh_msg("iy %d / %d",iy,ny);
    for (ix = 1; ix <= nx; ix++) {
      int count = 0;
      /* pixel need to be compute */
      if (mask_data[(iy-1)*nx+ix-1] == CPL_BINARY_0){ 
        for (k = 0; k < nimg; k++) {
	  int rej;
          double val;
    
          check (val = cpl_image_get (pimg[k], ix, iy, &rej) ) ;
	  if (rej == 0) {
            data[count] = val;
	    count++;
          }
	}
      }
      /* now get median of pixvector and set the value in the median */
      if (count) {
	double med = 0.0;
	
        check(pixvector = cpl_vector_wrap(count,data));
        check(med = cpl_vector_get_median( pixvector));
	check(cpl_image_set(median, ix, iy, med));
        check(xsh_unwrap_vector(&pixvector));

      }
    }
  }
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_unwrap_vector (&pixvector);
    }
    XSH_FREE(data); 
    XSH_FREE(pimg) ;
    return;
}

/** 
 * Create a mean image from a list of images, taking into account 
 * a mask given pixel to compute
 * 
 * @param mean the result average of the images collapse
 * @param list List of images to collapse
 * @param bpmap mask given pixels to compute 
 *  CPL_BINARY_0 ==> compute the median for this pixel
 */
void xsh_bpmap_collapse_mean(cpl_image * mean, cpl_imagelist * list, 
			     cpl_mask * bpmap)
{
  int nx, ny, nimg, i;
  cpl_image **pimg = NULL;
  cpl_vector *pixvector = NULL;
  double* data = NULL;
  cpl_binary* mask_data = NULL; 
  int ix, iy, k;

  XSH_ASSURE_NOT_NULL(mean);
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_NULL(bpmap);

  check(nx = cpl_image_get_size_x(mean));
  check(ny = cpl_image_get_size_y(mean));
  check(nimg = cpl_imagelist_get_size(list));

  /* create the array of pointers to images in list */
  XSH_CALLOC(pimg, cpl_image*, nimg);

  /* Populate images pointer array */
  for (i = 0; i < nimg; i++){
    *(pimg + i) = cpl_imagelist_get (list, i);
    assure(cpl_image_get_size_x(*(pimg + i)) == nx,CPL_ERROR_ILLEGAL_INPUT,
      " data list x size is not equal to median frame");
    assure(cpl_image_get_size_y(*(pimg + i)) == ny,CPL_ERROR_ILLEGAL_INPUT,
      " data list y size is not equal to median frame");
  }

  /* create pixvector Vector */
  XSH_MALLOC(data, double, nimg);
  check(mask_data = cpl_mask_get_data(bpmap));
  /* Loop over all pixels */
  for (iy = 1; iy <= ny; iy++) {
//    xsh_msg("iy %d / %d",iy,ny);
    for (ix = 1; ix <= nx; ix++) {
      int count = 0;
      /* pixel need to be compute */
      if (mask_data[(iy-1)*nx+ix-1] == CPL_BINARY_0){ 
        for (k = 0; k < nimg; k++) {
	  int rej;
          double val;
    
          check (val = cpl_image_get (pimg[k], ix, iy, &rej) ) ;
	  if (rej == 0) {
            data[count] = val;
	    count++;
          }
	}
      }
      /* now get avg of pixvector and set the value into the mean */
      if (count) {
	double avg = 0.0;
	
        check(pixvector = cpl_vector_wrap(count,data));
        check(avg = cpl_vector_get_mean( pixvector));
	check(cpl_image_set(mean, ix, iy, avg ));
        check(xsh_unwrap_vector(&pixvector));

      }
    }
  }
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_unwrap_vector (&pixvector);
    }
    XSH_FREE(data); 
    XSH_FREE(pimg) ;
    return;
}

/**
 * Create In the rare case input image has some flagged pixel flag them in the
 * qual
 *
 * @param qual input pixel quality image
 * @param image input image (with possibly flagged pixels)
 */
cpl_error_code
xsh_badpixel_flag_rejected(cpl_image* qual, cpl_image* image)
{
	assure( CPL_TYPE_INT == cpl_image_get_type(qual), cpl_error_get_code(),
	    		"wrong ima qual data type");
    int nx = cpl_image_get_size_x(qual);
    int ny = cpl_image_get_size_y(qual);
    //xsh_msg("nx=%d ny=%d",nx,ny);
    //nx = cpl_image_get_size_x(image);
    //ny = cpl_image_get_size_y(image);
    //xsh_msg("nx=%d ny=%d",nx,ny);

    int* pqual=cpl_image_get_data_int(qual);
    cpl_mask* bpm=cpl_image_get_bpm(image);
    cpl_binary* pbpm=cpl_mask_get_data(bpm);
    //xsh_msg("Bitwise OR combine");
    for(int j=0;j<ny;j++) {
        int j_nx=j*nx;
        for(int i=0;i<nx;i++) {
            if( pbpm[j_nx+i] == CPL_BINARY_1 ) {

                pqual[j_nx+i] |= QFLAG_ALL_PIX_BAD;
            }
        }
    }
    cleanup:
    return cpl_error_get_code();
}

/** 
 * Create coadd a mask frame with another 
 * 
 * @param self input bp map image
 * @param right input bp map image 
 * @param mode  Bitwise AND or Bitwise OR
 */
cpl_error_code
xsh_badpixelmap_image_coadd(cpl_image **self, const cpl_image *right, const int mode )
{
  int nx = 0, ny = 0;
  int* pself=NULL;
  const int* pright=NULL;
  int i=0;
  int j=0;
  int j_nx=0;

  /* get size */
  check(nx = cpl_image_get_size_x(*self));
  check(ny = cpl_image_get_size_y(*self));
  assure(nx == cpl_image_get_size_x(right),CPL_ERROR_ILLEGAL_INPUT,
    "addendum mask %" CPL_SIZE_FORMAT " and original mask %d must have same size in x",
    cpl_image_get_size_x(right),nx);
  assure(ny == cpl_image_get_size_y(right),CPL_ERROR_ILLEGAL_INPUT,
    "addendum mask %" CPL_SIZE_FORMAT " and original mask %d must have same size in y",
    cpl_image_get_size_y(right),ny);

  /* get bpmap mask */
  pself=cpl_image_get_data_int(*self);
  pright=cpl_image_get_data_int_const(right);
  if(mode) {
     //xsh_msg("Bitwise OR combine");
     for(j=0;j<ny;j++) {
        j_nx=j*nx;
        for(i=0;i<nx;i++) {
 
           pself[j_nx+i]|=pright[j_nx+i];

        }
     }
  } else {
     //xsh_msg("Bitwise AND combine");
     for(j=0;j<ny;j++) {
        j_nx=j*nx;
        for(i=0;i<nx;i++) {
 
           pself[j_nx+i]&=pright[j_nx+i];
        }
     }
  }
  cleanup:

  return cpl_error_get_code();

}




/** 
 * Create coadd a mask frame with another 
 * 
 * @param self input bp map frame
 * @param right input bp map frame 
 */
cpl_error_code
xsh_badpixelmap_coadd(cpl_frame *self, const cpl_frame *right,const int mode )
{


  const char* self_name=NULL;
  const char* right_name=NULL;
  cpl_image* map_self=NULL;
  cpl_image* map_right=NULL;
  cpl_propertylist* plist=NULL;


  /* check parameters */
  assure(self  != NULL, CPL_ERROR_NULL_INPUT,"BpMap is NULL pointer" ) ;
  assure(right != NULL, CPL_ERROR_NULL_INPUT,"mask is NULL pointer" ) ;

  check(self_name=cpl_frame_get_filename(self));
  check(right_name=cpl_frame_get_filename(right));
  //xsh_msg("self_name=%s",self_name);
  check(plist=cpl_propertylist_load(self_name,0));

  check(map_self=cpl_image_load(self_name,CPL_TYPE_INT,0,0));
  check(map_right=cpl_image_load(right_name,CPL_TYPE_INT,0,0));
  xsh_msg("Bit-wise OR of %s with %s frame",
          cpl_frame_get_tag(self),cpl_frame_get_tag(right));

  check(xsh_badpixelmap_image_coadd(&map_self,map_right,mode));

  /*
  check(cpl_image_save(map_self,"self.fits",CPL_BPP_IEEE_FLOAT,
			   plist,CPL_IO_DEFAULT));

  check(cpl_image_save(map_right,"right.fits",CPL_BPP_IEEE_FLOAT,
			   plist,CPL_IO_DEFAULT));
  xsh_msg("self name=%s",self_name);
  */
  check(cpl_image_save(map_self,"BP_COMBINE.fits",CPL_BPP_IEEE_FLOAT,
			   plist,CPL_IO_DEFAULT));
  cpl_frame_set_filename(self,"BP_COMBINE.fits");
  xsh_add_temporary_file("BP_COMBINE.fits");

  cleanup:

  xsh_free_propertylist(&plist);
  xsh_free_image(&map_self);
  xsh_free_image(&map_right);

  return cpl_error_get_code();

}


/** 
 * Create coadd a mask frame with another 
 * 
 * @param frame input frame
 * @param bpmap bp map frame to be combined 
 */
cpl_error_code
xsh_frame_qual_update(cpl_frame *frame, const cpl_frame *bpmap,xsh_instrument* inst)
{
   xsh_pre* pre=NULL;
   const char* frame_name=NULL;
   const char* bpmap_name=NULL;
   const char* frame_tag=NULL;

   cpl_image* map_self=NULL;
   cpl_image* map_right=NULL;
   cpl_frame* product=NULL;
   const int mode_or=1;
   assure(frame != NULL, CPL_ERROR_NULL_INPUT,"INPUT frame is NULL pointer" ) ;
   assure(bpmap != NULL, CPL_ERROR_NULL_INPUT,"BP MAP frame is NULL pointer" ) ;

   check(frame_name=cpl_frame_get_filename(frame));
   check(frame_tag=cpl_frame_get_tag(frame));
   check(bpmap_name=cpl_frame_get_filename(bpmap));
 
   check(pre=xsh_pre_load(frame,inst));
   check(map_right=cpl_image_load(bpmap_name,CPL_TYPE_INT,0,0));
   xsh_badpixelmap_image_coadd(&(pre->qual), map_right,mode_or);
   check(product=xsh_pre_save(pre,frame_name,frame_tag,0));
 
  cleanup:
   xsh_pre_free(&pre);
   xsh_free_image(&map_self);
   xsh_free_image(&map_right);
   xsh_free_frame(&product);
   return cpl_error_get_code();

}

/*-------------------------------------------------------------------------*/
/*
 * @brief  Get cold bpixels mask
 * @param  master  Input image
 * @param  ks_low ref level
 * @param  ks_high ref level
 * @param  ks_iter number of iterations 
 * @param  bpmhot      hot pixels map
 * @param  hotpix_nb   number of hot pixels
 * @param  bpmcold     cold pixels map
 * @param  coldpix_nb   number of hot pixels
 * @param  hplist      cold pix header
 * @param  cplist      cold pix header

 * @return CPL_ERROR_NONE on success.
 */
/*-------------------------------------------------------------------------*/

static cpl_error_code
xsh_image_get_hot_cold_maps(cpl_image* masterbias, 
                    const double kappa_low,
                    const double kappa_high,
                    const int low_niter,
                    const int high_niter,
			    cpl_mask** bpmhot,
			    int *hotpix_nb,
			    cpl_mask** bpmcold,
			    int *coldpix_nb,
                            cpl_propertylist** hplist,
                            cpl_propertylist** cplist)
{


  double lower=0;
  double upper=0;
  double dark_med=0;
  double stdev=0;
  double mean=0;

  check(xsh_ksigma_clip(masterbias, 1, 1,
			cpl_image_get_size_x(masterbias),
			cpl_image_get_size_y(masterbias),
			kappa_low,
			low_niter, 1e-5,
			&mean, &stdev));

  check(cpl_propertylist_append_double(*cplist,XSH_QC_MASTER_MEAN,mean));
  check(cpl_propertylist_set_comment(*cplist,XSH_QC_MASTER_MEAN,XSH_QC_MASTER_MEAN_C));

  check(cpl_propertylist_append_double(*cplist, XSH_QC_MASTER_RMS,stdev));
  check(cpl_propertylist_set_comment(*cplist,XSH_QC_MASTER_RMS,XSH_QC_MASTER_RMS_C));

  cpl_image_accept_all(masterbias);

  /* Compute median-rms of the central part of the dark  */
  check(dark_med = cpl_image_get_median(masterbias));


  lower = dark_med - stdev * kappa_low;
  //upper = dark_med + stdev * kappa_low;
  xsh_msg_dbg_low("dark_med=%f lower=%f",dark_med,lower);


  check_msg(*bpmcold = cpl_mask_threshold_image_create(masterbias,
						      -FLT_MAX,lower),
	    "Cannot compute the cold pixel map");


  if(*bpmcold!=NULL) {
    check(*coldpix_nb = cpl_mask_count(*bpmcold));
  } else {
    *coldpix_nb=0;
  }
  check(cpl_propertylist_append_int(*cplist, XSH_QC_COLD_PIX_NUM,*coldpix_nb));
  check(cpl_propertylist_set_comment(*cplist,XSH_QC_COLD_PIX_NUM,XSH_QC_COLD_PIX_NUM_C));

/* Hot pixels */

  check(xsh_ksigma_clip(masterbias, 1, 1,
			cpl_image_get_size_x(masterbias),
			cpl_image_get_size_y(masterbias),
			kappa_high,
			high_niter, 1e-5,
			&mean, &stdev));



  check(cpl_propertylist_append_double(*hplist, XSH_QC_MASTER_MEAN,mean));
  check(cpl_propertylist_set_comment(*hplist,XSH_QC_MASTER_MEAN,XSH_QC_MASTER_MEAN_C));
  check(cpl_propertylist_append_double(*hplist,XSH_QC_MASTER_RMS,stdev));
  check(cpl_propertylist_set_comment(*hplist,XSH_QC_MASTER_RMS,XSH_QC_MASTER_RMS_C));

  cpl_image_accept_all(masterbias);


  /* Compute median-rms of the central part of the dark  */
  check(dark_med = cpl_image_get_median(masterbias));

  //lower = dark_med - stdev * kappa_high;
  upper = dark_med + stdev * kappa_high;
  xsh_msg_dbg_low("dark_med=%f upper=%f",dark_med,upper);
    
  check_msg(*bpmhot = cpl_mask_threshold_image_create(masterbias,
						      upper,DBL_MAX),
	    "Cannot compute the hot pixel map");

  if(*bpmhot!=NULL) {
    check(*hotpix_nb = cpl_mask_count(*bpmhot));
  } else {
    *hotpix_nb=0;
  }

  check(cpl_propertylist_append_int(*hplist,XSH_QC_HOT_PIX_NUM,*hotpix_nb));
  check(cpl_propertylist_set_comment(*hplist,XSH_QC_HOT_PIX_NUM,XSH_QC_HOT_PIX_NUM_C));


 cleanup:
  return cpl_error_get_code();

}



/*-------------------------------------------------------------------------*/
/*
 * @brief  Get cold bpixels mask
 * @param  master  Input image
 * @param  parameters recipe parameters
 * @param  frameset recipe frameset
 * @return CPL_ERROR_NONE on success.
 */
/*-------------------------------------------------------------------------*/
cpl_error_code
xsh_image_get_hot_cold_pixs(cpl_frame* frame_image, 
                            xsh_instrument* instrument,
                            const double ks_low,
                            const int cold_niter,
                            const double ks_high,
                            const int hot_niter,
                            cpl_frame** cpix_frm,
                            cpl_frame** hpix_frm)

{


  cpl_image* image=NULL;

  cpl_mask* bpmcold=NULL;
  cpl_mask* bpmhot=NULL;
  int hotpix_nb=0;
  int coldpix_nb=0;
  cpl_image* cpix_ima=NULL;
  cpl_image* hpix_ima=NULL;
  cpl_propertylist* cplist=NULL;
  cpl_propertylist* hplist=NULL;
  cpl_propertylist* plist=NULL;


  const char* cpix_pro_catg=NULL;
  const char* hpix_pro_catg=NULL;
  char* cpix_name=NULL;
  char* hpix_name=NULL;
  const char* name=NULL;

  check(name=cpl_frame_get_filename(frame_image));
  
  check(image=cpl_image_load(cpl_frame_get_filename(frame_image),
		       CPL_TYPE_FLOAT,0,0));


  check(hplist=cpl_propertylist_new());
  check(cplist=cpl_propertylist_new());
  xsh_msg("determine hot and cold pixels");
  check(xsh_image_get_hot_cold_maps(image, 
                                    ks_low,ks_high,cold_niter,hot_niter,
				    &bpmhot,&hotpix_nb,
                                    &bpmcold,&coldpix_nb,&hplist,&cplist));


  xsh_msg("Hot pix nb=%d Cold pix nb=%d",hotpix_nb,coldpix_nb);


  check(cpix_ima=cpl_image_new_from_mask(bpmcold));
  //check(cpl_image_cast(cpix_ima,CPL_TYPE_FLOAT));
  check(cpl_image_multiply_scalar(cpix_ima,QFLAG_CAMERA_DEFECT));


  //check(xsh_bpmap_bitwise_to_flag(cpix_ima,QFLAG_CAMERA_DEFECT));

  check(cpix_name= cpl_sprintf("%s_%s.fits",XSH_BP_MAP_CP,
			       xsh_instrument_arm_tostring(instrument)));


  check(plist=cpl_propertylist_load(name,0));
  check(cpl_propertylist_append(plist,cplist));
  cpl_image_save(cpix_ima,cpix_name,CPL_BPP_32_SIGNED,plist,CPL_IO_DEFAULT);
  xsh_free_propertylist(&plist);
  xsh_free_propertylist(&cplist);
  //xsh_add_temporary_file(cpix_name);


  check(cpix_pro_catg=XSH_GET_TAG_FROM_ARM(XSH_BP_MAP_CP,instrument));

  check(*cpix_frm=xsh_frame_product(cpix_name,cpix_pro_catg,
				    CPL_FRAME_TYPE_IMAGE,
				    CPL_FRAME_GROUP_PRODUCT,
				    CPL_FRAME_LEVEL_FINAL));


  check(hpix_ima=cpl_image_new_from_mask(bpmhot));
  //check(cpl_image_cast(hpix_ima,CPL_TYPE_FLOAT));
  check(cpl_image_multiply_scalar(hpix_ima,QFLAG_QUESTIONABLE_PIXEL));

  //check(xsh_bpmap_bitwise_to_flag(cpix_ima,QFLAG_CAMERA_DEFECT));


  check(hpix_name=cpl_sprintf("%s_%s.fits",XSH_BP_MAP_HP,
			      xsh_instrument_arm_tostring(instrument))) ;

  check(plist=cpl_propertylist_load(name,0));
  check(cpl_propertylist_append(plist,hplist));
  cpl_image_save(hpix_ima,hpix_name,CPL_BPP_32_SIGNED,plist,CPL_IO_DEFAULT);
  xsh_free_propertylist(&plist);
  xsh_free_propertylist(&hplist);
  //xsh_add_temporary_file(hpix_name);

  check(hpix_pro_catg=XSH_GET_TAG_FROM_ARM(XSH_BP_MAP_HP,instrument));
  check(*hpix_frm=xsh_frame_product(hpix_name,hpix_pro_catg,
                                   CPL_FRAME_TYPE_IMAGE,
                                   CPL_FRAME_GROUP_PRODUCT,
                                   CPL_FRAME_LEVEL_FINAL));

 cleanup:
    xsh_free_mask(&bpmcold);
    xsh_free_mask(&bpmhot);

    xsh_free_image(&image);
    xsh_free_image(&cpix_ima);
    xsh_free_image(&hpix_ima);
    xsh_free_propertylist(&cplist);
    xsh_free_propertylist(&hplist);
    xsh_free_propertylist(&plist);
    XSH_FREE(hpix_name);
    XSH_FREE(cpix_name);
  
    return cpl_error_get_code();

}
/*-------------------------------------------------------------------------*/
/*
 * @brief  Get cold bpixels mask
 * @param  master  Input image
 * @param  parameters recipe parameters
 * @param  frameset recipe frameset
 * @return mask frame on success.
 */
/*-------------------------------------------------------------------------*/
cpl_frame*
xsh_image_local_cold_pixs(cpl_image* ima, 
			  const double kappa,
                          const int r,
                          xsh_instrument* instr)

{

  int nx=0;
  int ny=0;
  int i=0;
  int j=0;
  double med=0;
  double rms=0;
  double* pima=NULL;
  double* pmsk=NULL;
  cpl_image* msk=NULL;
  char name[256];
  char tag[256];
  cpl_frame* msk_frm=NULL;

  nx=cpl_image_get_size_x(ima);
  ny=cpl_image_get_size_y(ima);
  msk=cpl_image_new(nx,ny,CPL_TYPE_DOUBLE);

  pima=cpl_image_get_data_double(ima);
  pmsk=cpl_image_get_data_double(msk);

  for(j=r;j<ny-r;j++) {
    for(i=r;i<nx-r;i++) {
      check(rms=cpl_image_get_stdev_window(ima,i-r+1,j-r+1,i+r,j+r));
      check(med=cpl_image_get_median_window(ima,i-r+1,j-r+1,i+r,j+r));
      if(pima[i+j*nx]< med- kappa*rms) {
	pmsk[i+j*nx]=QFLAG_LOW_QE_PIXEL;
      }
    }
  }

  sprintf(tag,"%s_%s",XSH_BP_MAP_DP,xsh_instrument_arm_tostring(instr));
  sprintf(name,"%s.fits",tag);
  check(cpl_image_save(msk,name,XSH_PRE_DATA_BPP,NULL,
		       CPL_IO_DEFAULT));

  check(msk_frm=xsh_frame_product(name,tag,CPL_FRAME_TYPE_IMAGE,
				  CPL_FRAME_GROUP_PRODUCT,
				  CPL_FRAME_LEVEL_FINAL));

 cleanup:
  return msk_frm; 

}

/*-------------------------------------------------------------------------*/
/*
 * @brief  Get hot bpixels mask
 * @param  master  Input image
 * @param  parameters recipe parameters
 * @param  frameset recipe frameset
 * @return mask frame on success.
 */
/*-------------------------------------------------------------------------*/
cpl_frame*
xsh_image_local_hot_pixs(cpl_image* ima, 
			 const double kappa,
                         const int r,
                         xsh_instrument* instr)

{

  int nx=0;
  int ny=0;
  int i=0;
  int j=0;
  double med=0;
  double rms=0;
  double* pima=NULL;
  double* pmsk=NULL;
  cpl_image* msk=NULL;
  char name[256];
  char tag[256];
  cpl_frame* msk_frm=NULL;

  nx=cpl_image_get_size_x(ima);
  ny=cpl_image_get_size_y(ima);
  msk=cpl_image_new(nx,ny,CPL_TYPE_DOUBLE);

  pima=cpl_image_get_data_double(ima);
  pmsk=cpl_image_get_data_double(msk);

  for(j=r;j<ny-r;j++) {
    for(i=r;i<nx-r;i++) {
      check(rms=cpl_image_get_stdev_window(ima,i-r+1,j-r+1,i+r,j+r));
      check(med=cpl_image_get_median_window(ima,i-r+1,j-r+1,i+r,j+r));
      if(pima[i+j*nx]> med+ kappa*rms) {
	pmsk[i+j*nx]=QFLAG_WELL_SATURATION;
      }
    }
  }

  sprintf(tag,"%s_%s",XSH_BP_MAP_SP,xsh_instrument_arm_tostring(instr));
  sprintf(name,"%s.fits",tag);
  check(cpl_image_save(msk,name,XSH_PRE_DATA_BPP,NULL,CPL_IO_DEFAULT));
  
  check(msk_frm=xsh_frame_product(name,tag,CPL_FRAME_TYPE_IMAGE, 
				  CPL_FRAME_GROUP_PRODUCT,
				  CPL_FRAME_LEVEL_FINAL));

 cleanup:
  return msk_frm; 

}


/*-------------------------------------------------------------------------*/
/*
 * @brief  Get cold bpixels mask
 * @param  master  Input image
 * @param  parameters recipe parameters
 * @param  frameset recipe frameset
 * @return CPL_ERROR_NONE on success.
 */
/*-------------------------------------------------------------------------*/
cpl_error_code
xsh_image_clean_mask_pixs(cpl_image** ima,cpl_image* msk,const int r)

{
  int nx=0;
  int ny=0;
  int i=0;
  int j=0;
  double med=0;
  double* pima=NULL;
  double* pmsk=NULL;

  nx=cpl_image_get_size_x(*ima);
  ny=cpl_image_get_size_y(*ima);

  pima=cpl_image_get_data_double(*ima);
  pmsk=cpl_image_get_data_double(msk);

  for(j=r;j<ny-r;j++) {
    for(i=r;i<nx-r;i++) {
      check(med=cpl_image_get_median_window(*ima,i-r+1,j-r+1,i+r,j+r));
      if(pmsk[i+j*nx]==1) {
	pima[i+j*nx]=med;
      }
    }
  }

 cleanup:
  return cpl_error_get_code(); 

}

static cpl_error_code
xsh_image_coadd(cpl_image **self, const cpl_image *add )
{
   int nx1=0;
   int ny1=0;   
   int nx2=0;
   int ny2=0;
   float* pself=NULL;
   const float* padd=NULL;
   int i=0;

   XSH_ASSURE_NOT_NULL( self ) ;
   XSH_ASSURE_NOT_NULL( add ) ;

   check(nx1=cpl_image_get_size_x(*self));
   check(ny1=cpl_image_get_size_y(*self));


   check(nx2=cpl_image_get_size_x(add));
   check(ny2=cpl_image_get_size_y(add));

   pself=cpl_image_get_data_float(*self);
   padd=cpl_image_get_data_float_const(add);

   if (nx1 != nx2 || ny1 != ny2) {
      xsh_msg("Input image of different size");
   }
   for(i=0;i<nx1*ny2;i++){
      if( (pself[i]==0) && (padd[i] !=0) ) {
         pself[i]+=padd[i];
      }
   }
  cleanup:
   return cpl_error_get_code();

}

cpl_image*
xsh_image_flag_bptype_with_crox(cpl_image* ima)

{


   cpl_image* shift=NULL;
   cpl_image* res=NULL;
   

   res=cpl_image_duplicate(ima);

   /* shift x+1 */
   shift=cpl_image_duplicate(ima);
   cpl_image_shift(shift,1,0);
   check(xsh_image_coadd(&res,shift));
   xsh_free_image(&shift);

   /* shift x-1 */
   shift=cpl_image_duplicate(ima);
   cpl_image_shift(shift,-1,0);
   check(xsh_image_coadd(&res,shift));
   xsh_free_image(&shift);

   /* shift y-1 */
   shift=cpl_image_duplicate(ima);
   cpl_image_shift(shift,0,-1);
   check(xsh_image_coadd(&res,shift));
   xsh_free_image(&shift);

   /* shift y+1 */
   shift=cpl_image_duplicate(ima);
   cpl_image_shift(shift,0,1);
   check(xsh_image_coadd(&res,shift));
   xsh_free_image(&shift);


 cleanup:
  return res;

}

cpl_error_code
xsh_count_crh(xsh_pre* pre, xsh_instrument* instr,const int datancom)
{
    int* pqual=NULL;
    int size;
    int ncrh=0;
    int i=0;

    float ncrh_avg=0;
    XSH_ASSURE_NOT_NULL_MSG(pre, "Null input pre frame");
    XSH_ASSURE_NOT_NULL_MSG(instr, "Null input pre frame");
    size=pre->nx*pre->ny;


    check(pqual=cpl_image_get_data_int(pre->qual));
    for(i=0;i<size;i++) {
        if(
            (QFLAG_COSMIC_RAY_REMOVED   & pqual[i]) > 0 ||
            (QFLAG_COSMIC_RAY_UNREMOVED & pqual[i]) > 0
            )
        {
          (ncrh)++;
        }
    }
    xsh_msg("ncrh=%d",ncrh);
    xsh_pfits_set_qc_ncrh(pre->data_header,ncrh);
    xsh_msg("datancom=%d",datancom);
    ncrh_avg=ncrh/datancom;
    xsh_msg("ncrh=%f",ncrh_avg);
    xsh_pfits_set_qc_ncrh_mean(pre->data_header,ncrh_avg);

    cleanup:
     return cpl_error_get_code();
}


cpl_error_code
xsh_count_satpix(xsh_pre* pre, xsh_instrument* instr,const int datancom)
{
    int* pqual=NULL;
    int size;
    int bp_code_sat=0;
    int nsat=0;
    int i=0;

    float nsat_avg=0;
    XSH_ASSURE_NOT_NULL_MSG(pre, "Null input pre frame");
    XSH_ASSURE_NOT_NULL_MSG(instr, "Null input pre frame");
    size=pre->nx*pre->ny;
    bp_code_sat=QFLAG_ADC_SATURATION;

    if(xsh_instrument_get_arm(instr) == XSH_ARM_NIR) {
        bp_code_sat=QFLAG_SATURATED_DATA;
    }

    check(pqual=cpl_image_get_data_int(pre->qual));
    for(i=0;i<size;i++) {
        if( (bp_code_sat & pqual[i]) > 0 ) {
          (nsat)++;
        }
    }
    xsh_msg("nsat=%d",nsat);
    xsh_pfits_set_total_nsat(pre->data_header,nsat);
    xsh_msg("datancom=%d",datancom);
    nsat_avg=nsat/datancom;
    nsat_avg=((float)nsat/size);
    xsh_msg("nsat=%f",nsat_avg);
    xsh_pfits_set_total_frac_sat(pre->data_header,nsat_avg);

    cleanup:
     return cpl_error_get_code();
}

cpl_error_code
xsh_badpixelmap_flag_saturated_pixels(xsh_pre* pre,xsh_instrument* instr,
                                      const double cor_val, const int flag,
									  const int is_flat, int* nsat)
{
    //int size=0;
    int i=0;
    float* pima=NULL;
    int* pqual=NULL;
    int bp_code_sat=0;
    int bp_code_neg=0;
    double thresh_max=0;
    //double time=0;
    float cpima=0;

    XSH_ASSURE_NOT_NULL_MSG(pre, "Null input pre frame");
    XSH_ASSURE_NOT_NULL_MSG(instr, "Null input pre frame");
    //size=pre->nx*pre->ny;
    /* define thresholds and corresponding codes */
    bp_code_sat=QFLAG_ADC_SATURATION;
    bp_code_neg=QFLAG_NEGATIVE_DATA;
    if(xsh_instrument_get_arm(instr) != XSH_ARM_NIR) {
        thresh_max=65000;
    } else {
        thresh_max=42000;
        bp_code_sat=QFLAG_SATURATED_DATA;
    }

    check(pima=cpl_image_get_data_float(pre->data));
    check(pqual=cpl_image_get_data_int(pre->qual));
    int nx=pre->nx;
    int ny=pre->ny;
    int ipix=0;
    int j=0;
    int jnx=0;
    //xsh_msg("nx=%d ny=%d",nx,ny);

    /* if the frame was overscan corrected we correct the threshold */
    thresh_max=thresh_max -cor_val;
    /* position of bottom left edge of NIR frame corresponding to K band */
    int knx=758;
    knx=1000;
    knx=nx; //removed special treatment of K band: PIPE-7075
    if(flag) {
        double thresh_min=1-cor_val;
        for(j=0;j<ny;j++) {
            jnx=j*nx;
            for(i=0;i<nx;i++) {
                ipix=i+jnx;
                //for(ipix=0;ipix<size;ipix++) {
                cpima=pima[ipix];
                //xsh_msg("ima val=%g",cpima);
                if( pima[ipix] > thresh_max) {
                    pqual[ipix] |= bp_code_sat;
                    (*nsat)++;
                }
                if  ( cpima < thresh_min  ) {
                    pqual[ipix] |= bp_code_neg;
                }
            }
        }
    } else {
        for(j=0;j<ny;j++) {
            jnx=j*nx;
            for(i=0;i<knx;i++) {
                ipix=i+jnx;
                if( pima[ipix] > thresh_max) {
                    (*nsat)++;
                }
            }
        }
    }


    cleanup:
    return cpl_error_get_code();
}

cpl_error_code
xsh_badpixelmap_count_range_pixels(xsh_pre* pre,
				   const double thresh_min, 
				   const double thresh_max,
                                   const double cor_val, 
                                   int* nrange, 
                                   double* frange)
{
   float* pdata=NULL;
   int i=0;
   int size=pre->nx*pre->ny;

   /* Flag saturated pixels for QC */
   pdata=cpl_image_get_data_float(pre->data);
   for(i=0;i<size;i++) {
     if(pdata[i]>=(thresh_min-cor_val) && 
        pdata[i]<=(thresh_max-cor_val)) {
         (*nrange)++;
     }
   }
   *frange=(double)(*nrange)/(size);
 return cpl_error_get_code();

}

cpl_error_code
xsh_badpixelmap_count_sat_pixels(xsh_pre* pre,const double sat_thresh, 
                                 const double cor_val, int* nsat, 
                                 double* frac_sat)
{
   float* pdata=NULL;
   int i=0;
   int size=pre->nx*pre->ny;

   /* Flag saturated pixels for QC */
   pdata=cpl_image_get_data_float(pre->data);
   for(i=0;i<size;i++) {
     if(pdata[i]>=(sat_thresh-cor_val) || pdata[i]==-cor_val) (*nsat)++;
   }
   *frac_sat=(double)(*nsat)/(size);
 return cpl_error_get_code();

}


cpl_error_code
xsh_badpixelmap_fill_bp_pattern_holes(cpl_frame* frm)
{
   const char* name=NULL;
   cpl_image* dima=NULL;
   cpl_image* eima=NULL;
   cpl_image* qima=NULL;
   cpl_propertylist* phead=NULL;
   cpl_propertylist* ehead=NULL;
   cpl_propertylist* qhead=NULL;
   int* pima=NULL;
   int npix=0;
   int i=0;
   int j=0;

   int nx=0;
   int ny=0;
   int j_nx=0;
   int j_nx_i=0;

   name=cpl_frame_get_filename(frm);
   dima=cpl_image_load(name,XSH_PRE_DATA_TYPE,0,0);
   eima=cpl_image_load(name,XSH_PRE_ERRS_TYPE,0,1);
   qima=cpl_image_load(name,XSH_PRE_QUAL_TYPE,0,2);
   phead=cpl_propertylist_load(name,0);
   ehead=cpl_propertylist_load(name,1);
   qhead=cpl_propertylist_load(name,2);
   pima=cpl_image_get_data_int(qima);
   nx=cpl_image_get_size_x(qima);
   ny=cpl_image_get_size_y(qima);
   if(cpl_propertylist_has(phead,XSH_QC_NHPIX)) {
     npix=xsh_pfits_get_qc_nhpix(phead);
   }


   for(j=1;j<ny-1;j++){
      j_nx = j*nx;
      for(i=1;i<nx-1;i++){
         j_nx_i = j_nx + i;
         if ((pima[j_nx_i] & QFLAG_ELECTRONIC_PICKUP) > 0) {continue;};
         if ((pima[j_nx_i - 1] & QFLAG_ELECTRONIC_PICKUP) == 0) {continue;};
         if ((pima[j_nx_i + 1] & QFLAG_ELECTRONIC_PICKUP) == 0) {continue;};
         if ((pima[j_nx_i - nx] & QFLAG_ELECTRONIC_PICKUP) == 0) {continue;};
         if ((pima[j_nx_i + nx] & QFLAG_ELECTRONIC_PICKUP) == 0) {continue;};
         pima[j_nx_i]  |= QFLAG_ELECTRONIC_PICKUP;
         npix++;
         //xsh_msg("i=%d j=%d",i,j);
      }
   } 

   xsh_pfits_set_qc_nhpix(phead,npix);
   xsh_pfits_set_qc_noisepix(phead,npix);
 
   cpl_image_save(dima,name,XSH_PRE_DATA_BPP,phead,CPL_IO_DEFAULT);
   cpl_image_save(eima,name,XSH_PRE_ERRS_BPP,ehead,CPL_IO_EXTEND);
   cpl_image_save(qima,name,XSH_PRE_QUAL_BPP,qhead,CPL_IO_EXTEND);


   xsh_free_image(&dima);
   xsh_free_image(&eima);
   xsh_free_image(&qima);
   xsh_free_propertylist(&phead);
   xsh_free_propertylist(&ehead);
   xsh_free_propertylist(&qhead);

  return cpl_error_get_code();
}
/*---------------------------------------------------------------------------*/
/**
  @brief 
    This function create a sub bad pixel map frame from input bad pixel map 
    frame. the sub frame is described by a box.

  @param[in] frame
    A bad pixel map frame
  @param[in] xmin    
    The x coordinate of the low-left corner of the box
  @param[in] ymin
    The y coordinate of the low-left corner of the box
  @param[in] xmax    
    The x coordinate of the up-right corner of the box
  @param[in] ymax
    The y coordinate of the up-right corner of the box
  @param[in] name
    The name of the sub frame
  @param[in] instr 
    Instrument containing the arm , mode and lamp in use

*/

cpl_frame* xsh_badpixelmap_extract(cpl_frame* frame, int xmin, int ymin, 
  int xmax, int ymax)
{
  cpl_image* ima=NULL;
  cpl_image* ext=NULL;
  cpl_propertylist* plist=NULL;
  const char* fname=NULL;
  cpl_frame* result=NULL;
  char name_o[256];

  XSH_ASSURE_NOT_NULL( frame);

  result=cpl_frame_duplicate(frame);
  fname=cpl_frame_get_filename(frame);
  plist=cpl_propertylist_load(fname,0);
  ima=cpl_image_load(fname, XSH_PRE_DATA_BPP, 0,0);
  ext=cpl_image_extract(ima,xmin,ymin,xmax,ymax);
  sprintf(name_o,"SUB_%s",fname);
  cpl_image_save(ext, name_o, XSH_PRE_DATA_BPP,plist, CPL_IO_DEFAULT);
  check( cpl_frame_set_filename( result, name_o));
  xsh_add_temporary_file(name_o);

  cleanup:
    if( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_free_frame( &result);
    } 
    xsh_free_image(&ima);
    xsh_free_image(&ext);
    xsh_free_propertylist(&plist);

    return result;

}

/**@}*/
