/* $Id: sinfo_rec_distortion.c,v 1.43 2013-09-17 08:13:17 amodigli Exp $
 *
 * This file is part of the SINFONI Pipeline
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: amodigli $
 * $Date: 2013-09-17 08:13:17 $
 * $Revision: 1.43 $
 * $Name: not supported by cvs2svn $
 */

/****************************************************************
 *          Distortions Frames Data Reduction                          *
 ****************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>          /* allows the program compilation */
#endif

/*-----------------------------------------------------------------------------
                                Includes
 ----------------------------------------------------------------------------*/
/* std */
#include <strings.h>
#include <string.h>

#include <stdio.h>
#include <math.h>

/* cpl */       
#include <cpl.h>       

/* irplib */
#include <irplib_utils.h>

#include <sinfo_hidden.h>
#include <sinfo_pro_types.h>
#include <sinfo_raw_types.h>
#include <sinfo_key_names.h>

#include <sinfo_globals.h>
#include <sinfo_general_config.h>
#include <sinfo_utilities.h>
#include <sinfo_product_config.h>
#include <sinfo_lamp_flats_config.h>
#include <sinfo_bp_config.h>
#include <sinfo_bp_dist_config.h>
#include <sinfo_prepare_stacked_frames_config.h>

#include <sinfo_north_south_test_config.h>
#include <sinfo_distortion_config.h>
#include <sinfo_new_lamp_flats.h>
#include <sinfo_bp_norm.h>
#include <sinfo_new_prepare_stacked_frames.h>
#include <sinfo_new_find_distortions.h>
#include <sinfo_new_nst.h>

#include <sinfo_functions.h>
#include <sinfo_tpl_utils.h>
#include <sinfo_tpl_dfs.h>
#include <sinfo_msg.h>
#include <sinfo_error.h>
#include <sinfo_utils_wrappers.h>



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

static int sinfo_rec_distortion_create(cpl_plugin *plugin);
static int sinfo_rec_distortion_exec(cpl_plugin *plugin);
static int sinfo_rec_distortion_destroy(cpl_plugin *plugin);
static int sinfo_rec_distortion(cpl_parameterlist *config, cpl_frameset *set);
static int new_pre_process(cpl_frameset* set, 
                           const float lo_rej, const float hi_rej, const char* name_o);

/*-----------------------------------------------------------------------------
                            Static variables
 ----------------------------------------------------------------------------*/
static char sinfo_rec_distortion_description1[] =
                "This recipe determines the optical distortions and the slitlets distances.\n"
                "Necessary input are:\n"
                "Several (usually 75) raw frames classified as FIBRE_NS\n"
                "Standard (an 'ON' and an 'OFF') flat frames having classified as FLAT_NS\n"
                "Standard (an 'ON' and an 'OFF') arc lamp frames having classified as WAVE_NS\n"
                "A corresponding (band) reference arc line list classified as REF_LINE_ARC\n"
                "A reference table with data reduction parameters classified as DRS_SETUP_WAVE.\n";


static char sinfo_rec_distortion_description2[] =
                "Default output are (with their PRO.CATG)\n"
                "A master flat: MASTER_FLAT_LAMP\n"
                "A Bad pixel map: BP_MAP_DI\n"
                "A fake-off fibre stacked frame: FIBRE_NS_STACKED_OFF\n"
                "A fake-on fibre  stacked frame: FIBRE_NS_STACKED_ON\n"
                "A fake on-off fibre  stacked frame: FIBRE_NS_STACKED\n"
                "A table with optical distortion coefficients: DISTORTION\n";


static char sinfo_rec_distortion_description3[] =
                "A distortion corrected frame: FIBRE_NS_STACKED_DIST\n"
                "A temporary frame: MASTER_SLIT\n"
                "A table with the slitlets distances: SLITLETS_DISTANCE\n"
                "\n";

static char sinfo_rec_distortion_description[1300];

/*-----------------------------------------------------------------------------
                                Functions code
 ----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
 * @defgroup sinfo_rec_distortion   Recipe to compute optical distortions
 */
