/*                                                                            *
 *   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: 2010-12-08 14:11:18 $
 * $Revision: 1.8 $
 * $Log: not supported by cvs2svn $
 * Revision 1.7  2010/09/24 09:32:07  amodigli
 * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
 *
 * Revision 1.5  2010/02/08 07:10:14  amodigli
 * replace cpl_image_filter_median/linear by uves_image_filter_median/linear
 *
 * Revision 1.4  2010/01/07 08:12:09  amodigli
 * saved old version
 *
 * Revision 1.2  2009/02/18 12:06:38  amodigli
 * fixe compiler warning
 *
 * Revision 1.1  2009/01/02 08:31:34  amodigli
 * copmmitted to CVS
 *
 *
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_remove_crh_single  Remove Cosmic Rays single (uves_remove_crh_single)
 * @ingroup drl_functions
 *
 * Suppress Cosmic Rays by analysing on files
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>

//#include <xsh_drl.h>
//#include <xsh_data_pre.h>
//#include <xsh_dfs.h>
//#include <xsh_pfits.h>
#include <uves_error.h>
#include <uves_msg.h>
#include <uves_utils_wrappers.h>
#include <cpl.h>
//#include <xsh_badpixelmap.h>
//#include <xsh_parameters.h>
//#include <xsh_data_localization.h>

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

/*-----------------------------------------------------------------------------
  Implementation
 -----------------------------------------------------------------------------*/

/* This should be defined in a more clever way, a parameter for example */
#define MAX_ITERATIONS 6


/** 
 * Remove cosmic rays from a single frame.
 * 
 * @param[in] sci_frame 
 *   The science Frame
 * @param[in] loc_table_frame
 *   The localization table frame
 * @param[in] single_par 
 *   Parameters for remove crh single
 * 
 * @return The science image after removal of Cosmics
 */
