/*
 * 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;


}
