/* $Id: xsh_irplib_mkmaster.c,v 1.13 2012-06-13 09:25:32 amodigli Exp $
 *
 * This file is part of the irplib package
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: amodigli $
 * $Date: 2012-06-13 09:25:32 $
 * $Revision: 1.13 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include <math.h>

#include "xsh_irplib_mkmaster.h"
#include "xsh_utils_imagelist.h"
#include "xsh_pfits.h"
#include "xsh_ksigma_clip.h"
#include "xsh_msg.h"
/*----------------------------------------------------------------------------*/
/**
 * @defgroup irplib_calib   Functions for calibrations
 */
/*----------------------------------------------------------------------------*/

/**@{*/
/*---------------------------------------------------------------------------*/
/**
  @brief    find out the character string associated to the DIT keyword
            in a propertylist
  @param    plist propertylist 
  @return   dit value
 */
/*---------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/**
  @brief	Computes kappa-sigma clean mean (free bad pixels) for each input image of the input imagelist.
  @param	iml      input imagelist
  @param	kappa    value for kappa-sigma clip
  @param	nclip    Number of clipping iterations
  @param	tolerance tolerance on range between two successive clip iterations

  @return	vector with computed values for each image of the list

  The returned vector must be deallocated.
 */
/*--------------------------------------------------------------------------*/
cpl_vector *
xsh_irplib_imagelist_get_clean_mean_levels(const cpl_imagelist* iml,
                                       const double kappa, 
                                       const int nclip,
                                       const double tolerance)
{

   const cpl_image* img=NULL;
   int size=0;
   int i=0;
   cpl_vector* levels=NULL;
   double* pval=NULL;
   double mean=0;
   double stdev=0;


   cpl_error_ensure(iml != NULL, CPL_ERROR_NULL_INPUT, return(levels),
                    "Null input image list");
   cpl_error_ensure(kappa >= 0, CPL_ERROR_ILLEGAL_INPUT, return(levels), 
                    "Must be kappa>0");

   size=cpl_imagelist_get_size(iml);
   levels=cpl_vector_new(size);
   pval=cpl_vector_get_data(levels);

   for(i=0;i<size;i++) {
      img=cpl_imagelist_get_const(iml,i);
      xsh_ksigma_clip(img,1,1, cpl_image_get_size_x(img),
			 cpl_image_get_size_y(img),
			    nclip,kappa,tolerance,&mean,&stdev);
      //cpl_msg_info(cpl_func,"Ima %d mean level: %g",i+1,mean);
      pval[i]=mean;
   }


   return levels;
}

/*-------------------------------------------------------------------------*/
/**
  @brief  Subtract from input imagelist values specified in input vector.
  @param  iml      input imagelist
  @param  values   value to be subtracted
  @return corrected imagelist

 */
/*--------------------------------------------------------------------------*/
static cpl_error_code
irplib_imagelist_subtract_values(cpl_imagelist** iml, cpl_vector* values)
{

   cpl_image* img=NULL;
   int size=0;
   int i=0;
   double* pval=NULL;
  
   size=cpl_imagelist_get_size(*iml);
   pval=cpl_vector_get_data(values);

   for(i=0;i<size;i++) {
      img=cpl_imagelist_get(*iml,i);
      cpl_image_subtract_scalar(img,pval[i]);
      cpl_imagelist_set(*iml,img,i);
   }

   return cpl_error_get_code();
}
#if 0
/*---------------------------------------------------------------------------*/
/** 
 * @brief  Perform kappa-sigma clip.
   @author C. Izzo
   @param  values values to be checked
   @param  klow   kappa to clip too low level values
   @param  khigh  kappa to clip too high values
   @param  kiter  number of iterations

   @note   In first iteration a median is the reference value for robustness

 * @return 
 */
