/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 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: 2013-08-08 13:36:46 $
 * $Revision: 1.85 $
 * $Name: not supported by cvs2svn $
 *
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <cpl.h>
/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_response  Recipe: Response
 *
 * This recipe calculates the response function and the quantum detection
 * efficiency.
 * See man-page for details.
 */
/*----------------------------------------------------------------------------*/

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

/* Definitions */
#include <uves.h>

/* Macro steps */
#include <uves_reduce.h>
#include <uves_reduce_utils.h>
#include <uves_response_efficiency.h>
#include <uves_response_utils.h>
#include <uves_star_index.h>
/* Utility functions */
#include <uves_extract.h>
#include <uves_plot.h>
#include <uves_dfs.h>
#include <uves_pfits.h>
#include <uves_parameters.h>
#include <uves_utils.h>
#include <uves_utils_wrappers.h>
#include <uves_utils_cpl.h>
#include <uves_qclog.h>
#include <uves_recipe.h>
#include <uves_error.h>
#include <uves_msg.h>
#include <uves_merge.h>

/* Library */
#include <cpl.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include <../hdrl/hdrl.h>
#define UVES_HDRL_USE_EXPERIMENTAL 0
#define UVES_RESPONSE_WLU 50.0


/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
static void uves_efficiency_qclog(cpl_table* table,
                  uves_propertylist* raw_header,
                  enum uves_chip chip,
                  cpl_table* qclog,
                  const char *ref_obj_name);

static int
uves_response_define_parameters(cpl_parameterlist *parameters);
const hdrl_spectrum1D_wave_scale
global_scale = hdrl_spectrum1D_wave_scale_linear;
/*-----------------------------------------------------------------------------
                            Recipe standard code
 -----------------------------------------------------------------------------*/
#define cpl_plugin_get_info uves_response_get_info
UVES_RECIPE_DEFINE(
    UVES_RESPONSE_ID, UVES_RESPONSE_DOM, uves_response_define_parameters,
    "Jonas M. Larsen", "cpl@eso.org",
    "Determines response function and quantum efficiency",
"This recipe reduces a standard star frame (STANDARD_xxx or STANDARD_xxx,\n"
"where xxx = BLUE, RED) using a combination (depending on recipe parameters\n"
"and provided input frames) of the steps:\n"
"  - bias subtraction,\n"
"  - dark subtraction,\n"
"  - background subtraction,\n"
"  - extraction/cosmic ray removal,\n"
"  - flat-field correction,\n"
"  - wavelength rebinning,\n"
"  - sky subtraction,\n"
"  - order merging.\n"
"\n"
" Expected input for this recipe is an raw std star frame, STANDARD_xxx or \n"
"order table(s) for each chip, ORDER_TABLE_xxxx (where xxxx=BLUE, REDL, REDU),\n"
"line table(s) for each chip, LINE_TABLE_xxxx, a master bias frame,\n"
"MASTER_BIAS_xxxx, a master flat, MASTER_FLAT_xxxx, a reference standard star\n"
"flux table, FLUX_STD_TABLE, a table describing the atmospheric extintion,\n"
"EXTCOEFF_TABLE, and the catalog indicating points to fit the response, \n"
"RESP_FIT_POINTS_CATALOG.\n"
"\n"
"Two reductions are performed, the first using optimal extraction (used to\n"
"compute the instrument response function), the second using linear extraction\n"
"(used to get the Quantum Detection Efficiency)\n"
"\n"
"For each chip (xxxx = BLUE, REDL, REDU) the recipe produces\n"
"  INSTR_RESPONSE_FINE_xxxx          Response curve\n"
"  RED_STD_xxxx                 Reduced spectrum\n"
"  EFFICIENCY_TABLE_xxxx        Efficiency table\n"
"  BKG_STD_xxxx                 The subtracted background\n");

/**@{*/
/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options
  @param    parameters        the parameterlist to fill
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int
uves_response_define_parameters(cpl_parameterlist *parameters)
{
    /*****************
     *    General    *
     *****************/

    if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
    {
        return -1;
    }

    /*******************
     *    Reduce       *
     ******************/
    /* Get reduce parameters for the top level and also for 'efficiency' substep */
    if (uves_propagate_parameters_step(
        UVES_REDUCE_ID, parameters, make_str(UVES_RESPONSE_ID), NULL) != 0)
    {
        return -1;
    }


    check(uves_define_efficiency_parameters(parameters),
          "Defining efficiency parameters");


  cleanup:
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}


static hdrl_spectrum1D *
uves_sci_frame_to_hdrl_spectrum1D(cpl_frame* frm_sci)
{
    cpl_ensure(frm_sci != NULL, CPL_ERROR_NULL_INPUT, NULL);
    hdrl_spectrum1D * sci_s1d;
    const char* name_sci=NULL;
    name_sci=cpl_frame_get_filename(frm_sci);
    cpl_table* sci_tab=NULL;
    sci_tab=cpl_table_load(name_sci,1,0);
    cpl_ensure(sci_tab != NULL, CPL_ERROR_NULL_INPUT, NULL);


    sci_s1d=hdrl_spectrum1D_convert_from_table(sci_tab, "flux",
                "wavelength", NULL, NULL, global_scale);
    uves_free_table(&sci_tab);
    return sci_s1d;

}

static hdrl_spectrum1Dlist *
uves_get_telluric_models(const cpl_frame * telluric_cat){

    cpl_ensure(telluric_cat != NULL, CPL_ERROR_NULL_INPUT, NULL);
    const char * cat_name = cpl_frame_get_filename(telluric_cat);
    const cpl_size next = cpl_frame_get_nextensions(telluric_cat);

    hdrl_spectrum1Dlist * list = hdrl_spectrum1Dlist_new();

    for(cpl_size i = 0; i < next; ++i){
        cpl_table * tab = cpl_table_load(cat_name, 1 + i, 1);

        hdrl_spectrum1D * s =
                hdrl_spectrum1D_convert_from_table(tab,
                        "flux",
                        "lam",
                        NULL,
                        NULL,
                        global_scale);
        cpl_table_delete(tab);
        hdrl_spectrum1Dlist_set(list, s, i);
    }

    return list;
}


/*---------------------------------------------------------------------------*/
/**
  @brief    load reference table

  @param    cat        input frame catalog
  @param    dRA        Right Ascension
  @param    dDEC       Declination
  @param    EPSILON    tolerance to find ref spectra on catalog on (ra,dec)
  @param    ptable     pointer to new table
  @return   Interpolated data points
 */
/*---------------------------------------------------------------------------*/

static void
uves_parse_catalog_std_stars(cpl_frame* cat,
                double dRA,
                double dDEC,
                double EPSILON,
                cpl_table** pptable)
{


  if (cat != NULL) {

      const char* name = cpl_frame_get_filename(cat);

      if (name) {
          star_index* pstarindex = star_index_load(name);
          //star_index_dump(pstarindex, stdout);
          if (pstarindex) {
              const char* star_name = 0;
              uves_msg("The catalog is loaded, looking for star in RA[%f] DEC[%f] tolerance[%f]", dRA, dDEC, EPSILON);
              *pptable = star_index_get(pstarindex, dRA, dDEC, EPSILON, EPSILON, &star_name);
              //uves_msg("pptable %p",*pptable);
              if (*pptable && star_name) {
                  uves_msg("REF table is found in the catalog, star name is [%s]", star_name);
              }
              else {
                  uves_msg("ERROR - REF table could not be found in the catalog");
                  cpl_error_reset();
              }
          }
          else {
              uves_msg("ERROR - could not load the catalog");
          }
          star_index_delete(pstarindex);

      }
  }
  uves_print_rec_status(0);

  return;
}


static hdrl_spectrum1D *
uves_std_cat_frame_to_hdrl_spectrum1D(cpl_frame* frm_std_cat,const double dRA,
        const double dDEC)
{
    cpl_ensure(frm_std_cat != NULL, CPL_ERROR_NULL_INPUT, NULL);
    hdrl_spectrum1D * std_s1d;

    //const char* name = cpl_frame_get_filename(frm_std_cat);
    cpl_table* tbl_ref = NULL;


    double dEpsilon=0.5;

    uves_parse_catalog_std_stars(frm_std_cat,dRA,dDEC,dEpsilon,&tbl_ref);

    if(tbl_ref == NULL) {
        uves_msg_error("Provide std star catalog frame");
        return NULL;
    } else {
        //cpl_table_dump(tbl_ref,0,2,stdout);
        if(cpl_table_has_column(tbl_ref,"NDATA")) {
            uves_msg("NDATA case");

            int status;
            int ndata=cpl_table_get_int(tbl_ref,"NDATA",0,&status);
            cpl_array* a_lambda = cpl_table_get_array(tbl_ref,"LAMBDA",ndata);

            //cpl_array_dump(a_lambda, 0, 10, stdout);
            cpl_array* a_flux = cpl_table_get_data_array(tbl_ref,"F_LAMBDA");

            double* plambda= cpl_array_get_data_double(a_lambda);

            float* pflux= cpl_array_get_data_float(a_flux);

            cpl_table* tab = cpl_table_new(ndata);
            cpl_table_new_column(tab,"LAMBDA",CPL_TYPE_DOUBLE);
            cpl_table_new_column(tab,"FLUX",CPL_TYPE_DOUBLE);
            for(int i=0; i<ndata;i++) {
                cpl_table_set_double(tab,"LAMBDA",i,(double)plambda[i]);
                cpl_table_set_double(tab,"FLUX",i, (double)pflux[i]);
            }

            std_s1d=hdrl_spectrum1D_convert_from_table(tbl_ref, "FLUX",
                            "LAMBDA", NULL, NULL, global_scale);

        } else {
        //cpl_table_dump(tbl_ref,0,2,stdout);
        std_s1d=hdrl_spectrum1D_convert_from_table(tbl_ref, "FLUX",
                "LAMBDA", NULL, NULL, global_scale);

        }

    }
    uves_print_rec_status(0);
    uves_free_table(&tbl_ref);
    return std_s1d;
}