/*---------------------------------------------------------------------------*/
/**@{*/
/*---------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

  Create the recipe instance and make it available to the application using the 
  interface. This function is exported.
 */
/*---------------------------------------------------------------------------*/
int
cpl_plugin_get_info(cpl_pluginlist *list)
{

    cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
    cpl_plugin *plugin = &recipe->interface;
    strcpy(sinfo_rec_distortion_description,sinfo_rec_distortion_description1);
    strcat(sinfo_rec_distortion_description,sinfo_rec_distortion_description2);
    strcat(sinfo_rec_distortion_description,sinfo_rec_distortion_description3);


    cpl_plugin_init(plugin,
                    CPL_PLUGIN_API,
                    SINFONI_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "sinfo_rec_distortion",
                    "Finds optical distortions and slitlets distances",
                    sinfo_rec_distortion_description,
                    "Andrea Modigliani",
                    "Andrea.Modigliani@eso.org",
                    sinfo_get_license(),
                    sinfo_rec_distortion_create,
                    sinfo_rec_distortion_exec,
                    sinfo_rec_distortion_destroy);

    cpl_pluginlist_append(list, plugin);

    return 0;

}


/*---------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Defining the command-line/configuration parameters for the recipe.
 */
/*---------------------------------------------------------------------------*/
static int sinfo_rec_distortion_create(cpl_plugin *plugin)
{
    cpl_recipe      * recipe ;

    /* Check that the plugin is part of a valid recipe */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;
    cpl_error_reset();
    irplib_reset();

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new() ; 


    /*
     * Fill the parameter list.
     */


    /* Output file name */
    sinfo_general_config_add(recipe->parameters);
    sinfo_product_config_add(recipe->parameters);
    sinfo_lamp_flats_config_add(recipe->parameters);
    sinfo_bp_config_add(recipe->parameters);
    sinfo_bp_dist_config_add(recipe->parameters);
    sinfo_prepare_stacked_frames_config_add(recipe->parameters);
    sinfo_distortion_config_add(recipe->parameters);
    sinfo_north_south_test_config_add(recipe->parameters);


    return 0;

}
/*---------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
static int sinfo_rec_distortion_exec(cpl_plugin *plugin)
{
    cpl_recipe  *   recipe ;
    int status=0;

    cpl_errorstate initial_errorstate = cpl_errorstate_get();

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;
    status=sinfo_rec_distortion(recipe->parameters, recipe->frames);

    if (!cpl_errorstate_is_equal(initial_errorstate)) {                      
        /* Dump the error history since recipe execution start.                
           At this point the recipe cannot recover from the error */           
        cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);              
    } 
    return status;

}

/*---------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
static int sinfo_rec_distortion_destroy(cpl_plugin *plugin)
{
    cpl_recipe  *   recipe ;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;

    cpl_parameterlist_delete(recipe->parameters);

    return 0;

}

/*---------------------------------------------------------------------------*/
/**
  @brief    Interpret the command line options and execute the data processing
  @param    parlist     the parameters list
  @param    frameset   the frames list
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
/*
 * The actual recipe actually start here.
 */