cpl_image * uves_remove_crh_single( cpl_image * sci_image,
				     double crh_frac_max,
				     double sigma_lim,
				     double f_lim,
				     int max_iter,
				     double gain,
				     double ron)
{
  int i,j,k,l,m;
  double  frac = 0. ;
  /* Only pointers */

  /* Need to be free */
  //xsh_localization_list * loc_list = NULL ;
  cpl_image* laplacian_image = NULL;
  cpl_image* laplacian_redu_image = NULL;
  cpl_image* two_sub_sample = NULL;
  cpl_image* sci_median5_image = NULL;
  cpl_image* noise_image = NULL;
  cpl_image* s_image = NULL;
  cpl_image* s_median_image = NULL;
  cpl_image* s2_image = NULL;
  cpl_image* sci_median3_image = NULL;
  cpl_image* sci_median3_7_image = NULL;
  cpl_image* f_image = NULL;
  cpl_image* r_image = NULL;
  int two_sub_sample_nx = 0;
  int two_sub_sample_ny = 0;
  /* Only pointers */
  float* sci_data = NULL;
  float* two_sub_sample_data = NULL;
  float* laplacian_data = NULL;
  float* laplacian_redu_data = NULL;
  float* sci_median5_data = NULL;
  float* sci_median3_data = NULL;
  float* sci_median3_7_data = NULL;
  float* noise_data = NULL;
  float* s_data = NULL;
  float* s_median_data = NULL;
  float* s2_data = NULL;
  float* f_data = NULL;
  float* r_data = NULL;
  /* Need to be free */
  float* cosmic_data = NULL; 
 
  cpl_matrix* laplacian_kernel = NULL;
  cpl_matrix* median3_kernel = NULL;
  cpl_matrix* median5_kernel = NULL;
  cpl_matrix* median7_kernel = NULL;
  int new_crh =1, nb_crh = 0;
  int nbiter = 1 ;
  cpl_vector* median = NULL;
  /* const char * tag = NULL ; */
  int nx=0;
  int ny=0;
  cpl_image* res_image=NULL;

  /* Check parameters */
  cknull( sci_image,"null input image" ) ; ;

  uves_msg( "Entering uves_remove_crh_single");
  uves_msg( "  Params: frac_max %.1f, sigma_lim %.2f f_lim %.2f, iter %d",
	   crh_frac_max, sigma_lim, f_lim, max_iter);

  /* Preparing different kernels */
  nx=cpl_image_get_size_x(sci_image);
  ny=cpl_image_get_size_y(sci_image);

  /* Laplacian */
  check_nomsg( laplacian_kernel = cpl_matrix_new(3,3));
  cpl_matrix_set( laplacian_kernel,0,0,0.0);
  cpl_matrix_set( laplacian_kernel,0,1,-1.0);
  cpl_matrix_set( laplacian_kernel,0,2,0.0);
  cpl_matrix_set( laplacian_kernel,1,0,-1.0);
  cpl_matrix_set( laplacian_kernel,1,1,4.0);
  cpl_matrix_set( laplacian_kernel,1,2,-1.0);
  cpl_matrix_set( laplacian_kernel,2,0,0.0);
  cpl_matrix_set( laplacian_kernel,2,1,-1.0);
  cpl_matrix_set( laplacian_kernel,2,2,0.0);
  cpl_matrix_divide_scalar( laplacian_kernel, 4.0); 	
/*
  cpl_matrix_set( laplacian_kernel,0,0,-1.0);
  cpl_matrix_set( laplacian_kernel,0,1,-1.0);
  cpl_matrix_set( laplacian_kernel,0,2,-1.0);
  cpl_matrix_set( laplacian_kernel,1,0,-1.0);
  cpl_matrix_set( laplacian_kernel,1,1,8.0);
  cpl_matrix_set( laplacian_kernel,1,2,-1.0);
  cpl_matrix_set( laplacian_kernel,2,0,-1.0);
  cpl_matrix_set( laplacian_kernel,2,1,-1.0);
  cpl_matrix_set( laplacian_kernel,2,2,-1.0);
  cpl_matrix_divide_scalar( laplacian_kernel, 8.0);
*/
  /* Median 3x3*/
  check_nomsg( median3_kernel = cpl_matrix_new(3,3));
  for(j=0; j< 3; j++){
    for(i=0; i< 3; i++){
      cpl_matrix_set( median3_kernel, i,j,1.0);
    }
  }

  /* Median 5x5 */
  check_nomsg( median5_kernel = cpl_matrix_new(5,5));
  for(j=0; j< 5; j++){
    for(i=0; i< 5; i++){
      cpl_matrix_set( median5_kernel, i,j,1.0);
    }
  }

  /* Median 7x7 */
  check_nomsg( median7_kernel = cpl_matrix_new(7,7));
  for(j=0; j< 7; j++){
    for(i=0; i< 7; i++){
      cpl_matrix_set( median7_kernel, i,j,1.0);
    }
  }

  check_nomsg (res_image = cpl_image_duplicate( sci_image));

  /* Allocate images and pointers */
  check_nomsg (sci_data = cpl_image_get_data_float( res_image));

  two_sub_sample_nx = nx*2;
  two_sub_sample_ny = ny*2;
  check_nomsg( two_sub_sample = cpl_image_new( two_sub_sample_nx,
    two_sub_sample_ny, CPL_TYPE_FLOAT));
  check_nomsg(two_sub_sample_data = cpl_image_get_data_float( two_sub_sample));
  check_nomsg( laplacian_redu_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg(laplacian_redu_data = cpl_image_get_data_float(
    laplacian_redu_image));
  check_nomsg( noise_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg( noise_data = cpl_image_get_data_float( noise_image));
  check_nomsg( s_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg( s_data = cpl_image_get_data_float( s_image));
  check_nomsg( s2_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg( s2_data = cpl_image_get_data_float( s2_image));
  check_nomsg( f_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg( f_data = cpl_image_get_data_float( f_image));
  check_nomsg( r_image = cpl_image_new(nx,ny, CPL_TYPE_FLOAT));
  check_nomsg( r_data = cpl_image_get_data_float( r_image));
  cosmic_data=cpl_calloc(nx*ny, sizeof(float));

  /* LGG - Added limit on frac_max AND limit on nb iterations */
  while( new_crh > 0 && frac < crh_frac_max && nbiter <= max_iter ){
    uves_msg("Iteration %d",nbiter );
    /* Create a 2n x 2n images like this 
        | 1 | 2 |  =>  | 1 | 1 | 2 | 2 |
        | 3 | 4 |      | 1 | 1 | 2 | 2 |
                       | 3 | 3 | 4 | 4 |
                       | 3 | 3 | 4 | 4 | */
    uves_msg_debug("Create a 2n images");
    for( j=0; j< ny; j++){
      for( i=0; i< nx; i++){
        float val = sci_data[i+j*nx];

	if ( val < 0. ) val = 0. ;
        two_sub_sample_data[i*2+j*2*two_sub_sample_nx] = val;
        two_sub_sample_data[i*2+1+j*2*two_sub_sample_nx] = val;
        two_sub_sample_data[i*2+(j*2+1)*two_sub_sample_nx] = val;
        two_sub_sample_data[i*2+1+(j*2+1)*two_sub_sample_nx] = val;
      }
    }
    uves_msg_debug("Doing laplacian convolution");
    /* Doing the laplacian convolution 
        0  -1   0
       -1   4  -1 
        0  -1   0 */
    check_nomsg(laplacian_image = uves_image_filter_linear( two_sub_sample, 
                                                            laplacian_kernel));

    /* multiply by two to normalize correctly the laplacian [RD5] 
       and filter negative values */
    uves_msg_debug("Normalize laplacian");
    check_nomsg (laplacian_data = cpl_image_get_data_float( laplacian_image));
    for ( i=0; i< two_sub_sample_nx*two_sub_sample_ny; i++){
      if (laplacian_data[i] > 0.0){
        laplacian_data[i] = 2.0 * laplacian_data[i]; 
      }
      else{
        laplacian_data[i] = 0.0;
      }
    }
    uves_msg_debug("Save Lpositive");
    cpl_image_save(laplacian_image, "Lpositive.fits", CPL_BPP_IEEE_FLOAT, NULL,
      CPL_IO_DEFAULT);

    /* resample to the original size 
       | 1 | 1 | 2 | 2 |    | 1 | 2 | 
       | 1 | 1 | 2 | 2 |    | 3 | 4 |
       | 3 | 3 | 4 | 4 | => 
       | 3 | 3 | 4 | 4 |               */

    uves_msg_debug("Resample to the original size");

    for( j=0; j< ny; j++){
      for( i=0; i< nx; i++){
        laplacian_redu_data[i+j*nx] = 
          (laplacian_data[i*2+j*2*two_sub_sample_nx]+
           laplacian_data[i*2+1+j*2*two_sub_sample_nx]+
           laplacian_data[i*2+(j*2+1)*two_sub_sample_nx]+
           laplacian_data[i*2+1+(j*2+1)*two_sub_sample_nx])/4.0;
      }
    }

    cpl_image_save(laplacian_redu_image, "Lplus.fits", CPL_BPP_IEEE_FLOAT, 
      NULL, CPL_IO_DEFAULT);

    uves_msg_debug("Apply median filter");
    /* Apply 5x5 median filter on data */
    check_nomsg( sci_median5_image = uves_image_filter_median( sci_image, 
      median5_kernel)); 
    check_nomsg (sci_median5_data = cpl_image_get_data_float( sci_median5_image));
 
    uves_msg_debug("Compute noise");
    /* computes the noise image */
    for( i=0; i< nx*ny; i++){
      noise_data[i] = sqrt(sci_median5_data[i]*gain+
        ron*ron)/ gain;
    }

    uves_msg_debug("Compute S");
    /* compute S image */
    for( i=0; i< nx*ny; i++){
      s_data[i] = laplacian_redu_data[i] / (2.0*noise_data[i]);
    }

    uves_msg_debug("Compute S median");
    /* compute S median image */
    check_nomsg( s_median_image = uves_image_filter_median( s_image,
      median5_kernel));
    check_nomsg( s_median_data = cpl_image_get_data_float( s_median_image));

    uves_msg_debug("Compute s2");
    /* compute s2 */
    for( i=0; i< nx*ny; i++){
      s2_data[i] = s_data[i] -s_median_data[i];
    }
 
       cpl_image_save( s2_image, "S2.fits", CPL_BPP_IEEE_FLOAT, NULL,
	 CPL_IO_DEFAULT);

    uves_msg_debug("Apply 3x3 filter");
    /* Apply 3x3 median filter on data */
    check_nomsg( sci_median3_image = uves_image_filter_median( sci_image,
      median3_kernel));

    uves_msg_debug("Apply 7x7 filter");
    /* Apply 7x7 median filter */ 
    check_nomsg( sci_median3_7_image = uves_image_filter_median( sci_median3_image,
      median7_kernel));
    uves_msg_debug("Apply 7x7 filter ok");
    check_nomsg ( sci_median3_data = cpl_image_get_data_float( sci_median3_image));
    check_nomsg ( sci_median3_7_data = cpl_image_get_data_float( 
      sci_median3_7_image));

    uves_msg_debug("Compute F");
    /* compute F */
    for( i=0; i< nx*ny; i++){
      f_data[i] = sci_median3_data[i] -sci_median3_7_data[i];
      if (f_data[i] < 0.01){
        f_data[i] = 0.01;
      }
    }
    cpl_image_save( f_image, "F.fits", CPL_BPP_IEEE_FLOAT, NULL,
      CPL_IO_DEFAULT);

    uves_msg_debug("Compute R");
    /* compute R */
    for( i=0; i< nx*ny; i++){
      r_data[i] = laplacian_redu_data[i]/f_data[i];
    }

    cpl_image_save( r_image, "R.fits", CPL_BPP_IEEE_FLOAT, NULL,
      CPL_IO_DEFAULT);

    /* Search for cosmics */

    uves_msg_debug("Search for cosmic");
    new_crh = 0;
    median = cpl_vector_new(24);

    for( j=1; j< ny-1; j++){
      double *data = NULL;
      cpl_vector* med_vect = NULL;

      for( i=1; i< nx-1; i++){
        if ( (s2_data[i+j*nx] >= sigma_lim) && 
          (r_data[i+j*nx] >= f_lim)){
          int li,lj,ui,uj;
          cosmic_data[i+j*nx] = 1.0;
          new_crh++;
          li = i-2;
          lj = j-2;
          ui = i+2;
          uj = j+2;
          m = 0;
          if (li < 0) li = 0;
          if (ui >= nx) ui = nx-1;
          if (lj < 0) lj = 0;
          if (uj >= ny) uj = ny-1;
          for( k=lj; k <= uj; k++){
            for( l=li; l <= ui; l++){
              //uves_msg("REGDEBUG k %d l %d m %d", k, l, m);
              if ( k < j){
                cpl_vector_set(median, m, sci_data[l+k*nx]);
                m++;
              }
              else if ( (k == j) && ( l < i)){
                 cpl_vector_set(median, m, sci_data[l+k*nx]);
                m++;
              }
              else if ( l!=i && k!=j && (s2_data[l+k*nx] < sigma_lim)
                && (r_data[l+k*nx] < f_lim)){
                cpl_vector_set(median, m, sci_data[l+k*nx]);
                m++;
              }
            }
          }
          check_nomsg( data = cpl_vector_get_data( median));
          uves_msg_debug("REGDEBUG i %d j %d m %d", i, j ,m);
          check_nomsg( med_vect = cpl_vector_wrap( m, data));
          check_nomsg( sci_data[i+j*nx] = cpl_vector_get_median( med_vect));
          cpl_vector_unwrap( med_vect);
        }
      }
    }
    uves_free_vector( &median ) ;
    nb_crh += new_crh;
    frac = (double)nb_crh/(double)(nx*ny) ;
    uves_msg("   new cosmics %d, total %d, frac %.4f [%d pixels]",new_crh,nb_crh,
	    frac, nx*ny);
    nbiter++;
    uves_free_image( &laplacian_image);
    uves_free_image( &sci_median3_7_image ) ;
    uves_free_image( &sci_median3_image ) ;
    uves_free_image( &s_median_image ) ;
    uves_free_image( &sci_median5_image ) ;
  }
  {
    FILE *debug = NULL;

    debug = fopen("cosmic.log","w");
    
    for( j=0; j< ny; j++){
      for( i=0; i< nx; i++){
        if ( cosmic_data[i+j*nx] == 1.0){
          fprintf(debug,"%.1f %.1f\n",i+1.0,j+1.0);
        }
      }
    }
    fclose(debug);
  }

  //check_nomsg( res_frame = cpl_frame_duplicate( sci_frame ) ) ;
  //uves_msg( "Saving Result Frame '%s'", res_name ) ;
  //check_nomsg( add_qc_crh( sci_pre, nb_crh, 1, instrument ) ) ;
  //check_nomsg( res_frame = xsh_pre_save( sci_pre, res_name, 1 ) ) ;
  //tag = cpl_frame_get_tag( in_sci_frame ) ;
  //check_nomsg( cpl_frame_set_tag( res_frame, tag ) ) ;


  cleanup:
  //xsh_pre_free( &sci_pre);
  //xsh_localization_list_free( &loc_list ) ;

    /* free kernel */
    uves_free_matrix( &laplacian_kernel);
    uves_free_matrix( &median3_kernel);
    uves_free_matrix( &median5_kernel);
    uves_free_matrix( &median7_kernel);
    /* free images */
    uves_free_image( &laplacian_image);
    uves_free_image( &laplacian_redu_image);
    uves_free_image( &two_sub_sample);
    uves_free_image( &sci_median5_image);
    uves_free_image( &noise_image);
    uves_free_image( &s_image);
    uves_free_image( &s_median_image); 
    uves_free_image( &s2_image);
    uves_free_image( &sci_median3_image);
    uves_free_image( &sci_median3_7_image);
    uves_free_image( &f_image);
    uves_free_image( &r_image);
    /* free vector */
    uves_free_vector( &median);
    /* free tab */
    if(cosmic_data!=NULL) cpl_free( cosmic_data);
    return res_image;
}
/**@}*/