static hdrl_spectrum1D *
uves_atmext_frame_to_hdrl_spectrum1D(cpl_frame* frm_atmext)
{
    cpl_ensure(frm_atmext != NULL, CPL_ERROR_NULL_INPUT, NULL);
    hdrl_spectrum1D * ext_s1d;
    const char* name_atm = cpl_frame_get_filename(frm_atmext);

    cpl_table* tbl_atmext = cpl_table_load(name_atm,1,0);


    ext_s1d=hdrl_spectrum1D_convert_from_table(tbl_atmext, "LA_SILLA",
            "LAMBDA", NULL, NULL, global_scale);
    uves_free_table(&tbl_atmext);
    return ext_s1d;
}
static cpl_bivector *
uves_read_wlen_windows(const cpl_frame * fit_areas_frm){

    cpl_ensure(fit_areas_frm != NULL, CPL_ERROR_NULL_INPUT, NULL);
    cpl_table * tab = NULL;

    tab = cpl_table_load(cpl_frame_get_filename(fit_areas_frm),1,0);


    const cpl_size nrow = cpl_table_get_nrow(tab);

    double * pwmin = cpl_table_unwrap(tab,"LAMBDA_MIN");
    double * pwmax = cpl_table_unwrap(tab,"LAMBDA_MAX");

    cpl_vector * wmin = cpl_vector_wrap(nrow, pwmin);
    cpl_vector * wmax = cpl_vector_wrap(nrow, pwmax);
    cpl_bivector * to_ret = cpl_bivector_wrap_vectors(wmin, wmax);
    cpl_table_delete(tab);
    uves_print_rec_status(0);
    return to_ret;
}
static hdrl_parameter*
uves_response_parameters_calc_par(cpl_propertylist* plist, enum uves_chip chip)
{
    cpl_ensure(plist != NULL, CPL_ERROR_NULL_INPUT, NULL);
    hdrl_value Ap = {0,0};

    double airmass=0;
    double airmass_start=0;
    double airmass_end=0;
    airmass_start=uves_pfits_get_airmass_start(plist);
    airmass_end=uves_pfits_get_airmass_end(plist);
    airmass=0.5*(airmass_start+airmass_end);

    hdrl_value Am = {airmass,0};
    uves_msg("resp airmass=%g",airmass);
    double gain=uves_pfits_get_gain((uves_propertylist*)plist,chip);
    hdrl_value G= {gain,0};
    uves_msg("resp gain=%g",gain);
    double exptime=uves_pfits_get_exptime(plist);
    uves_msg("resp exptime=%g",exptime);

    hdrl_value Tex= {exptime,0};
    hdrl_parameter* calc_par = hdrl_response_parameter_create(Ap, Am, G, Tex);

    return calc_par;

}

static cpl_array *
uves_read_fit_points(const cpl_frame * frm_fit_points,
        const double ra, const double dec, const double ra_dec_tolerance){

    cpl_ensure(frm_fit_points != NULL, CPL_ERROR_NULL_INPUT, NULL);
    const char * fname = cpl_frame_get_filename(frm_fit_points);

    cpl_table * index_table = cpl_table_load(fname, 1, CPL_FALSE);
    const cpl_size sz = cpl_table_get_nrow(index_table);

    cpl_size selected_ext = -1;
    //uves_msg("fit point table: %s",fname);
    for(cpl_size i = 0; i < sz; ++i){
        int rej;
        const int ext_id = cpl_table_get_int(index_table, "ext_id", i ,&rej);
        const double curr_ra = cpl_table_get(index_table, "ra", i, &rej);
        const double curr_dec = cpl_table_get(index_table, "dec", i, &rej);
        if ((ext_id > 0) && (fabs(curr_ra - ra) < ra_dec_tolerance) &&
                (fabs(curr_dec - dec) < ra_dec_tolerance)){
            selected_ext = ext_id;
        }
    }

    cpl_table_delete(index_table);
    cpl_ensure(selected_ext >= 0, CPL_ERROR_ILLEGAL_OUTPUT, NULL);


    cpl_table * tb = cpl_table_load(fname, selected_ext, CPL_FALSE);

    const cpl_size sz_points = cpl_table_get_nrow(tb);
    cpl_array * fit_points = cpl_array_new(sz_points, CPL_TYPE_DOUBLE);

    for(cpl_size i = 0; i < sz_points; ++i){
        int rej = 0;
        const double d = cpl_table_get(tb, "LAMBDA", i, &rej);
        cpl_array_set(fit_points, i, d);
    }

    cpl_table_delete(tb);
    return fit_points;
}






cpl_error_code
uves_response_hdrl(cpl_frame* frm_sci, cpl_frameset* sof,enum uves_chip chip,
        bool debug_mode, cpl_parameterlist* parameters, const char* starttime)