/*---------------------------------------------------------------------------*/
static double 
irplib_vector_ksigma(cpl_vector *values,
                     const double klow, const double khigh, int kiter)
{
   cpl_vector *accepted;
   double  mean  = 0.0;
   double  sigma = 0.0;
   double *data  = cpl_vector_get_data(values);
   int     n     = cpl_vector_get_size(values);
   int     ngood = n;
   int     count = 0;
   int     i;
 
   /*
    * At first iteration the mean is taken as the median, and the
    * standard deviation relative to this value is computed.
    */

   mean = cpl_vector_get_median(values);

   for (i = 0; i < n; i++) {
      sigma += (mean - data[i]) * (mean - data[i]);
   }
   sigma = sqrt(sigma / (n - 1));

   while (kiter) {
      count = 0;
      for (i = 0; i < ngood; i++) {
         if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
            data[count] = data[i];
            ++count;
         }
      }

      if (count == 0) // This cannot happen at first iteration.
         break;      // So we can break: we have already computed a mean.

      /*
       * The mean must be computed even if no element was rejected
       * (count == ngood), because at first iteration median instead
       * of mean was computed.
       */

      accepted = cpl_vector_wrap(count, data);
      mean = cpl_vector_get_mean(accepted);
      if(count>1) {
         sigma = cpl_vector_get_stdev(accepted);
      }
      cpl_vector_unwrap(accepted);

      if (count == ngood) {
         break;
      }
      ngood = count;
      --kiter;
   }

   return mean;
}
#endif
#if 0
/**
 * @brief
 *   Stack images using k-sigma clipping
 *
 * @param imlist      List of images to stack
 * @param klow        Number of sigmas for rejection of lowest values
 * @param khigh       Number of sigmas for rejection of highest values
 * @param kiter       Max number of iterations
 *
 * @return Stacked image.
 *
 * At the first iteration the value of sigma is computed relatively to
 * the median value of all pixels at a given image position. For the
 * next iterations the sigma is computed in the standard way. If
 * at some iteration all points would be rejected, the mean computed
 * at the previous iteration is returned.
 */

static cpl_image *
irplib_imagelist_ksigma_stack(const cpl_imagelist *imlist, 
                              double klow, double khigh, int kiter)
{
   int         ni, nx, ny, npix;
   cpl_image  *out_ima=NULL;
   cpl_imagelist  *loc_iml=NULL;
   float      *pout_ima=NULL;
   cpl_image  *image=NULL;
   const float     **data=NULL;
   double     *med=NULL;
   cpl_vector *time_line=NULL;
  
   double     *ptime_line=NULL;
   int         i, j;
   double mean_of_medians=0;

   cpl_error_ensure(imlist != NULL, CPL_ERROR_NULL_INPUT, return(out_ima),
                    "Null input image list");

   ni         = cpl_imagelist_get_size(imlist);
   loc_iml        = cpl_imagelist_duplicate(imlist);
   image      = cpl_imagelist_get(loc_iml, 0);
   nx         = cpl_image_get_size_x(image);
   ny         = cpl_image_get_size_y(image);
   npix       = nx * ny;

   out_ima    = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
   pout_ima   = cpl_image_get_data_float(out_ima);

   time_line  = cpl_vector_new(ni);
   
   ptime_line = cpl_vector_get_data(time_line);

   data = cpl_calloc(sizeof(float *), ni);
   med  = cpl_calloc(sizeof(double), ni);

   for (i = 0; i < ni; i++) {
      image = cpl_imagelist_get(loc_iml, i);
      med[i]=cpl_image_get_median(image);
      cpl_image_subtract_scalar(image,med[i]);
      data[i] = cpl_image_get_data_float(image);
      mean_of_medians+=med[i];

   }
   mean_of_medians/=ni;

   for (i = 0; i < npix; i++) {
      for (j = 0; j < ni; j++) {
         ptime_line[j] = data[j][i];
      }
      pout_ima[i] = irplib_vector_ksigma(time_line, klow, khigh, kiter); 
   }
 
   cpl_image_add_scalar(out_ima,mean_of_medians);

 
   cpl_free(data);
   cpl_free(med);
   cpl_vector_delete(time_line);
   cpl_imagelist_delete(loc_iml);

   return out_ima;

} 
#endif



/*-------------------------------------------------------------------------*/
/**
  @brief  Computes master frame by clean stack mean of the input imagelist.
  @param  images   input imagelist
  @param  kappa    value for kappa-sigma clip
  @param  nclip    Number of clipping iterations
  @param  tolerance tolerance on range between two successive clip iterations
  @param  klow   kappa value applied to kappa sigma clip low pix values
  @param  khigh   kappa value applied to kappa sigma clip low pix values

  @return master image 

  The returned image must be deallocated.
 */
/*--------------------------------------------------------------------------*/
cpl_image*
xsh_irplib_mkmaster_mean(cpl_imagelist* images,const double kappa, const int nclip,
    const double tolerance,const double klow,const double khigh)
{

   cpl_image* master=NULL;
   cpl_vector* levels=NULL;
   double mean=0;
   cpl_imagelist* iml=NULL;

   //cpl_msg_info(cpl_func,"method mean");
   iml=cpl_imagelist_duplicate(images);
   levels=xsh_irplib_imagelist_get_clean_mean_levels(iml,kappa,nclip,tolerance);
   mean=cpl_vector_get_mean(levels);
   //cpl_msg_info(cpl_func,"Master mean level: %g",mean);

   irplib_imagelist_subtract_values(&iml,levels);

   //master = irplib_imagelist_ksigma_stack(iml,klow,khigh,niter);
   master = cpl_imagelist_collapse_sigclip_create(iml,klow,khigh,.1,CPL_COLLAPSE_MEAN,NULL);
   cpl_image_add_scalar(master,mean);

   cpl_vector_delete(levels);
   cpl_imagelist_delete(iml);

   return master;

}