static int
sinfo_rec_distortion(cpl_parameterlist *config, cpl_frameset *set)
{

    cpl_parameter *p=NULL;
    cpl_frame* frame=NULL;
    cpl_propertylist* plist=NULL;
    cpl_image* ima=NULL;
    cpl_frameset* raw_set=NULL;
    cpl_frameset* set_off=NULL;
    cpl_frameset* set_on=NULL;
    cpl_frameset* set_fibre_ns=NULL;
    cpl_frameset* set_flat_ns=NULL;
    cpl_frameset* set_wave_ns=NULL;
    char file_name[FILE_NAME_SZ];

    fake* fk=sinfo_fake_new();
    int pdensity=0;
    int line_cor=0;

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


    sinfo_msg("Welcome to SINFONI Pipeline release %d.%d.%d",
              SINFONI_MAJOR_VERSION,SINFONI_MINOR_VERSION,SINFONI_MICRO_VERSION);
    if(sinfo_dfs_set_groups(set)) {
        sinfo_msg_error("Cannot indentify RAW and CALIB frames") ;
        return -1;
    }

    check_nomsg(p=cpl_parameterlist_find(config, "sinfoni.general.lc_sw"));
    check_nomsg(line_cor=cpl_parameter_get_bool(p));
    if(line_cor==1) {
        check_nomsg(sinfo_ima_line_cor(config,set));
    }

    //cknull_nomsg(set_fibre_ns=sinfo_frameset_extract(set,RAW_FIBRE_NS));
    set_fibre_ns=cpl_frameset_duplicate(set);
    cknull_nomsg(set_flat_ns=sinfo_frameset_extract(set,RAW_FLAT_NS));
    cknull_nomsg(set_wave_ns=sinfo_frameset_extract(set,RAW_WAVE_NS));
    check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.bp.method"));
    check_nomsg(cpl_parameter_set_string(p,"Normal"));
    check_nomsg(raw_set=cpl_frameset_duplicate(set));

    sinfo_msg("-----------------------------");
    sinfo_msg("  DETERMINE MASTER_LAMP_NS ");
    sinfo_msg("-----------------------------");

    sinfo_msg("REDUCE LAMPFLAT") ;
    ck0(sinfo_new_lamp_flats(cpl_func,config, set,set_flat_ns ),
        "reducing lampflats") ;
    sinfo_msg("SUCCES: DETERMINATION MASTER_LAMP_NS") ;

    sinfo_msg("-----------------------------");
    sinfo_msg("  DETERMINE BP_MAP_DI        ");
    sinfo_msg("-----------------------------");

    check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.bp.method"));
    check_nomsg(cpl_parameter_set_string(p,"Normal"));

    ck0(sinfo_new_bp_search_normal(cpl_func,config,set,set_flat_ns,
                    PRO_BP_MAP_DI),
        "determining %s",PRO_BP_MAP_DI);
    sinfo_msg("SUCCESS DETERMINATION %s",PRO_BP_MAP_DI);
    /*
       ---------------------------------------------------------
        pre iteration: get a collassed frame 
       ---------------------------------------------------------
     */

    check_nomsg(set_off=cpl_frameset_duplicate(raw_set));
    ck0_nomsg(new_pre_process(set_off, 0.0, 0.2,"out_fibre_off.fits"));


    /* add the off artificial frame to the frameset */
    if(NULL != cpl_frameset_find(set_off,RAW_FIBRE_NS)) {
        frame = cpl_frameset_find(set_off,RAW_FIBRE_NS);
        cpl_frameset_erase(set,RAW_FIBRE_NS);
        cpl_frameset_insert(set,cpl_frame_duplicate(frame));
    } else {
        sinfo_msg_error("Frame %s not found!", RAW_FIBRE_NS);
        goto cleanup;
    }


    /*
       ---------------------------------------------------------
        1st iteration: get off frame 
       ---------------------------------------------------------
     */

    sinfo_msg("STACK FIBRE,NS TO GET FAKE OFF\n") ;
    strcpy(fk->pro_class,RAW_FIBRE_NS);
    fk->frm_switch=1;
    fk->mask_index=0;
    fk->ind_index=0;
    fk->flat_index=0;
    fk->wfix_index=0;
    fk->low_rej=0.0;
    fk->hig_rej=0.2;


    ck0(sinfo_new_prepare_stacked_frames(cpl_func,config, set, set_fibre_ns,
                    PRO_FIBRE_NS_STACKED_OFF,0,fk),
        "Error stacking frame %s",PRO_FIBRE_NS_STACKED_OFF);

    strcpy(file_name,"out_fibre_off.fits");
    check_nomsg(ima=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0));
    sinfo_free_frameset(&set_off);

    cknull(plist = cpl_propertylist_load(file_name, 0),
           "getting header from reference ima frame %s",file_name);

    if (cpl_propertylist_has(plist, KEY_NAME_LAMP_HALO)) {
        cpl_propertylist_set_bool(plist, KEY_NAME_LAMP_HALO, LAMP_OFF);
    } else {
        cpl_propertylist_append_bool(plist, KEY_NAME_LAMP_HALO,LAMP_OFF);
    }

    sinfo_free_propertylist(&plist);
    sinfo_free_image(&ima);
    sinfo_msg("SUCCESS: STACKED FIBRE,NS TO GET FAKE OFF\n") ;

    /*
       ---------------------------------------------------------
        2nd iteration: get on frame 
       ---------------------------------------------------------
     */

    check_nomsg(set_on=cpl_frameset_duplicate(raw_set));

    ck0_nomsg(new_pre_process(set_on, 0.0, 0.0,"out_fibre_on.fits"));

    if(NULL != cpl_frameset_find(set_on,RAW_FIBRE_NS)) {
        frame = cpl_frameset_find(set_on,RAW_FIBRE_NS);
        cpl_frameset_erase(set,RAW_FIBRE_NS);
        cpl_frameset_insert(set,cpl_frame_duplicate(frame));
    } else {
        sinfo_msg_error("Frame %s not found!", RAW_FIBRE_NS);
        goto cleanup;
    }


    sinfo_msg("STACK FIBRE,NS TO GET FAKE ON\n") ;

    strcpy(fk->pro_class,RAW_FIBRE_NS);
    fk->frm_switch=1;
    fk->mask_index=0;
    fk->ind_index=0;
    fk->flat_index=0;
    fk->wfix_index=0;
    fk->low_rej=0.0;
    fk->hig_rej=0.0;


    ck0(sinfo_new_prepare_stacked_frames(cpl_func,config, set, set_fibre_ns,
                    PRO_FIBRE_NS_STACKED_ON,0,fk),
        "error stacking frame %s",PRO_FIBRE_NS_STACKED_ON);
    sinfo_msg("SUCCESS DETERMINATION %s",PRO_FIBRE_NS_STACKED_ON) ;

    /* cpl_frameset_erase(set,RAW_FIBRE_NS); */
    sinfo_free_frameset(&set_on);

    /* here we have a problem with pipefile of following step product */
    /*
       ---------------------------------------------------------
                3rd iteration combines on and off fake frames
       ---------------------------------------------------------
     */

    sinfo_msg("COMBINES FAKE ON AND OFF\n") ;
    strcpy(fk->pro_class,PRO_FIBRE_NS_STACKED);
    fk->frm_switch=1;
    fk->mask_index=0;
    fk->ind_index=0;
    fk->flat_index=1;
    fk->wfix_index=0;
    fk->low_rej=0.0;
    fk->hig_rej=0.0;

    ck0(sinfo_new_prepare_stacked_frames(cpl_func,config, set, set_fibre_ns,
                    PRO_FIBRE_NS_STACKED,0,fk),
        "Stacking frame %s",PRO_FIBRE_NS_STACKED);
    sinfo_msg("SUCCESS DETERMINATION %s",PRO_FIBRE_NS_STACKED) ;

    /* cpl_frameset_erase(set,RAW_FIBRE_NS); */

    /*
       ---------------------------------------------------------
                STACK WAVECAL
       ---------------------------------------------------------
     */

    sinfo_msg("STACK on WAVE frame\n") ;
    strcpy(fk->pro_class,RAW_WAVE_NS);
    fk->frm_switch=1;
    fk->mask_index=0;
    fk->ind_index=0;
    fk->flat_index=1;
    fk->wfix_index=0;
    fk->low_rej=0.1;
    fk->hig_rej=0.1;

    sinfo_msg("STACK on WAVE frame\n") ;
    ck0(sinfo_new_prepare_stacked_frames(cpl_func,config, set, set_wave_ns,
                    PRO_WAVE_LAMP_STACKED,0,fk),
        "spacking frame %s",PRO_WAVE_LAMP_STACKED);
    sinfo_msg("SUCCESS DETERMINATION %s",PRO_WAVE_LAMP_STACKED) ;

    if(pdensity < 2) {
        cpl_frameset_erase(set,PRO_FIBRE_NS_STACKED_OFF);
        cpl_frameset_erase(set,PRO_FIBRE_NS_STACKED_ON);
        cpl_frameset_erase(set,PRO_MASTER_FLAT_LAMP);
    }
    /*
       ---------------------------------------------------------
                DISTORTIONS 
       ---------------------------------------------------------
     */

    sinfo_msg("COMPUTE DISTORTIONS\n") ;
    ck0(sinfo_new_find_distortions(cpl_func,config, set,set_fibre_ns),
        "computing distortions");


    if(NULL != cpl_frameset_find(set,PRO_FIBRE_NS_STACKED)) {
        frame = cpl_frameset_find(set,PRO_FIBRE_NS_STACKED);
        strcpy(file_name,cpl_frame_get_filename(frame));
    } else {
        sinfo_msg_error("Frame %s not found!", PRO_FIBRE_NS_STACKED);
        goto cleanup;
    }
    check_nomsg(ima=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0));

    check(plist = cpl_propertylist_load(file_name, 0),
          "getting header from reference ima frame %s",file_name);


    if (cpl_propertylist_has(plist, KEY_NAME_LAMP_HALO)) {
        cpl_propertylist_set_bool(plist, KEY_NAME_LAMP_HALO, LAMP_ON);
    } else {
        cpl_propertylist_append_bool(plist, KEY_NAME_LAMP_HALO,LAMP_ON);
    }


    /* Save the file */
    /*
    if (cpl_image_save(ima, file_name, CPL_BPP_IEEE_FLOAT,
                      plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
        sinfo_msg_error("Cannot save the product %s",file_name);
        goto cleanup;

    }
    */

    sinfo_free_image(&ima);
    sinfo_free_propertylist(&plist);
    sinfo_msg("SUCCESS: COMPUTED DISTORTIONS\n") ;

    /*
       ---------------------------------------------------------
       4th iteration: distort fake frame
       ---------------------------------------------------------
     */



    sinfo_msg("DISTORT FAKE FRAME\n") ;

    strcpy(fk->pro_class,PRO_FIBRE_NS_STACKED_DIST);
    fk->frm_switch=1;
    fk->mask_index=1;
    fk->ind_index=1;
    fk->flat_index=0;
    fk->wfix_index=1;
    fk->low_rej=0.0;
    fk->hig_rej=0.0;

    ck0(sinfo_new_prepare_stacked_frames(cpl_func,config, set, set_fibre_ns,
                    PRO_FIBRE_NS_STACKED_DIST,0,fk),
        "Stacking frame %s",PRO_FIBRE_NS_STACKED_DIST);

    sinfo_msg("SUCCESS: DISTORTED FAKE FRAME\n") ;



    if(NULL != cpl_frameset_find(set,PRO_FIBRE_NS_STACKED_DIST)) {
        frame = cpl_frameset_find(set,PRO_FIBRE_NS_STACKED_DIST);
        strcpy(file_name,cpl_frame_get_filename(frame));
    } else {
        sinfo_msg_error("Frame %s not found!", PRO_FIBRE_NS_STACKED_DIST);
        goto cleanup;
    }
    check_nomsg(ima=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0));
    check(plist = cpl_propertylist_load(file_name, 0),
          "getting header from reference ima frame %s",file_name);


    if (cpl_propertylist_has(plist, KEY_NAME_LAMP_HALO)) {
        cpl_propertylist_set_bool(plist, KEY_NAME_LAMP_HALO, LAMP_ON);
    } else {
        cpl_propertylist_append_bool(plist, KEY_NAME_LAMP_HALO,LAMP_ON);
    }

    /* Save the file */
    /*
      if (cpl_image_save(ima, file_name, CPL_BPP_IEEE_FLOAT, 
                         plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
       sinfo_msg_error("Cannot save the product %s",file_name);
       goto cleanup;
      }
     */
    sinfo_free_propertylist(&plist);
    sinfo_free_image(&ima);

    /*
       ---------------------------------------------------------
                               NST 
       ---------------------------------------------------------
     */

    if(pdensity < 2) {
        cpl_frameset_erase(set,PRO_BP_MAP_DI);
        cpl_frameset_erase(set,PRO_FIBRE_NS_STACKED);
        cpl_frameset_erase(set,PRO_WAVE_LAMP_STACKED);
    }


    sinfo_msg("RUN NORD SOUTH TEST\n") ;
    ck0(sinfo_new_nst(cpl_func,config, set,set_fibre_ns),
        "Running north south test");


    if(pdensity < 2) {
        cpl_frameset_erase(set,PRO_FIBRE_NS_STACKED_DIST);
    }

    sinfo_msg("SUCCESS: RUNNED NORD SUD TEST\n") ;
    sinfo_msg("SUCCESS: RECIPE\n") ;

    sinfo_free_frameset(&raw_set);
    sinfo_fake_delete(&fk);


    cleanup:

    sinfo_free_frameset(&set_on);
    sinfo_free_frameset(&set_fibre_ns);
    sinfo_free_frameset(&set_wave_ns);
    sinfo_free_frameset(&set_flat_ns);
    sinfo_free_image(&ima);
    sinfo_free_propertylist(&plist) ;
    sinfo_free_frameset(&set_off);
    sinfo_free_frameset(&raw_set);
    sinfo_free_frameset(&raw_set);
    sinfo_fake_delete(&fk);

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        return -1;
    } else {
        return 0;
    }


}