{
    //cpl_ensure(frm_sci != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);

    uves_msg("resp hdrl start");
    cpl_frame* frm_std_cat = cpl_frameset_find(sof, FLUX_STD_TABLE);
    cpl_frame* frm_atmext = cpl_frameset_find(sof, UVES_EXTCOEFF_TABLE);
    cpl_frame* frm_tell_cat = cpl_frameset_find(sof, TELL_MOD_CATALOG);
    cpl_frame* frm_resp_fit_points = cpl_frameset_find(sof, RESP_FIT_POINTS_CAT);
    cpl_frame* frm_resp_quality_areas = cpl_frameset_find(sof, QUALITY_AREAS);
    cpl_frame* frm_resp_fit_areas = cpl_frameset_find(sof, FIT_AREAS);

    const char* name_sci=cpl_frame_get_filename(frm_sci);
    //uves_msg("name sci frame=%s",name_sci);
    cpl_propertylist* plist=cpl_propertylist_load(name_sci,0);
    double wcen = uves_pfits_get_gratwlen(plist, chip);
    int correct_tellurics=0;
    hdrl_data_t wguess = 0; // um
    char band[MAX_NAME_SIZE];
    sprintf(band,"%s",UVES_ARM(chip));
    /* set reference wavelength to determine STD star shift and if telluric
     * correction needs to be done: not for BLUE arm, and only in REDU
     * after ~760nm
     */
    if (strcmp(band,"BLUE") == 0) {
        if(wcen==346) {
            wguess = 383.5384;
        } else if (wcen == 390 ) {
            wguess = 410.1737;
        } else if (wcen == 437 ) {
            wguess = 434.0468;
        }
        correct_tellurics=0;
    } else if (strcmp(band,"RED") == 0) {
        if( ((wcen == 520 || wcen==564 || wcen == 580 ) &&
                chip == UVES_CHIP_REDL)  ||
            (wcen == 520 && chip == UVES_CHIP_REDU) ) {
            wguess = 486.1322;
            correct_tellurics=0;//was 1
        } else if ( (wcen == 564 || wcen == 580 || wcen == 600) &&
                chip == UVES_CHIP_REDU) {
            wguess = 656.2817;
            correct_tellurics=0;//was 1
        } else if (wcen == 860 && chip == UVES_CHIP_REDU) {
            wguess = 954.5974;
            correct_tellurics=0;//was 1
        } else {
            correct_tellurics=0;//was 1
        }
    }

    // abort early if any necessary inputs are missing
    const bool essentials_present = (frm_std_cat && frm_atmext && frm_resp_fit_points);
    const bool tellurics_present = (frm_tell_cat && frm_resp_quality_areas && frm_resp_fit_areas);
    if(!essentials_present || (correct_tellurics && !tellurics_present)) {
        uves_free_propertylist(&plist);
        return(CPL_ERROR_NULL_INPUT);
    }

    double dRA=uves_pfits_get_ra(plist);
    double dDEC=uves_pfits_get_dec(plist);
    //uves_msg("ra=%g dec=%g",dRA,dDEC);
    cpl_table* sci_tab=cpl_table_load(name_sci,1,0);
    double sci_wmin=cpl_table_get_column_min(sci_tab,"wavelength");
    double sci_wmax=cpl_table_get_column_max(sci_tab,"wavelength");

    //uves_msg("sci_min=%g",sci_wmin);
    //uves_msg("sci_max=%g",sci_wmax);
    int nrow = cpl_table_get_nrow(sci_tab);
    double bin_size=(sci_wmax-sci_wmin)/(nrow-1);
    uves_free_table(&sci_tab);

    double AA2nm=0.1;
    hdrl_spectrum1D * sci_s1d = uves_sci_frame_to_hdrl_spectrum1D(frm_sci);
#if   UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(sci_s1d, "sci_s1d_1.fits");
#endif

    /* HDRL expects results in nm */
    hdrl_spectrum1D_wavelength_mult_scalar_linear(sci_s1d,AA2nm);
#if UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(sci_s1d, "sci_s1d_2.fits");
#endif
    /* because the reference flux std assumes the flux in units of ergs/cm2/s/A
     * we need to convert the spectrum to Angstroms and know the size of a bin
     * in angstroms
     */

    double conv_factor=0;
    double nm2AA = 10.;

    uves_msg("bin_size=%g",bin_size);
    /* For UVES the bin size is already in angstrom */
    conv_factor=bin_size;
    uves_msg("conv_factor=%g",conv_factor);
    hdrl_spectrum1D_div_scalar(sci_s1d,(hdrl_value){conv_factor,0.});

#if UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(sci_s1d, "sci_s1d_3.fits");
#endif
        hdrl_spectrum1D * std_s1d;


    if (NULL == (std_s1d = uves_std_cat_frame_to_hdrl_spectrum1D(frm_std_cat,dRA,dDEC))) {
        uves_msg_error("Determining ref std star spectrum. Observed STD star not in catalog");
        goto cleanup;
    }
#if UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(std_s1d, "std_s1d_2.fits");
#endif
    /* Check that std star spectrum in in Angstrom or nm units and eventually
     * rescale to nm
     */
    int status;
    hdrl_spectrum1D_wavelength w;
    double wave_med;
    w = hdrl_spectrum1D_get_wavelength(std_s1d);
    wave_med = cpl_array_get_median(w.wavelength);


    if (wave_med > 1500) {
        /* UVES goes from 298 till 1100 nm
         * If the catalog contains values in Angstroms one has to correct values.
         */
        hdrl_spectrum1D_wavelength_mult_scalar_linear(std_s1d,AA2nm);
    }
#if UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(std_s1d, "std_s1d.fits");
#endif

    cpl_ensure(std_s1d != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);

    hdrl_spectrum1D * ext_s1d = uves_atmext_frame_to_hdrl_spectrum1D(frm_atmext);
    /* Check that atm ext spectrum in in Angstrom or nm units and eventually
         * rescale to nm
         */
    w = hdrl_spectrum1D_get_wavelength(ext_s1d);
    wave_med = cpl_array_get_median(w.wavelength);

    if (wave_med > 1500) {
        hdrl_spectrum1D_wavelength_mult_scalar_linear(ext_s1d,AA2nm);
    }
#if UVES_HDRL_USE_EXPERIMENTAL
        hdrl_spectrum1D_save(ext_s1d, "ext_s1d.fits");
#endif

    //hdrl_parameter* eff_pars;
    /*HDRL expects telescope area in cm2 */

    hdrl_spectrum1Dlist * telluric_models = NULL;
    if(frm_tell_cat) telluric_models = uves_get_telluric_models(frm_tell_cat);
    cpl_bivector * fit_areas = NULL;
    if(frm_resp_fit_areas) fit_areas = uves_read_wlen_windows(frm_resp_fit_areas);
    cpl_bivector * quality_areas = NULL;
    if(frm_resp_quality_areas) quality_areas = uves_read_wlen_windows(frm_resp_quality_areas);

        /* set parameters for telluric correction  */
        double wmin=log(cpl_array_get_min((hdrl_spectrum1D_get_wavelength(std_s1d)).wavelength));
        double wmax=log(cpl_array_get_max((hdrl_spectrum1D_get_wavelength(std_s1d)).wavelength));

        hdrl_data_t range_wmin =sci_wmin*AA2nm;
        hdrl_data_t range_wmax =sci_wmax*AA2nm;

        hdrl_data_t fit_half_win = 1.5;
        uves_msg("wmin=%g wmax=%g",range_wmin,range_wmax);
        wmin=log(range_wmin); //log scale
        wmax=log(range_wmax);
        //uves_msg("end of checks band: %s",band);
        cpl_boolean velocity_enable = CPL_FALSE;
        if( (range_wmin < wguess && wguess < range_wmax) &&
                     wcen!=346) {
            velocity_enable = CPL_TRUE;
        } else {
            velocity_enable = CPL_FALSE;
        }
        hdrl_data_t fit_wmin = wguess-fit_half_win;
        hdrl_data_t fit_wmax = wguess+fit_half_win;
        uves_msg("band=%s chip: %s wcen=%g log wave range wmin=%g wmax=%g",
                band,uves_chip_get_det(chip),wcen,wmin,wmax);

        const hdrl_data_t lmin = (hdrl_data_t)wmin;
        const hdrl_data_t lmax = (hdrl_data_t)wmax;
        uves_msg("wguess: %g wave shift corr: %d tell corr: %d lmin %g lmax %g",
                        wguess,velocity_enable,correct_tellurics,lmin,lmax);

        const cpl_boolean xcorr_normalize = (cpl_boolean) CPL_TRUE;
        const cpl_boolean shift_in_log_scale = (cpl_boolean) CPL_TRUE;

        hdrl_parameter* shift_par = NULL;
        if(velocity_enable) {
            shift_par = hdrl_spectrum1D_shift_fit_parameter_create(wguess, range_wmin,
                    range_wmax, fit_wmin, fit_wmax,fit_half_win);
        }
        uves_print_rec_status(200);
        hdrl_parameter* resp_calc_par = uves_response_parameters_calc_par(plist,chip);

        double ra_dec_tolerance=0.1;
        cpl_array* fit_points = uves_read_fit_points(frm_resp_fit_points,
                dRA, dDEC, ra_dec_tolerance);

        hdrl_parameter * telluric_par=NULL;
        /* performs telluric correction if needed & possible */
        if(!telluric_models) correct_tellurics=0;
        if(correct_tellurics && telluric_models && quality_areas && fit_areas) {
            uves_msg("Correct tellurics");
            const cpl_size xcorr_half_win = 200;
            hdrl_data_t xcorr_w_step=1e-5;
            xcorr_w_step=7.826e-6;
            telluric_par = hdrl_response_telluric_evaluation_parameter_create(telluric_models,
                    xcorr_w_step, xcorr_half_win, xcorr_normalize, shift_in_log_scale, quality_areas,
                    fit_areas, lmin, lmax);
            uves_print_rec_status(210);
        }

        uves_msg("lmin %g lmax %g",lmin,lmax);
        const cpl_size post_smoothing_radius=11;

        //TODO: fill following params
        const hdrl_data_t fit_wrange=1.0;//was 4
        hdrl_parameter* resp_par =
        hdrl_response_fit_parameter_create(post_smoothing_radius, fit_points,
                fit_wrange, NULL);

        hdrl_spectrum1D * std_s1d_e = uves_extend_hdrl_spectrum(sci_wmin * AA2nm, sci_wmax * AA2nm, std_s1d);
        hdrl_spectrum1D * ext_s1d_e = uves_extend_hdrl_spectrum(sci_wmin * AA2nm, sci_wmax * AA2nm, ext_s1d);

#if UVES_HDRL_USE_EXPERIMENTAL
           hdrl_spectrum1D_save(std_s1d_e, "std_s1d_e.fits");
           hdrl_spectrum1D_save(ext_s1d_e, "ext_s1d_e.fits");
#endif
        uves_print_rec_status(211);
        /* compute instrument response */
        hdrl_response_result * response =
                hdrl_response_compute(sci_s1d,std_s1d_e,ext_s1d_e,telluric_par,
                        shift_par, resp_calc_par,resp_par);
        uves_print_rec_status(220);
        /*
        uves_msg("size sci = %lld size resp = %lld",
                hdrl_spectrum1D_get_size(sci_s1d),
                hdrl_spectrum1D_get_size(response->final_response));
                */

        uves_print_rec_status(221);
        hdrl_spectrum1D_delete(&std_s1d_e);
        hdrl_spectrum1D_delete(&ext_s1d_e);
        /* extends response to same wavelength range as observed sci to flux-cal  */
        if(response != NULL){

            const hdrl_spectrum1D * resp_s1d_smo =
                    hdrl_response_result_get_final_response(response);

            const hdrl_spectrum1D * resp_s1d_raw =
                    hdrl_response_result_get_raw_response(response);
            //uves_msg("wmin=%g wmax=%g",sci_wmin,sci_wmax);
#if UVES_HDRL_USE_EXPERIMENTAL
               hdrl_spectrum1D_save(resp_s1d_smo, "resp_s1d_smo.fits");
               hdrl_spectrum1D_save(resp_s1d_raw, "resp_s1d_raw.fits");
#endif

            cpl_table* tab=hdrl_spectrum1D_convert_to_table(resp_s1d_smo,
                                            "response", "wavelength", "response_error", "response_bpm");

            sci_wmin=cpl_table_get_column_min(tab,"wavelength");
            sci_wmax=cpl_table_get_column_max(tab,"wavelength");
            //uves_msg("wmin=%g wmax=%g",sci_wmin,sci_wmax);

            cpl_table* resp_smo=NULL;
            cpl_table* resp_raw=NULL;
            cpl_table* resp_pts=NULL;
            resp_smo = hdrl_spectrum1D_convert_to_table(resp_s1d_smo,
                            "response_smo", "wavelength", "response_smo_error",
                            "response_smo_bpm");

            resp_raw = hdrl_spectrum1D_convert_to_table(resp_s1d_raw,
                            "response_raw", "wavelength", "response_raw_error",
                            "response_raw_bpm");

            resp_pts = hdrl_spectrum1D_convert_to_table(
                            hdrl_response_result_get_selected_response(response),
                            "response_fit_pts", "wavelength", "response_fit_pts_error",
                            "response_fit_pts_bpm");
            uves_free_table(&tab);

            /* put back computed response (that was in nm) to angstrom units */
            cpl_table_divide_scalar(resp_smo,"wavelength",AA2nm);
            cpl_table_divide_scalar(resp_raw,"wavelength",AA2nm);
            cpl_table_divide_scalar(resp_pts,"wavelength",AA2nm);
            cpl_table* qclog_tbl=NULL;
            //cpl_table* qclog_tbl = uves_qclog_init();
            cpl_frame* frm_resp_wind_qc = cpl_frameset_find(sof, RESPONSE_WINDOWS);

            const hdrl_spectrum1D * selected_points = hdrl_response_result_get_selected_response(response);
            const cpl_array * waves = hdrl_spectrum1D_get_wavelength(selected_points).wavelength;
            const double wmin_g = cpl_array_get_min(waves)/AA2nm;
            const double wmax_g = cpl_array_get_max(waves)/AA2nm;
            //uves_msg("good wavelengths wmin=%g wmax=%g",wmin_g,wmax_g);


            if( resp_smo != NULL && frm_resp_wind_qc != NULL) {

                //uves_resp_qc(resp_smo, frm_resp_wind_qc, wmin_g, wmax_g, &qclog_tbl);

            }
            /*
            uves_pro_save_tbl(resp_smo, ref_set, sof,(char*) RESPONSE_FILENAME,
                    PRO_RESPONSE, qclog_tbl, plugin_id, config);
                    */

            /* Save product response */
            char* product_filename=NULL;
            product_filename = uves_response_hdrl_filename(chip);


            cpl_table_and_selected_int(resp_smo,"response_smo_bpm",CPL_EQUAL_TO, 0);
            cpl_table* resp_plot = cpl_table_extract_selected(resp_smo);
            cpl_table_multiply_scalar(resp_plot,"response_smo",1.e16);
            check( uves_plot_table(resp_plot, "wavelength", "response_smo",
                    "Computed smoothed response (%s chip)",
                    uves_chip_tostring_upper(chip)),
               "Plotting failed");
            cpl_table_select_all(resp_smo);
            cpl_table_delete(resp_plot);

            cpl_table_and_selected_int(resp_raw,"response_raw_bpm",CPL_EQUAL_TO, 0);
            resp_plot = cpl_table_extract_selected(resp_raw);
            cpl_table_multiply_scalar(resp_plot,"response_raw",1.e16);
            check( uves_plot_table(resp_plot, "wavelength", "response_raw",
                    "Computed raw response (%s chip)",
                    uves_chip_tostring_upper(chip)),
               "Plotting failed");
            cpl_table_select_all(resp_raw);
            cpl_table_delete(resp_plot);


            char extname[80];
            uves_propertylist* table_header = uves_propertylist_new();
            cpl_propertylist* response_header = cpl_propertylist_new();
            sprintf(extname,"RESPONSE");
            uves_pfits_set_extname(table_header,extname);
            cpl_propertylist* qclog=NULL;
            uves_frameset_insert(
                    sof,
                    resp_smo,
                    CPL_FRAME_GROUP_PRODUCT,
                    CPL_FRAME_TYPE_TABLE,
                    CPL_FRAME_LEVEL_INTERMEDIATE,
                    product_filename,
                    UVES_INSTR_RESPONSE_FINE(chip),
                    plist,
                    response_header,
                    qclog,
                    parameters,
                    make_str(UVES_RESPONSE_ID),
                    PACKAGE "/" PACKAGE_VERSION,
                    qclog,
                    starttime, true, 0);

            cpl_table_save(resp_raw,NULL,NULL,product_filename,CPL_IO_EXTEND);
            cpl_table_save(resp_pts,NULL,NULL,product_filename,CPL_IO_EXTEND);
                cpl_propertylist_delete(response_header);
                uves_propertylist_delete(table_header);
                cpl_free(product_filename);
            cpl_table_delete(qclog_tbl);
            cpl_table_delete(resp_smo);
            cpl_table_delete(resp_raw);
            cpl_table_delete(resp_pts);


        }

        cpl_bivector_delete(quality_areas);
        cpl_bivector_delete(fit_areas);

        hdrl_parameter_delete(telluric_par);
        hdrl_parameter_delete(shift_par);
        hdrl_parameter_delete(resp_calc_par);
        hdrl_parameter_delete(resp_par);

        hdrl_spectrum1D_delete(&sci_s1d);
        hdrl_spectrum1D_delete(&std_s1d);
        hdrl_spectrum1D_delete(&ext_s1d);
        uves_free_propertylist(&plist);
        hdrl_spectrum1Dlist_delete(telluric_models);


        cpl_array_delete(fit_points);
        hdrl_response_result_delete(response);




     cleanup:
        uves_print_rec_status(0);
        return (cpl_error_get_code() != CPL_ERROR_NONE);
}

