/*                                                                            *
 *   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();
}



/** 
 * 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 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 bp_code_sat=0;
    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, int flag, 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));

  /* if the frame was overscan corrected we correct the threshold */
  thresh_max=thresh_max -cor_val;

  if(flag) {
    double thresh_min=1-cor_val;
    for(i=0;i<size;i++) {
      cpima=pima[i];
      //xsh_msg("ima val=%g",cpima);
      if( pima[i] > thresh_max) {
	pqual[i] |= bp_code_sat;
	(*nsat)++;
      } 
      if  ( cpima < thresh_min  ) {
	pqual[i] |= bp_code_neg;
      }
    }
  } else {
    for(i=0;i<size;i++) {
      if( pima[i] > 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;

}

/**@}*/