static int 
new_pre_process(cpl_frameset* set, 
                const float lo_rej, const float hi_rej, const char* name_o)
{

    int nf=0;
    int ng=10;


    int nt=0;

    int i=0;

    int lo_cut=0;
    int hi_cut=0;


    cpl_frame* frame=NULL;
    cpl_frame* pframe=NULL;
    cpl_frameset* sof=NULL;
    cpl_frameset* fibre_raw_set=NULL;
    char* file_name=NULL;

    cpl_propertylist* plist=NULL;

    cpl_imagelist* imset_tot=NULL;
    cpl_image** avg_img_list=NULL;

    cpl_image* img=NULL;

    cpl_image** img_list=NULL;


    sof = cpl_frameset_duplicate(set);
    fibre_raw_set = cpl_frameset_new();

    sinfo_contains_frames_kind(sof,fibre_raw_set,RAW_FIBRE_NS);

    nf = cpl_frameset_get_size(fibre_raw_set);
    if(nf < 1) {
        sinfo_msg_warning("No input frames in data set");
        sinfo_free_frameset(&sof);
        sinfo_free_frameset(&fibre_raw_set);
        return -1;
    }
    frame = cpl_frameset_get_frame(fibre_raw_set,0);
    file_name=(char*) cpl_frame_get_filename(frame);


    if ((plist = cpl_propertylist_load(file_name, 0)) == NULL) {
        sinfo_msg_error("getting header from  ima frame %s",file_name);
        return -1 ;
    }


    if( nf > ng) {

        sinfo_msg("Total raw frames nf=%d > max frm per group ng=%d",nf,ng);
        int ns = (nf+1)/ng;
        int nr = nf-ns*ng;
        imset_tot=cpl_imagelist_new();
        avg_img_list=cpl_malloc((ns+1) * sizeof(cpl_image *));

        int k=0;
        cpl_imagelist* imset_tmp=NULL;
        for (i=0;i<ns;i++) {
            sinfo_msg("iteration i=%d\n",i);
            imset_tmp=cpl_imagelist_new();
            img_list=cpl_malloc(ng * sizeof(cpl_image *));


            for (int j=0;j<ng;j++) {
                k=i*ng+j;
                frame = cpl_frameset_get_frame(fibre_raw_set,k);
                file_name=(char*)cpl_frame_get_filename(frame);
                cpl_image* img_tmp=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0);
                cpl_image* img_dup=cpl_image_duplicate(img_tmp);
                cpl_imagelist_set(imset_tmp,img_dup,j);
                cpl_image_delete(img_tmp);
            }


            nt=cpl_imagelist_get_size(imset_tmp);
            lo_cut=(floor)(lo_rej*nt+0.5);
            hi_cut=(floor)(hi_rej*nt+0.5);
            avg_img_list[i]=cpl_imagelist_collapse_minmax_create(imset_tmp,
                            lo_cut,hi_cut);
            cpl_imagelist_set(imset_tot,avg_img_list[i],i);
            cpl_imagelist_delete(imset_tmp);
            cpl_free(img_list);

        }

        if(ns*ng<nf) {
            imset_tmp=cpl_imagelist_new();
            img_list=cpl_malloc((nf-ns*ng) * sizeof(cpl_image *));
            for(i=0;i<nr;i++) {
                k=i+ns*ng;
                frame = cpl_frameset_get_frame(fibre_raw_set,k);
                file_name  = (char*) cpl_frame_get_filename(frame);

                img_list[i]=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0);
                cpl_imagelist_set(imset_tmp,img_list[i],i);
            }
            nt=cpl_imagelist_get_size(imset_tmp);
            lo_cut=(floor)(lo_rej*nt+0.5);
            hi_cut=(floor)(hi_rej*nt+0.5);

            avg_img_list[ns]=cpl_imagelist_collapse_minmax_create(imset_tmp,
                            lo_cut,hi_cut);
            cpl_imagelist_set(imset_tot,avg_img_list[ns],ns);

            cpl_free(img);
            cpl_imagelist_delete(imset_tmp);
            cpl_free(img_list);
        }

    } else {

        sinfo_msg("Total raw frames nf=%d < max frm per group ng=%d",nf,ng);
        imset_tot=cpl_imagelist_new();
        img_list=cpl_malloc(nf * sizeof(cpl_image *));
        for (i=0;i<nf;i++) {
            frame = cpl_frameset_get_frame(fibre_raw_set,i);
            file_name  = (char*) cpl_frame_get_filename(frame);
            img_list[i]=cpl_image_load(file_name,CPL_TYPE_FLOAT,0,0);
            cpl_imagelist_set(imset_tot,img_list[i],i);
        }
        cpl_free(img_list);
    }
    cpl_free(avg_img_list);

    cpl_frameset_delete(fibre_raw_set);
    nt=cpl_imagelist_get_size(imset_tot);
    lo_cut=(floor)(lo_rej*nt+0.5);
    hi_cut=(floor)(hi_rej*nt+0.5);

    if( (img = cpl_imagelist_collapse_minmax_create(imset_tot,
                    lo_cut,hi_cut)) == NULL) {
        sinfo_msg_error("Error code");
        sinfo_msg_error("%s", (char* ) cpl_error_get_message());
        cpl_imagelist_delete(imset_tot);
        cpl_frameset_delete(sof);
        cpl_propertylist_delete(plist) ;
        return -1;
    }

    if (cpl_image_save(img,name_o, CPL_BPP_IEEE_FLOAT,
                    plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
        sinfo_msg_error("Cannot save the product %s",name_o);
        cpl_imagelist_delete(imset_tot);
        cpl_frameset_delete(sof);
        cpl_propertylist_delete(plist) ;
        return -1 ;
    }
    cpl_imagelist_delete(imset_tot);
    cpl_frameset_erase(set,RAW_FIBRE_NS);

    /* Create product frame */
    pframe = cpl_frame_new();
    cpl_frame_set_filename(pframe, name_o) ;
    cpl_frame_set_tag(pframe, "FIBRE_NS") ;
    cpl_frame_set_type(pframe, CPL_FRAME_TYPE_IMAGE) ;
    cpl_frame_set_group(pframe, CPL_FRAME_GROUP_RAW) ;
    cpl_frame_set_level(pframe, CPL_FRAME_LEVEL_FINAL) ;
    if (cpl_error_get_code()) {
        sinfo_msg_error("Error while initialising the product frame") ;
        cpl_propertylist_delete(plist) ;
        cpl_frame_delete(pframe) ;
        cpl_image_delete(img) ;
        return -1 ;
    }


    /* Save the file */
    if (cpl_image_save(img, name_o, CPL_BPP_IEEE_FLOAT, plist,
                    CPL_IO_DEFAULT) != CPL_ERROR_NONE) {
        sinfo_msg_error("Could not save product");
        cpl_propertylist_delete(plist) ;
        cpl_frame_delete(pframe) ;
        cpl_image_delete(img) ;
        return -1 ;
    }
    cpl_propertylist_delete(plist) ;
    cpl_image_delete(img) ;

    /* Log the saved file in the input frameset */
    cpl_frameset_insert(set, pframe) ;
    cpl_frameset_delete(sof);

    return 0;
}
/**@}*/