static cpl_frame*
uves_image_to_table_frame(cpl_image * data,cpl_image* errs,
                          cpl_propertylist* head, cpl_propertylist* rhead)
{
    cpl_frame* frm;
    cpl_ensure(data != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);
    cpl_ensure(errs != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);
    cpl_ensure(head != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);

    cpl_table* tab = NULL;
    cpl_size nx = cpl_image_get_size_x(data);
    cpl_size ny = cpl_image_get_size_y(data);
    double crval1=uves_pfits_get_crval1(head);
    double crpix1=uves_pfits_get_crpix1(head);
    double cdelt1=uves_pfits_get_cdelt1(head);

    double ra = uves_pfits_get_ra(rhead);
    double dec = uves_pfits_get_dec(rhead);
    double mjd_obs = uves_pfits_get_mjdobs(rhead);
    double exptime = uves_pfits_get_exptime(rhead);
    cpl_propertylist_append_double(head,"RA",ra);
    cpl_propertylist_append_double(head,"DEC",dec);
    cpl_propertylist_append_double(head,UVES_MJDOBS,mjd_obs);
    cpl_propertylist_append_double(head,UVES_EXPTIME,exptime);
    cpl_propertylist_append_double(head,"CRPIX1",crpix1);
    cpl_propertylist_append_double(head,"CRVAL1",crval1);
    cpl_propertylist_append_double(head,"CDELT1",cdelt1);
    tab = cpl_table_new(nx);
    cpl_table_new_column(tab,"wavelength",CPL_TYPE_DOUBLE);
    cpl_table_new_column(tab,"flux",CPL_TYPE_DOUBLE);
    cpl_table_new_column(tab,"qual",CPL_TYPE_DOUBLE);
    cpl_table_new_column(tab,"errs",CPL_TYPE_DOUBLE);

    cpl_table_fill_column_window_double(tab,"wavelength",0,nx,0);
    cpl_table_fill_column_window_double(tab,"flux",0,nx,0);
    cpl_table_fill_column_window_double(tab,"qual",0,nx,0);
    cpl_table_fill_column_window_double(tab,"errs",0,nx,0);

    double* ptflux=NULL;
    double* pterrs=NULL;
    double* ptwave=NULL;
    double* pidata=NULL;
    double* pierrs=NULL;

    pidata=cpl_image_get_data_double(data);
    pierrs=cpl_image_get_data_double(errs);
    ptflux=cpl_table_get_data_double(tab,"flux");
    pterrs=cpl_table_get_data_double(tab,"errs");
    ptwave=cpl_table_get_data_double(tab,"wavelength");
    uves_msg("crpix1=%g",crpix1);
    uves_msg("cdelt1=%g",cdelt1);
    uves_msg("crval1=%g",crval1);
    for(int i = 0; i < nx; i++) {
       ptwave[i]=crval1+(i-crpix1+1)*cdelt1;
       ptflux[i]=pidata[i];
       pterrs[i]=pierrs[i];
    }
    uves_sanitise_propertylist(head);
    cpl_table_save(tab,head,NULL,"sci_red.fits",CPL_IO_DEFAULT);
    frm=cpl_frame_new();
    cpl_frame_set_filename(frm,"sci_red.fits");

    cpl_table_delete(tab);
    return frm;
}
/*----------------------------------------------------------------------------*/
/**
  @brief    Reduce one chip of a UVES std frame
  @param    raw_image           The raw image
  @param    raw_header          FITS header of raw image
  @param    rotated_header      Header describing the geometry of the raw image after
                                rotation and removal of pre- and overscan areas
  @param    master_bias         The master bias image for this chip, or NULL
  @param    master_dark         The master dark image for this chip, or NULL
  @param    mflat_header        FITS header of master flat frame
  @param    master_flat         The master flat image for this chip, or NULL
  @param    mdark_header        FITS header of master dark frame
  @param    ordertable          Order table describing the order locations on the raw image
  @param    order_locations     The polynomial describing the order positions
  @param    linetable           Length 3 array of linetable for sky, object, sky.
  @param    linetable_header    Length 3 array of linetable headers for sky, object, sky.
  @param    dispersion_relation Length 3 array of dispersion relations for sky, object, sky.
  @param    flux_table          Catalogue standard star fluxes
  @param    atm_extinction      Atmospheric extinction coefficients
  @param    chip                CCD chip
  @param    debug_mode               If set to true, intermediate results are saved to
                                the current directory
  @param    parameters          The recipe parameter list containing parameters
                                for background subtraction, flat-fielding, extraction,
                rebinning
  @param    calc_response       Also calculate response curve (or just do the reduction)?
  @param    PACCURACY           Pointing accuracy (in arcseconds) used to identify object
  @param    ref_obj_id          (out) Reference object ID
  @param    reduced_spectrum    (out) The extracted, flat-fielded, rebinned, merged,
                                sky-subtracted (but not flux calibrated) science spectrum
  @param    reduced_header      (out) Header describing the geometry of the @em reduced_spectrum
  @param    background          (out) The background image that was subtracted
  @param    blaze_efficiency    (out) Efficiency at blaze function maximum, for each order
  @param    efficiency          (out) The quantum detection efficiency table
  @param    extraction_slit     (out) extraction slit length used
  @return   The reduced spectrum

  This function reduces the provided @em raw_image (see @c uves_reduce) and
  computes the response function as

  @em R ( @em lambda ) = @em flux_cat / @em flux_std

  where @em flux_cat is the tabulated standard star flux, and @em flux_std is
  the observed flux (corrected for exposure time, gain, binning and moved
  to the top of the atmosphere)

  Additionally the instrument Quantum Detection Efficiency is computed.
  (see @c uves_response_efficiency)
**/
/*----------------------------------------------------------------------------*/

