/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*----------------------------------------------------------------------------

   File name    :       sinfo_bp_lin.c
   Author       :    J. Schreiber
   Created on   :    May 5, 2003
   Description  :    Different methods for searching for bad pixels
                        used in the recipe spiffi_badsearch

 ---------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*----------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
//#include <cpl_imagelist_basic.h>
#include <cpl.h>
#include "sinfo_bp_lin.h"
#include "sinfo_detlin.h"
#include "sinfo_detlin_ini_by_cpl.h"
#include "sinfo_pro_save.h"
#include "sinfo_pro_types.h"
#include "sinfo_functions.h"
#include "sinfo_hidden.h"
#include "sinfo_error.h"
#include "sinfo_utils_wrappers.h"


/**
 * @addtogroup sinfo_bad_pix_search Bad Pixel Search
 *
 * TBD
 */

/*----------------------------------------------------------------------------
                                Defines
 ---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
                             Function Definitions
 ---------------------------------------------------------------------------*/
/**
   @name sinfo_new_bp_search_lin
   @memo Search pixels which have a non linear response
   @param   plugin_id recipe name
   @param   config input parameters list
   @param   sof  input set of frames

   @returns integer (0 if it worked, -1 if it doesn't)
   @doc
   this function searches for static bad pixels in stacks of flatfield frames
   with in/decreasing intensities. For each pixel position a curve is plotted
   of the pixel intensity in each plane against the clean mean of the whole
   plane.

   A polynomial is fit and the found coefficients are stored sequentially in
   a data cube. Then the deviation of the linear coefficients from the clean
   mean is computed for each pixel and the pixel are declared bad if the
   deviations exceed a threshold defined by a factor of the clean standard
   deviation.

   For the resting good pixels the non-linear coefficients are examined.
   If one coefficients deviate more than a user given threshold the pixel
   is also declared as bad. The data cubus with the fit results and
   a bad pixel mask image is stored

 */