/*-------------------------------------------------------------------------*/
/**
  @brief  Computes master frame by clean stack median of the input imagelist.
  @param  images   input imagelist
  @param  kappa    value for kappa-sigma clip
  @param  nclip    Number of clipping iterations
  @param  tolerance tolerance on range between two successive clip iterations

  @return master image 

  The returned image must be deallocated.
 */
/*--------------------------------------------------------------------------*/
cpl_image* 
xsh_irplib_mkmaster_median(cpl_imagelist* images,const double kappa, const int nclip,
    const double tolerance)
{

   cpl_image* master=NULL;
   cpl_vector* levels=NULL;
   double mean=0;
   cpl_imagelist* iml=NULL;

   cpl_ensure(images != NULL, CPL_ERROR_NULL_INPUT,NULL);
   //cpl_msg_info(cpl_func,"method median");
   iml=cpl_imagelist_duplicate(images);
   levels=xsh_irplib_imagelist_get_clean_mean_levels(iml,kappa,nclip,tolerance);

   mean=cpl_vector_get_mean(levels);
   //cpl_msg_info(cpl_func,"Master mean level: %g",mean);
   irplib_imagelist_subtract_values(&iml,levels);
       
   //When DFS11855 is solved we can use cpl_imagelist_collapse_median_create
   //master = cpl_imagelist_collapse_median_create(iml);
   master = xsh_imagelist_collapse_median_create(iml);


   cpl_image_add_scalar(master,mean);
   cpl_vector_delete(levels);
   cpl_imagelist_delete(iml);

   return master;

}

cpl_imagelist*
xsh_irplib_mkmaster_dark_fill_imagelist(const cpl_imagelist* raw_images,
    cpl_propertylist** raw_headers, const cpl_image* master_bias,
    double* mean_exptime) {
  /* First process each input image and store the results in a
   new image list */

  cpl_imagelist* preproc_images = NULL;
  int i = 0;
  cpl_image* current_dark = NULL;
  double min_exptime = 0;
  double max_exptime = 0;

  preproc_images = cpl_imagelist_new();
  for (i = 0; i < cpl_imagelist_get_size(raw_images); i++) {
    double exposure_time = 0.0;
    const cpl_propertylist *current_header;

    current_dark = cpl_image_duplicate(cpl_imagelist_get_const(raw_images, i));
    current_header = raw_headers[i];

    /* Subtract master bias */
    if (master_bias != NULL) {
      /* cpl_msg_info(cpl_func, "Subtracting master bias"); */
      cpl_image_subtract(current_dark, master_bias);
    } else {
      /* cpl_msg_info(cpl_func, "Skipping bias subtraction"); */
    }

    exposure_time = xsh_pfits_get_exptime(current_header);
    /* Initialize/update min/max exposure time*/
    if (i == 0 || exposure_time < min_exptime) {
      min_exptime = exposure_time;
    }
    if (i == 0 || exposure_time > max_exptime) {
      max_exptime = exposure_time;
    }

    /* Do not normalize to unit exposure time */
    /*        If this is uncommented, then remember to also calculate the
     correct master dark exposure time below.
     irplib_msg("Normalizing from %f s to unit exposure time", exposure_time);
     check(cpl_image_divide_scalar(current_dark, exposure_time),
     "Error normalizing dark frame");   */

    /* Append to imagelist */
    cpl_imagelist_set(preproc_images, current_dark, i);

    /* Don't deallocate the image. It will be deallocated when
     the image list is deallocated */
    current_dark = NULL;
  }

  
  /* Check exposure times */
   cpl_msg_info(cpl_func,
       "Exposure times range from %e s to %e s (%e %% variation)", min_exptime,
       max_exptime, 100 * (max_exptime - min_exptime) / min_exptime);

   if ((max_exptime - min_exptime) / min_exptime > .001) {
     cpl_msg_warning(cpl_func, "Exposure times differ by %e %%",
         100 * (max_exptime - min_exptime) / min_exptime);
   }

   /* compute correct exposure time */
   *mean_exptime=0.5 * (max_exptime + min_exptime);
  return preproc_images;
}

/**@}*/