static cpl_error_code
uves_response_process_chip(const cpl_image *raw_image,
                           const uves_propertylist *raw_header,
                           const uves_propertylist *rotated_header,
                           const cpl_image *master_bias,
                           const uves_propertylist *mbias_header,
                           const cpl_image *master_dark,
                           const uves_propertylist *mdark_header,
                           const cpl_image *master_flat,
                           const uves_propertylist *mflat_header,
                           const cpl_table *ordertable,
                           const polynomial *order_locations,
                           const cpl_table *linetable[3],
                           const uves_propertylist *linetable_header[3],
                           const polynomial *dispersion_relation[3],
                           const cpl_table *flux_table,
                           const cpl_table *atm_extinction,
                           enum uves_chip chip,
                           /* General */
                           bool   debug_mode,
                           /* Backsub */
                           /* Flat fielding */
                           /* Extraction */
                           /* Rebinning  */
                           const cpl_parameterlist *parameters,
                           bool calc_response,
                           /* Identification */
                           double PACCURACY,
                           /* Output */
                           char **ref_obj_id,
                           cpl_image **reduced_spectrum,
                           uves_propertylist **reduced_header,
                           cpl_image **background,
                           cpl_table **efficiency,
                           cpl_table** blaze_efficiency,
                           cpl_table** info_tbl,
                           double *extraction_slit,
                           cpl_frameset* sof,
                           const char* starttime)

{
    cpl_image         *rebinned_spectrum      = NULL;
    cpl_image         *rebinned_noise         = NULL;
    uves_propertylist *rebinned_header        = NULL;
    cpl_image         *reduced_rebinned       = NULL;
    cpl_image         *reduced_rebinned_noise = NULL;

    cpl_image         *reduced_noise          = NULL;

    cpl_table *cosmic_mask     = NULL;   /* Cosmic ray table  (not a product of this recipe) */
    cpl_table *order_trace     = NULL;   /* Order trace table (not a product of this recipe) */
    cpl_image *merged_spectrum = NULL;   /* Not sky-subtracted (if simple extraction)        */
    cpl_image *wave_map        = NULL;
    cpl_image *merged_sky      = NULL;
    cpl_image *merged_noise    = NULL;
    cpl_image *reduced_scaled  = NULL;   /* Merged, sky-subtracted and normalized
                        (exposure time, gain...) */

    cpl_table *catalogue_flux  = NULL;    /* Std star catalogue flux */

    /* Do the science reduction. Produces wave.cal. spectra. */
    uves_msg("Reducing standard star");

    check( uves_reduce(raw_image,
                       raw_header,
                       rotated_header,
                       master_bias,
                       mbias_header,
                       master_dark,
                       mdark_header,
                       master_flat,
                       mflat_header,
                       ordertable,
                       order_locations,
                       linetable,
                       linetable_header,
                       dispersion_relation,
                       chip,
                       debug_mode,
                       parameters,
                       make_str(UVES_RESPONSE_ID),
                       "",
                       /* Output */
                       NULL,
                       NULL,
                       NULL,  /* 2d products */
                       &cosmic_mask,
                       &wave_map,
                       background,
                       NULL,
                       NULL,  /* Variance of flat-fielded */
                       NULL,
                       NULL,  /* Don't need these intermediate products */
                       &merged_sky,
                       &rebinned_spectrum,
                       &rebinned_noise,
                       &rebinned_header,
                       &merged_spectrum,
                       &merged_noise,
                       reduced_header,
                       &reduced_rebinned,
                       &reduced_rebinned_noise,
                       reduced_spectrum,
                       &reduced_noise,
                       info_tbl,
                       extraction_slit,
                       &order_trace),
       "Could not reduce frame");


    check( uves_plot_image_rows(*reduced_spectrum, 1, 1, 1,
                "Wavelength (arbitrary units)", NULL,
                "Reduced spectrum (%s chip)",
                uves_chip_tostring_upper(chip)),
       "Plotting failed");

    if (calc_response)
    {
        /* put here HDRL based code (because later it is done a
         * filter_image_average */
        /*
        cpl_image_save(reduced_rebinned, "reduced_rebinned.fits",
                CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
        uves_print_rec_status(100);
        cpl_propertylist_dump(rebinned_header,stdout);
        uves_response_hdrl(reduced_rebinned,reduced_rebinned_noise,
                        rebinned_header,sof);
                        */


        uves_print_rec_status(101);
        cpl_frame* sci_frm =
                uves_image_to_table_frame(*reduced_spectrum,reduced_noise,
                        *reduced_header,raw_header);
        /* computes HDRL based INSTRUMENT RESPONSE */
        check(uves_response_hdrl(sci_frm,sof,chip,debug_mode,parameters,
                starttime),
                "Could not calculate HDRL based response curve");
        cpl_frame_delete(sci_frm);



        /* Calculate efficiency table */
        uves_msg("Calculating efficiency curve");

        check( uves_response_efficiency(raw_image,
                        raw_header,
                        rotated_header,
                        master_bias,
                        mbias_header,
                        master_dark,
                        mdark_header,
                        ordertable,
                        order_locations,
                        linetable,
                        linetable_header,
                        dispersion_relation,
                        flux_table,
                        atm_extinction,
                        chip,
                        debug_mode,
                        parameters,
                        PACCURACY,
                        efficiency,
                        blaze_efficiency),
           "Efficiency calculation failed");

        check( uves_plot_table(*efficiency, "Wave", "Eff",
                   "Detection Quantum Efficiency (%s chip)",
                   uves_chip_tostring_upper(chip)),
           "Plotting failed");

        /* Save blaze function efficiency (efficiency at center of order) */
        if (debug_mode) check( uves_save_table_local("Blaze efficiency table",
                            "blaze_efficiency",
                            *blaze_efficiency, chip, -1, -1, rotated_header, NULL),
                  "Error saving blaze efficiency table");
    }
    else
    {
        uves_msg("Skipping response/efficiency computation");
    }

  cleanup:
    uves_free_propertylist(&rebinned_header);
    uves_free_image(&rebinned_noise);
    uves_free_image(&rebinned_spectrum);
    uves_free_table(&cosmic_mask);
    uves_free_table(&order_trace);
    uves_free_image(&merged_spectrum);
    uves_free_image(&merged_noise);
    uves_free_image(&merged_sky);
    uves_free_image(&reduced_rebinned);
    uves_free_image(&reduced_rebinned_noise);
    uves_free_image(&reduced_noise);
    uves_free_image(&reduced_scaled);
    uves_free_table(&catalogue_flux);

    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        /* Output */
        uves_free_image(reduced_spectrum);
        uves_free_image(background);
        uves_free_propertylist(reduced_header);
        uves_free_table(efficiency);
    }

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    parameters  the parameters list
  @param    frames      the frames list
  @return   CPL_ERROR_NONE if everything is ok
 */