int
sinfo_new_bp_search_lin(const char* plugin_id,
                        cpl_parameterlist* config,
                        cpl_frameset* sof)
{
  detlin_config * cfg=NULL;
  cpl_imagelist * imgl=NULL ;
  cpl_imagelist * coeffs_imgl=NULL;
  cpl_image * img_tmp=NULL ;
  cpl_image * mask=NULL;
  cpl_vector* cube_mean=NULL;
  cpl_frameset* raw=NULL;
  cpl_frameset* raw_on=NULL;
  cpl_frameset* raw_of=NULL;
  cpl_frame* frm=NULL;
  cpl_frame* frm_dup=NULL;

  cpl_table* qclog_tbl=NULL;

  cpl_parameter *p=NULL;
  cpl_polynomial *pol=NULL;
  cpl_vector* vec_adl=NULL;
  cpl_vector* vec_med=NULL;
  double* mse=NULL;
  int i=0;
  int n_bad=0 ;
  int nff=0;
  char key_value[FILE_NAME_SZ];
  char key_name[FILE_NAME_SZ];
  cpl_table* det_lin=NULL;
  int nraw=0;
  int* status=NULL;
  cpl_table* gain=NULL;
  int ngain=0;
  int sz_imgl=0;

  int llx=270;
  int lly=1030;
  int urx=310;
  int ury=1060;
  int zone[4];
  //int kappa=5;
  //int nclip=25;
  int pdensity=0;


  check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.product.density"));
  check_nomsg(pdensity=cpl_parameter_get_int(p));


  /* parse the file names and parameters to the bad_config
     data structure cfg */
  check_nomsg(raw=cpl_frameset_new());
  cknull(cfg = sinfo_parse_cpl_input_detlin(config,sof,&raw),
	 "could not parse .ini file!");

  /* ======================================================================
     DETERMINES LINEARITY COEFF AS DFO DOES
     ======================================================================
  */
  nff=cpl_frameset_get_size(raw);
  raw_on=cpl_frameset_new();
  raw_of=cpl_frameset_new();
  /* separates on and off frames */

  for(i=0;i<nff;i++) {
    frm=cpl_frameset_get_frame(raw,i);
    frm_dup=cpl_frame_duplicate(frm);
    if(sinfo_frame_is_on(frm)) {
      cpl_frameset_insert(raw_on,frm_dup);
    } else {
      cpl_frameset_insert(raw_of,frm_dup);
    }
  }
  /*
    cknull(det_lin=sinfo_get_linearity(raw_on,raw_of),
    "Error computing linearity");
  */
  zone[0]=llx;
  zone[1]=lly;
  zone[2]=urx;
  zone[3]=ury;

  //If one refers to a larger area
  zone[0]=20;
  zone[1]=2028;
  zone[2]=20;
  zone[3]=2028;

  /*
    cknull(det_lin=irplib_compute_linearity(raw_on,raw_of),
    "Error computing linearity");
  */

  if(pdensity > 1 ) {

    sinfo_msg("Computes linearity");
    cknull(det_lin=sinfo_compute_linearity(raw_on,raw_of),
	   "Error computing linearity");

    check_nomsg(nraw=cpl_table_get_nrow(det_lin));
    check_nomsg(vec_adl=cpl_vector_new(nraw));
    check_nomsg(vec_med=cpl_vector_new(nraw));

    for(i=0;i<nraw;i++) {
      cpl_vector_set(vec_adl,i,cpl_table_get_double(det_lin,"adl",i,status));
      cpl_vector_set(vec_med,i,cpl_table_get_double(det_lin,"med",i,status));
    }
    check_nomsg(pol=sinfo_polynomial_fit_1d_create(vec_adl,vec_med,
						 cfg->order,mse));
    sinfo_free_my_vector(&vec_adl);
    sinfo_free_my_vector(&vec_med);

    cknull_nomsg(qclog_tbl=sinfo_qclog_init());
    cpl_size deg=0;
    for(deg=0;deg<cfg->order+1;deg++) {

      snprintf(key_name,MAX_NAME_SIZE-1,"%s%" CPL_SIZE_FORMAT "%s","QC BP-MAP LIN",deg," MED");
      sinfo_qclog_add_double(qclog_tbl,key_name,
			     cpl_polynomial_get_coeff(pol,&deg),
			     "Linearity Polynomial Coeff","%g");
    }
    sinfo_free_polynomial(&pol);

    check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.bp.method"));
    snprintf(key_value, MAX_NAME_SIZE-1,"%s",cpl_parameter_get_string(p));
    ck0_nomsg(sinfo_qclog_add_string(qclog_tbl,"QC BP-MAP METHOD",key_value,
				     "BP search method","%s"));

    ck0(sinfo_pro_save_tbl(det_lin,raw,sof,BP_LIN_LIN_DET_INFO_OUT_FILENAME,
			   PRO_LIN_DET_INFO,qclog_tbl,plugin_id,config),
	"cannot dump ims %s",BP_LIN_LIN_DET_INFO_OUT_FILENAME);

    sinfo_free_table(&det_lin);
    sinfo_free_table(&qclog_tbl);
  }

  /*=======================================================*/
  if(pdensity > 1 ) {
    sinfo_msg("Computes gain");

    cknull(gain=sinfo_compute_gain(raw_on,raw_of),"Error computing gain");

    //cknull(gain=irplib_compute_gain(raw_on,raw_of,zone,kappa,nclip),
    //         "Error computing gain");

    sinfo_free_frameset(&raw_on);
    sinfo_free_frameset(&raw_of);

    check_nomsg(ngain=cpl_table_get_nrow(gain));
    cknull_nomsg(qclog_tbl=sinfo_qclog_init());

    for(i=0;i<ngain;i++) {
      snprintf(key_name,MAX_NAME_SIZE-1,"%s%i","QC GAIN",i);
      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,key_name,
				       cpl_table_get_double(gain,"gain",i,status),
				       "Detector gain","%g"));
    }

    ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC GAIN",
				     cpl_table_get_column_median(gain,"gain"),"Detector gain median","%g"));
    ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC GAINERR",
				     cpl_table_get_column_stdev(gain,"gain"),"Detector gain error","%g"));

    ck0(sinfo_pro_save_tbl(gain,raw,sof,BP_LIN_GAIN_OUT_FILENAME,PRO_GAIN_INFO,
			   qclog_tbl,plugin_id,config),
	"cannot dump tbl %s", BP_LIN_GAIN_OUT_FILENAME);

    sinfo_free_table(&gain);
    sinfo_free_table(&qclog_tbl);
  }

  /* =======================================================================
     DETERMINES LINEARITY COEFF AS MPE DOES
     =======================================================================
  */


  check_nomsg(imgl = cpl_imagelist_new());
  for ( i = 0 ; i < cfg->nframes ; i++ )
    {
      if(sinfo_is_fits_file (cfg->framelist[i]) != 1) {
	sinfo_msg_error("Input file %s is not FITS",cfg->framelist[i] );
	return -1;
      }
      check_nomsg(img_tmp=cpl_image_load(cfg->framelist[i],CPL_TYPE_FLOAT,0,0));
      check_nomsg(cpl_imagelist_set(imgl,img_tmp,i));
    }

  /*----------------------------------------------------------------
   *---------------------- SEARCH FOR BAD PIXELS---------------------
   *--------------------------------------------------------------*/
  sinfo_msg("Search for bad pixels");
  cknull(coeffs_imgl=sinfo_new_fit_intensity_course(imgl,
						    cfg->order,
						    cfg->loReject,
						    cfg->hiReject),
	 "could not fit polynomial and store coeffs in a data cube!");

  sinfo_free_imagelist(&imgl) ;
  check_nomsg(sz_imgl=cpl_imagelist_get_size(coeffs_imgl));


  /*---store the polynomial fit coefficients in a data cube----*/
  /* CUBE NOT DUMPED BECAUSE PROBLEMS ON LAST PLANE */
  cube_mean=cpl_vector_new(sz_imgl);

  /* QC LOG */
  cknull_nomsg(qclog_tbl=sinfo_qclog_init());
  for(i=0;i<sz_imgl;i++) {
    cpl_vector_set(cube_mean,i,
		   cpl_image_get_median(cpl_imagelist_get(coeffs_imgl,i)));

    snprintf(key_name,MAX_NAME_SIZE-1,"%s%i%s","QC BP-MAP LIN",i," MEAN");
    snprintf(key_value,MAX_NAME_SIZE-1,"%g",cpl_vector_get(cube_mean,i));
    ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,key_name,
				     cpl_vector_get(cube_mean,i),
				     "Linearity Polynomial Coeff","%g"));

  }
  sinfo_free_my_vector(&cube_mean);
  check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.bp.method"));
  snprintf(key_value, MAX_NAME_SIZE-1,"%s",cpl_parameter_get_string(p));
  ck0_nomsg(sinfo_qclog_add_string(qclog_tbl,"QC BP-MAP METHOD",key_value,
				   "BP search method","%s"));


  ck0(sinfo_pro_save_ims(coeffs_imgl,raw,sof,cfg->coeffsCubeName,
			 PRO_BP_COEFF,qclog_tbl,plugin_id,config),
      "cannot dump ims %s", cfg->coeffsCubeName);

  sinfo_free_table(&qclog_tbl);

  /* =======================================================================
     DETERMINES BAD PIXEL MAP
     =======================================================================
  */
  sinfo_msg("Generates bad pixel map");
  cknull(mask = sinfo_new_search_bad_pixels (coeffs_imgl,
					     cfg->threshSigmaFactor,
					     cfg->nonlinearThresh,
					     cfg->loReject,
					     cfg->hiReject),
	 "could not create bad pixel mask!") ;

  sinfo_free_imagelist(&coeffs_imgl) ;

  check_nomsg(n_bad = sinfo_new_count_bad_pixels(mask)) ;
  sinfo_msg( "No of bad pixels: %d", n_bad ) ;

  /* QC LOG */
  cknull_nomsg(qclog_tbl=sinfo_qclog_init());
  check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.bp.method"));
  snprintf(key_value, MAX_NAME_SIZE-1,"%s",cpl_parameter_get_string(p));
  ck0_nomsg(sinfo_qclog_add_string(qclog_tbl,"QC BP-MAP METHOD",key_value,
				   "BP search method","%s"));

  ck0_nomsg(sinfo_qclog_add_int(qclog_tbl,"QC BP-MAP NBADPIX",n_bad,
				"No of bad pixels","%d"));

  ck0(sinfo_pro_save_ima(mask,raw,sof,cfg->outName,PRO_BP_MAP_NL,qclog_tbl,
			 plugin_id,config),
      "cannot save ima %s", cfg->outName);


  /* ======================================================================
     FREE MEMORY
     ======================================================================
  */



  sinfo_free_table(&qclog_tbl);
  sinfo_free_image(&mask);
  sinfo_free_frameset(&raw);
  sinfo_detlin_free(&cfg);

  return 0;

 cleanup:
  sinfo_free_frameset(&raw_on);
  sinfo_free_frameset(&raw_of);

  sinfo_free_image(&mask) ;
  sinfo_free_my_vector(&cube_mean);
  sinfo_free_imagelist(&imgl);
  sinfo_free_imagelist(&coeffs_imgl);
  sinfo_free_table(&gain);
  sinfo_free_table(&qclog_tbl);
  sinfo_free_table(&det_lin);
  sinfo_free_my_vector(&vec_adl);
  sinfo_free_my_vector(&vec_med);
  sinfo_free_polynomial(&pol);
  sinfo_detlin_free(&cfg);
  sinfo_free_frameset(&raw);

  return -1;


}