/*----------------------------------------------------------------------------*/
static void
UVES_CONCAT2X(UVES_RESPONSE_ID,exe)(cpl_frameset *frames,
          const cpl_parameterlist *parameters,
          const char *starttime)
{
    /*
     * Variables containg the values of recipe parameters
     */

    /* General */
    bool debug_mode;

    /* Background subtraction */
    /* Implicitly passed */

    /* Flat-fielding */

    /* Extraction */
    /* Implicitly passed */

    /* Rebinning */
    /* Implicitly passed */

    /* Efficiency */
    double PACCURACY;

    /* CPL objects */
    /* Input, raw */
    cpl_image        *raw_image[2]      = {NULL, NULL};
    uves_propertylist *raw_header[2]     = {NULL, NULL};
    uves_propertylist *rotated_header[2] = {NULL, NULL};

    /* Input, calib */
    cpl_image        *master_bias        = NULL;
    uves_propertylist *master_bias_header = NULL;

    cpl_image        *master_dark        = NULL;
    uves_propertylist *master_dark_header = NULL;

    cpl_image        *master_flat        = NULL;
    uves_propertylist *master_flat_header = NULL;

    cpl_table        *ordertable       = NULL;
    uves_propertylist *ordertable_header= NULL;
    polynomial       *order_locations  = NULL;
    cpl_table        *traces           = NULL;

    cpl_table        *flux_table       = NULL;

    cpl_table        *atm_extinction   = NULL;
    uves_propertylist *table_header= NULL;
    /* Line tables for sky, object, sky (UVES specific) */
    const cpl_table        *linetable[3]           = {NULL, NULL, NULL};
    const uves_propertylist *linetable_header[3]    = {NULL, NULL, NULL};
    const polynomial       *dispersion_relation[3] = {NULL, NULL, NULL};

    /* Output */
    cpl_image        *background         = NULL;
    cpl_image        *reduced_spectrum   = NULL;
    uves_propertylist *spectrum_header    = NULL;
    cpl_table        *efficiency         = NULL;
    cpl_table        *blaze_efficiency   = NULL;
    uves_propertylist *efficiency_header  = NULL;
    cpl_table* info_tbl=NULL;

    /* Local variables */
    cpl_table *qclog[2] = {NULL, NULL};
    cpl_table *qclog_optext[2] = {NULL, NULL};
    cpl_table *catalogue_flux = NULL;
    const char *raw_filename = "";        /* Static */
    const char *flux_table_filename = ""; /* Static */
    const char *atm_ext_filename = "";    /* Static */
    char *product_filename = NULL;        /* Dynamically allocated */
    char *prod_catg = NULL;
    char *ref_obj_name = NULL;            /* Reference object id */
    bool calc_response = false;           /* Calculate instr response? */
    double extraction_slit;

    bool blue  = false;
    enum uves_chip chip;
    int binx = 0;
    int biny = 0;
    const char* PROCESS_CHIP=NULL;

    const char *ordertable_filename = "";
    const char *linetable_filename = "";
    const char *master_bias_filename = "";
    const char *master_dark_filename = "";
    const char *master_flat_filename = "";
    const char *chip_name = "";
    int tracerow=0;                  /* Index of table row */

    int raw_index = 0;

    char extname[80];
    double trace_offset=0;
    int trace_number=0;
    int trace_enabled=0;
    int window=0;          /* window number */
    merge_method m_method;

    /* Read recipe parameters */
    {
    /* General */
    check( uves_get_parameter(parameters, NULL, "uves", "debug",
                  CPL_TYPE_BOOL  , &debug_mode      ), "Could not read parameter");

    check( uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
               "Could not read parameter");
    uves_string_toupper((char*)PROCESS_CHIP);

    /* Background subtraction, Flat-fielding, Rebinning */
    /* The input parameter list is passed */

    /* For both response curve and efficiency step:
       Allow only extraction methods average/linear/optimal */
    {
        extract_method em;

        /* Validate uves_response.reduce.extract.method */
        check( em = uves_get_extract_method(
               parameters, NULL,
               make_str(UVES_RESPONSE_ID) "." UVES_REDUCE_ID "." UVES_EXTRACT_ID),
           "Could not read extraction method");

        assure( em == EXTRACT_LINEAR || em == EXTRACT_AVERAGE || em == EXTRACT_OPTIMAL,
            CPL_ERROR_UNSUPPORTED_MODE,
            "Use linear/average/optimal extraction method to calculate response curve");

        /* Validate uves_response.efficiency.reduce.extract.method */
        check( em = uves_get_extract_method(
               parameters, NULL,
               make_str(UVES_RESPONSE_ID) ".efficiency." UVES_REDUCE_ID "." UVES_EXTRACT_ID),
           "Could not read extraction method");

        assure( em == EXTRACT_LINEAR || em == EXTRACT_AVERAGE || em == EXTRACT_OPTIMAL,
            CPL_ERROR_UNSUPPORTED_MODE,
            "Use linear/average/optimal extraction "
            "method to calculate quantum efficiency");
    }

    /* Identification */
    check( uves_get_parameter(parameters, NULL,
                  make_str(UVES_RESPONSE_ID) ".efficiency", "paccuracy",
                  CPL_TYPE_DOUBLE, &PACCURACY),
           "Could not read parameter");
    }

    /* Load raw image and header, and identify input frame as red or blue */
    check( uves_load_standard(frames,
                  &raw_filename, raw_image, raw_header, rotated_header,
                  &blue),
       "Error loading raw frame");

    /* Load flux table */
    check( uves_load_flux_table(frames, &flux_table_filename, &flux_table),
       "Error loading standard flux table");



    uves_msg_low("Using standard star flux table in '%s'", flux_table_filename);

    /* Before doing the reduction, find out if the standard star is in the table.
     * If not, still do the science reduction, but skip the instr.response part
     */
    catalogue_flux = uves_align(raw_header[0], flux_table, PACCURACY, &ref_obj_name);

    calc_response = true;
    if (cpl_error_get_code() == CPL_ERROR_INCOMPATIBLE_INPUT)
    {
        uves_error_reset();

        uves_msg_warning("No catalogue object found within %.2f arcsecs. "
                 "Instrument response curve will not be computed",
                 PACCURACY);

        calc_response = false;
    }

    /* Load atmospheric extinction table */
    check( uves_load_atmo_ext(frames, &atm_ext_filename, &atm_extinction),
       "Error loading extinction coefficients");

    uves_msg_low("Using atmospheric extinction table in '%s'", atm_ext_filename);

    //cpl_parameterlist_dump(parameters,stdout);
    //uves_msg("recipe=%s",UVES_RESPONSE_ID);
   check( m_method = uves_get_merge_method(parameters, "uves_cal_response", "reduce"),
           "Could not get merging method");
    /* Adjust parameters according to binning
     */
    check (binx = uves_pfits_get_binx(raw_header[0]),
       "Could not read x binning factor from input header");
    check (biny = uves_pfits_get_biny(raw_header[0]),
       "Could not read y binning factor from input header");

    /* Loop over one or two chips, over traces and
       over extraction windows */
    for (chip = uves_chip_get_first(blue);
     chip != UVES_CHIP_INVALID;
     chip = uves_chip_get_next(chip))
    {
        table_header = uves_propertylist_new();


        if(strcmp(PROCESS_CHIP,"REDU") == 0) {
            chip = uves_chip_get_next(chip);
        }


        /* const char *drs_filename        = "";    not used */
        raw_index = uves_chip_get_index(chip);
        uves_msg("Processing %s chip in '%s'",
             uves_chip_tostring_upper(chip), raw_filename);

        check_nomsg( chip_name = uves_pfits_get_chipid(raw_header[raw_index], chip));

        uves_msg_debug("Binning = %dx%d", binx, biny);

        /* Load master bias, set pointer to NULL if not present */
        uves_free_image(&master_bias);
        uves_free_propertylist(&master_bias_header);
        if (cpl_frameset_find(frames, UVES_MASTER_BIAS(chip)) != NULL)
        {
            check( uves_load_mbias(frames, chip_name,
                       &master_bias_filename,
                       &master_bias,
                       &master_bias_header, chip),
               "Error loading master bias");

            uves_msg_low("Using master bias in '%s'", master_bias_filename);
        }
        else
        {
            uves_msg_low("No master bias in SOF. Bias subtraction not done");
        }


        /* Load master dark, set pointer to NULL if not present */
        uves_free_image(&master_dark);
        uves_free_propertylist(&master_dark_header);
        if (cpl_frameset_find(frames, UVES_MASTER_DARK(chip)) != NULL)
        {
            check( uves_load_mdark(frames, chip_name,
                       &master_dark_filename,
                       &master_dark,
                       &master_dark_header, chip),
               "Error loading master dark");

            uves_msg_low("Using master dark in '%s'", master_dark_filename);
        }
        else
        {
            uves_msg_low("No master dark in SOF. Dark subtraction not done");
        }

        /* Load master flat */
        uves_free_image(&master_flat);
        uves_free_propertylist(&master_flat_header);
        check( uves_load_mflat_const(frames, chip_name,
                     &master_flat_filename,
                     &master_flat,
                     &master_flat_header,
                     chip, NULL), "Error loading master flat");

        uves_msg_low("Using master flat in '%s'", master_flat_filename);


        /* Load the order table for this chip */
        uves_free_table       (&ordertable);
        uves_free_propertylist(&ordertable_header);
        uves_polynomial_delete(&order_locations);
        uves_free_table       (&traces);

        check( uves_load_ordertable(frames,
                    false,  /* FLAMES? */
                    chip_name,
                    &ordertable_filename,
                    &ordertable,
                    &ordertable_header,
                                        NULL,
                    &order_locations, &traces,
                    NULL, NULL,
                                       NULL, NULL, /* fibre_pos,fibre_mask */
                    chip, false),
           "Could not load order table");
        uves_msg_low("Using order table in '%s'", ordertable_filename);


        /* Loop over all traces (1 trace for UVES) */
        for(tracerow = 0; tracerow < cpl_table_get_nrow(traces); tracerow++)
        {


            trace_offset  = cpl_table_get_double(traces, "Offset"    , tracerow, NULL);
            trace_number  = cpl_table_get_int   (traces, "TraceID"   , tracerow, NULL);
            trace_enabled = cpl_table_get_int   (traces, "Tracemask" , tracerow, NULL);

            if (trace_enabled != 0)
            {

                            if (cpl_table_get_nrow(traces) > 1) {
                                uves_msg("Processing trace %d", trace_number);
                            }

                /* This is UVES specific. Load linetable for the
                   two sky windows (number 1, 3) and for the object
                   window (number 2) */

                for (window = 1; window <= 3; window ++)
                {
                    uves_free_table_const ( &(linetable[window-1]) );
                    uves_free_propertylist_const( &(linetable_header[window-1]) );
                    uves_polynomial_delete_const( &(dispersion_relation[window-1]) );
                    check( uves_load_linetable_const(
                           frames,
                           false,  /* FLAMES? */
                           chip_name,
                           order_locations,
                           cpl_table_get_column_min(ordertable, "Order"),
                           cpl_table_get_column_max(ordertable, "Order"),
                           &linetable_filename,
                           &(linetable          [window-1]),
                           &(linetable_header   [window-1]),
                           &(dispersion_relation[window-1]),
                           NULL,
                           chip,
                           trace_number,
                           window),
                       "Could not load line table, window #%d", window);
                }

                uves_msg_low("Using line tables in '%s'", linetable_filename);
                /* end, UVES specific */

                /* Do the reduction + response calculation */
                cpl_free(ref_obj_name); ref_obj_name = NULL;
                uves_free_image(&reduced_spectrum);
                uves_free_image(&background);
                uves_free_propertylist(&spectrum_header);
                uves_free_table(&efficiency);
                uves_free_table(&blaze_efficiency);
                uves_free_table(&info_tbl);

                check( uves_response_process_chip(
                                       raw_image[raw_index],   /* Raw         */
                                       raw_header[raw_index],
                                       rotated_header[raw_index],
                                       master_bias,            /* Calibration */
                                       master_bias_header,
                                       master_dark,
                                       master_dark_header,
                                       master_flat,
                                       master_flat_header,
                                       ordertable,
                                       order_locations,
                                       linetable,
                                       linetable_header,
                                       dispersion_relation,
                                       flux_table,
                                       atm_extinction,
                                       chip,                  /* Parameters  */
                                       debug_mode,
                                       parameters,
                                       calc_response,
                                       PACCURACY,             /* Identification */
                                       &ref_obj_name,
                                       &reduced_spectrum,
                                       &spectrum_header,
                                       &background,
                                       &efficiency,
                                       &blaze_efficiency,
                                       &info_tbl,
                                       &extraction_slit,
                                       frames,
                                       starttime),
                   "Response computation failed");

                uves_msg("Saving products...");

                /* Calculate QC (two tables) */

                if (calc_response)
                {
                    uves_qclog_delete(&qclog[0]);
                    qclog[0] = uves_qclog_init(raw_header[raw_index], chip);

                    check( uves_efficiency_qclog(blaze_efficiency,
                                 raw_header[raw_index],
                                 chip,
                                 qclog[0],
                                 ref_obj_name),
                       "Error generating efficiency QC log");
                }

                uves_qclog_delete(&qclog_optext[0]);
                        qclog_optext[0] = cpl_table_new(0);
                /* Do not:
                   qclog_optext[0] = uves_qclog_init(raw_header[raw_index], chip);
                   because we don't want QC.DID for this
                */
                cpl_table_new_column(qclog_optext[0],"key_name", CPL_TYPE_STRING);
                cpl_table_new_column(qclog_optext[0],"key_type", CPL_TYPE_STRING);
                cpl_table_new_column(qclog_optext[0],"key_value",CPL_TYPE_STRING);
                cpl_table_new_column(qclog_optext[0],"key_help", CPL_TYPE_STRING);

                check( uves_qclog_add_sci(qclog_optext[0],
                              raw_header[raw_index],
                              raw_image[raw_index],
                              extraction_slit,
                              info_tbl),
                   "Error generating extraction QC log");

                /* Save reduced spectrum */
                cpl_free(product_filename);
                if (m_method == MERGE_NOAPPEND) {
               check( product_filename = uves_response_red_noappend_standard_filename(chip),
                   "Error getting filename");
                   prod_catg=UVES_RED_NOAPPEND_STD(chip);
                } else {

                check( product_filename = uves_response_red_standard_filename(chip),
                   "Error getting filename");
                   prod_catg=UVES_RED_STD(chip);

                }
                //uves_pfits_set_extname(spectrum_header,"reduced spectrum");
                check( uves_frameset_insert(frames,
                            reduced_spectrum,
                            CPL_FRAME_GROUP_PRODUCT,
                            CPL_FRAME_TYPE_IMAGE,
                            CPL_FRAME_LEVEL_INTERMEDIATE,
                            product_filename,
                            prod_catg,
                            raw_header[raw_index],
                            spectrum_header,
                            NULL,
                            parameters,
                            make_str(UVES_RESPONSE_ID),
                            PACKAGE "/" PACKAGE_VERSION,
                            qclog_optext,
                            starttime, false,
                            UVES_ALL_STATS),
                   "Could not add reduced spectrum '%s' (%s) to frameset",
                   product_filename, UVES_RED_STD(chip));

                uves_msg("Reduced spectrum '%s' (%s) added to frameset",
                     product_filename, UVES_RED_STD(chip));

                if (calc_response)
                {
                    /* Save efficiency table */
                    uves_free_propertylist(&efficiency_header);
                    efficiency_header = uves_propertylist_new();

                    cpl_free(product_filename);
                    check( product_filename =
                       uves_response_efficiency_filename(chip),
                       "Error getting filename");
                    //uves_pfits_set_extname(efficiency_header,"efficiency");
                    sprintf(extname,"EFFICIENCY");
                    uves_pfits_set_extname(table_header,extname);
                    check( uves_frameset_insert(
                           frames,
                           efficiency,
                           CPL_FRAME_GROUP_PRODUCT,
                           CPL_FRAME_TYPE_TABLE,
                           CPL_FRAME_LEVEL_INTERMEDIATE,
                           product_filename,
                           UVES_EFFICIENCY_TABLE(chip),
                           raw_header[raw_index],
                           efficiency_header,
                           table_header,
                           parameters,
                           make_str(UVES_RESPONSE_ID),
                           PACKAGE "/" PACKAGE_VERSION,
                           qclog,
                           starttime, true, 0),
                       "Could not add background image '%s' (%s) to frameset",
                       product_filename, UVES_EFFICIENCY_TABLE(chip));

                    uves_msg("Efficiency table '%s' (%s) added to frameset",
                         product_filename, UVES_EFFICIENCY_TABLE(chip));
                } /* end if calc_response */

                /* Save background image */
                cpl_free(product_filename);
                check( product_filename =
                   uves_response_bkg_standard_filename(chip),
                   "Error getting filename");
                //uves_pfits_set_extname(rotated_header[raw_index],"background");
                check( uves_frameset_insert(frames,
                            background,
                            CPL_FRAME_GROUP_PRODUCT,
                            CPL_FRAME_TYPE_IMAGE,
                            CPL_FRAME_LEVEL_INTERMEDIATE,
                            product_filename,
                            UVES_BKG_STD(chip),
                            raw_header[raw_index],
                            rotated_header[raw_index],
                            NULL,
                            parameters,
                            make_str(UVES_RESPONSE_ID),
                            PACKAGE "/" PACKAGE_VERSION, NULL,
                            starttime, false,
                            CPL_STATS_MIN | CPL_STATS_MAX),
                   "Could not add background image '%s' (%s) to frameset",
                   product_filename, UVES_BKG_STD(chip));

                uves_msg("Background image '%s' (%s) added to frameset",
                     product_filename, UVES_BKG_STD(chip));


                /* Save info table */
               cpl_free(product_filename);
                check( product_filename =
                   uves_order_extract_qc_standard_filename(chip),
                   "Error getting filename");
                //uves_pfits_set_extname(rotated_header[raw_index],"quality control table");
                sprintf(extname,"QC_INFO");
                uves_pfits_set_extname(table_header,extname);

                check( uves_frameset_insert(frames,
                            info_tbl,
                            CPL_FRAME_GROUP_PRODUCT,
                            CPL_FRAME_TYPE_TABLE,
                            CPL_FRAME_LEVEL_INTERMEDIATE,
                            product_filename,
                            UVES_ORDER_EXTRACT_QC(chip),
                            raw_header[raw_index],
                            rotated_header[raw_index],
                            table_header,
                            parameters,
                            make_str(UVES_RESPONSE_ID),
                            PACKAGE "/" PACKAGE_VERSION, NULL,
                            starttime, true,
                            0),
                   "Could not add extraction quality table '%s' (%s) to frameset",
                   product_filename, UVES_ORDER_EXTRACT_QC(chip));

                uves_msg("Extraction quality table '%s' (%s) added to frameset",
                     product_filename, UVES_ORDER_EXTRACT_QC(chip));



            }/* ... if trace is enabled */
            else
            {
                uves_msg_low("Skipping trace number %d", trace_number);
            }

        }/* For each trace */


        if(strcmp(PROCESS_CHIP,"REDL") == 0) {
            chip = uves_chip_get_next(chip);
        }

        uves_free_propertylist(&table_header);



    } /* For each chip */

  cleanup:
    /* Input */
    uves_free_image(&raw_image[0]);
    uves_free_image(&raw_image[1]);
    uves_free_propertylist(&raw_header[0]);
    uves_free_propertylist(&raw_header[1]);
    uves_free_propertylist(&rotated_header[0]);
    uves_free_propertylist(&rotated_header[1]);

    /* Input, calib */
    uves_free_image(&master_bias);
    uves_free_propertylist(&master_bias_header);

    uves_free_image(&master_dark);
    uves_free_propertylist(&master_dark_header);

    uves_free_image(&master_flat);
    uves_free_propertylist(&master_flat_header);

    uves_free_table(&ordertable);
    uves_free_propertylist(&ordertable_header);
    uves_polynomial_delete(&order_locations);
    uves_free_table(&traces);
    uves_free_propertylist(&table_header);
    uves_free_table_const( &(linetable[0]) );
    uves_free_table_const( &(linetable[1]) );
    uves_free_table_const( &(linetable[2]) );
    uves_free_propertylist_const( &(linetable_header[0]) );
    uves_free_propertylist_const( &(linetable_header[1]) );
    uves_free_propertylist_const( &(linetable_header[2]) );
    uves_polynomial_delete_const( &(dispersion_relation[0]) );
    uves_polynomial_delete_const( &(dispersion_relation[1]) );
    uves_polynomial_delete_const( &(dispersion_relation[2]) );
    uves_free_table( &flux_table );
    uves_free_table( &atm_extinction );

    /* Output */
    uves_qclog_delete(&qclog[0]);
    uves_qclog_delete(&qclog_optext[0]);
    uves_free_image(&background);
    uves_free_image(&reduced_spectrum);
    uves_free_propertylist(&spectrum_header);
    uves_free_propertylist(&efficiency_header);
    uves_free_table(&efficiency);
    uves_free_table(&blaze_efficiency);
    uves_free_table(&info_tbl);

    /* Local */
    uves_free_table(&catalogue_flux);
    cpl_free(product_filename);
    cpl_free(ref_obj_name);

    return;
}

/**
@@ brief QC log generation
@param table              efficiency table
@param info_tbl           table with info on object location during extraction
@param raw_header         FITS header
@param chip               chip ID
@param qclog              ouput QC log table
@param ref_obj_name       reference object name
*/
static void uves_efficiency_qclog(cpl_table* table,
                  uves_propertylist* raw_header,
                  enum uves_chip chip,
                  cpl_table* qclog,
                  const char *ref_obj_name)
{
  int i=0;
  bool new_format;

  check( new_format = uves_format_is_new(raw_header),
     "Error determining FITS header format");

  check_nomsg(uves_qclog_add_string(qclog,
                        "QC TEST1 ID",
                        "Efficiency-Test-Results",
                        "Name of QC test",
                    "%s"));

  check_nomsg(uves_qclog_add_string(qclog,
                        uves_remove_string_prefix(UVES_INSPATH,"ESO "),
                         uves_pfits_get_inspath(raw_header),
                        "Optical path used.",
                   "%s"));

  check_nomsg(uves_qclog_add_string(qclog,
                        uves_remove_string_prefix(UVES_INSMODE,"ESO "),
                         uves_pfits_get_insmode(raw_header),
                        "Instrument mode used.",
                   "%s"));

  check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_GRATNAME(chip),"ESO "),
                    uves_pfits_get_gratname(raw_header,chip),
                    "Cross disperser ID",
                    "%s"));

  check_nomsg(uves_qclog_add_common_wave(raw_header,
                                         chip, qclog));

  check_nomsg(
      uves_qclog_add_string(qclog,
                uves_remove_string_prefix(UVES_CHIP_NAME(chip),"ESO "),
                /* UVES_QC_CHIP_VAL(chip), */
                uves_pfits_get_chip_name(raw_header, chip),
                "Detector chip name",
                "%s"));

  check_nomsg(
      uves_qclog_add_double(qclog,
                uves_remove_string_prefix(UVES_GRATWLEN(chip),"ESO "),
                uves_pfits_get_gratwlen(raw_header,chip),
                "Grating central wavelength [nm] (hs).",
                "%.1f"));

  check_nomsg(
      uves_qclog_add_double(qclog,
                uves_remove_string_prefix(UVES_CONAD(new_format, chip),"ESO "),
                uves_pfits_get_conad(raw_header, chip),
                "Conversion from ADUs to electrons",
                "%8.2f"));


  check_nomsg(
      uves_qclog_add_double(qclog,
                uves_remove_string_prefix(UVES_QC_UIT(new_format, chip), "ESO "),
                uves_pfits_get_uit(raw_header),
                "user defined subintegration time",
                "%8.0f"));

  check_nomsg(
      uves_qclog_add_double(qclog,
                "AIRMASS",
                (uves_pfits_get_airmass_start(raw_header) +
                 uves_pfits_get_airmass_end(raw_header))/2.0,
                "Averaged airmass",
                "%8.4f"));

  check_nomsg(
      uves_qclog_add_string(qclog,
                uves_remove_string_prefix(UVES_TARG_NAME, "ESO "),
                ref_obj_name,
                "OB target name",
                "%s"));


  for(i = 0; i < cpl_table_get_nrow(table); i++)
      {
      char key_name[25];

      int order = cpl_table_get_int(table, "Order", i, NULL);

      sprintf(key_name,"QC BLAZEFF%d", order);

      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        cpl_table_get_double(table, "Eff", i, NULL),
                        "Blaze Efficiency",
                        "%13.6f"));
      /*
        uves_msg("QC-LOG: Wlen =%g Eff=%g",
        cpl_table_get_double(table,"Wave",i,NULL),
        cpl_table_get_double(table,"Eff",i,NULL));
      */

      sprintf(key_name,"QC BLAZWLEN%d", order);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        cpl_table_get_double(table, "Wave", i, NULL)/10.,
                        "Blaze wavelength",  /* nm */
                        "%13.6f"));
      }

  /* Are these QC parameters needed anywhere? */
#if 0
  for(i = 0; i < cpl_table_get_nrow(info_tbl); i++)
      {
      char key_name[25];

      int order = cpl_table_get_int(info_tbl, "Order", i, NULL);

      sprintf(key_name,"QC ORDER NUM%d", order);
      check_nomsg(uves_qclog_add_int(qclog,
                     key_name,
                     order,
                     "Order Number",
                     "%d"));
      /*
        uves_msg("QC-LOG: Order =%d S/N=%g",
        cpl_table_get_int(info_tbl,"Order",i,NULL),
        cpl_table_get_double(info_tbl,"S/N",i,NULL));
      */

      sprintf(key_name,"QC OBJ SN%d", order);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        cpl_table_get_double(info_tbl,"S/N",i,NULL),
                        "Order S/N",
                        "%f13.6"));
      }
#endif

 cleanup:
  return;
}

/**@}*/
