/*                                                                              *
 *   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: 2010-12-16 16:57:40 $
 * $Revision: 1.68 $
 * $Name: not supported by cvs2svn $
 *
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_infrastructure Recipe infrastructure
 *
 * This module provides functions useful for recipes communicating with
 * sub-steps and other recipes (such as propagation of parameters from a
 * sub-recipe or sub-step, and invoking a recipe)
 */
/*----------------------------------------------------------------------------*/

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

#include <uves.h>
#include <uves_parameters.h>
#include <uves_dump.h>
#include <uves_backsub.h>
#include <uves_extract.h>
#include <uves_rebin.h>
#include <uves_mdark_impl.h>
#include <uves_corrbadpix.h>
#include <uves_reduce.h>
#include <uves_utils_wrappers.h>
#include <uves_error.h>
#include <uves_msg.h>

#include <cpl.h>
#include <string.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
static int propagate(const char *substep_id, const cpl_parameterlist *sub_parameters, 
             cpl_parameterlist *parent_parameters,
             const char *parent_id, const char *context);
static cpl_parameter *
create_parameter_enum_int   (const char *name, cpl_type type,
                 const char *description, const char *context, 
                 int default_value, int size, int *values);
static cpl_parameter *
create_parameter_enum_double(const char *name, cpl_type type, const char *description,
                 const char *context, double default_value, 
                 int size, double *values);
static cpl_parameter *
create_parameter_enum_string(const char *name, cpl_type type, const char *description,
                 const char *context, const char *default_value, 
                 int size, const char **values);
/*-----------------------------------------------------------------------------
                            Defines
 -----------------------------------------------------------------------------*/
#define FAIL(return_code, error_code, ...) do {       \
        cpl_msg_error(__func__, __VA_ARGS__);         \
        if (cpl_error_get_code() == CPL_ERROR_NONE) { \
          cpl_error_set(__func__, error_code);        \
        }                                             \
        return return_code;                           \
        } while(false)

/**@{*/

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

/*---------------------------------------------------------------------------*/
/**
   @brief    Define parameters used for detector's trap correction
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @param    recipe_id           recipe name
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*---------------------------------------------------------------------------*/

cpl_error_code
uves_corr_traps_define_parameters(cpl_parameterlist * parameters,
                                  const char *recipe_id)
{

   //const char *context = "clean";

   char full_name[256];


    
   //
   const char* name = "clean_traps";
   sprintf(full_name,"%s.%s",recipe_id,name);
   /* PIPE-5514: decided to always have clean-trap false */
   if((strcmp(recipe_id,"uves_obs_scired") == 0) ||
      (strcmp(recipe_id,"uves_obs_spatred") == 0) ||
      (strcmp(recipe_id,"uves_cal_mbias") == 0) ||
      (strcmp(recipe_id,"uves_cal_mkmaster") == 0) ||
      (strcmp(recipe_id,"uves_cal_tflat") == 0) ) {
	   cpl_parameter *p;
      uves_parameter_new_value(p, full_name,
                               CPL_TYPE_BOOL,
                               "Clean detector traps. "
                               "If TRUE detector traps are interpolated."
                               "The bad pixels are replaced by the average of the"
                               "nearest good pixels in the same column, or simply marked "
                               "as bad. The positions of bad pixels are hard-coded "
                               "(as function of UVES chip).",
                               recipe_id,
                               CPL_FALSE);
 

      cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
      cpl_parameterlist_append(parameters, p);

   } else {

      uves_msg("Creation of trap not supported for recipe: '%s'",
               recipe_id);

   }

   if (cpl_error_get_code() != CPL_ERROR_NONE)
   {
 
      cpl_msg_error(__func__, 
                    "Creation of trap column parameters failed: '%s'",
                    cpl_error_get_where());
   }
 
    
   return cpl_error_get_code();


}



/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all frame stacking recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/

cpl_error_code
uves_master_stack_define_parameters(cpl_parameterlist *parlist, const char *recipe_id)
{
 
    const char *name = "";
    char full_name[256];
 
    cpl_parameter *p;

    {
    name = "stack_method";
    sprintf(full_name,"%s.%s",recipe_id,name);
    uves_parameter_new_enum(p, full_name,
                    CPL_TYPE_STRING,
                    "Method used to build master frame ",
                    recipe_id,
                            "median",2,"median","mean");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);

    }

    {
    name = "klow";
    sprintf(full_name,"%s.%s",recipe_id,name);
    uves_parameter_new_range(p, full_name,
                    CPL_TYPE_DOUBLE,
                    "Kappa used to clip low level values, when method is set to 'mean' ",
                    recipe_id,
                             5.,0.,100.);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);

    }

    {
    name = "khigh";
    sprintf(full_name,"%s.%s",recipe_id,name);
    uves_parameter_new_range(p, full_name,
                    CPL_TYPE_DOUBLE,
                    "Kappa used to clip high level values, when method is set to 'mean' ",
                    recipe_id,
                             5.,0.,100.);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);

    }



    {
    name = "niter";
    sprintf(full_name,"%s.%s",recipe_id,name);
 
    uves_parameter_new_range(p, full_name,
                    CPL_TYPE_INT,
                    "Number of kappa sigma iterations, when method is set to 'mean' ",
                    recipe_id,
                             5,0,100);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);

    }

    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of kappa sigma parameters failed: '%s'",
              cpl_error_get_where());
    }
    
    return cpl_error_get_code();
}



/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters for flat recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/

cpl_error_code
uves_master_flat_define_parameters(cpl_parameterlist *parlist, const char *recipe_id)
{
 
    const char *name = "";
    char full_name[256];
 
    cpl_parameter *p;


    name = "norm_method";
    sprintf(full_name,"%s.%s",recipe_id,name);
    uves_msg("recipe id %s",recipe_id);
    uves_parameter_new_enum(p, full_name,
                            CPL_TYPE_STRING,
                            "Method used to build master frame ",
                            recipe_id,
                            (strstr(recipe_id,"flames") !=NULL) ? "exptime" : "explevel",
                            2,"exptime","explevel");

    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);



    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of master flat parameters failed: '%s'",
              cpl_error_get_where());
    }
    
    return cpl_error_get_code();
}



/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_global_parameters(cpl_parameterlist *parlist)
{
    const char *context = "uves";
    const char *name = "";
    char *full_name = NULL;
    cpl_parameter *p;

    {
    name = "debug";
    full_name = uves_sprintf("%s.%s", context, name);
    uves_parameter_new_value(p, full_name,
                    CPL_TYPE_BOOL,
                    "Whether or not to save intermediate "
                    "results to local directory",
                    context,
                    false);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);
    cpl_free(full_name);
    }


    {
        name = "plotter";
        full_name = uves_sprintf("%s.%s", context, name);
        uves_parameter_new_value(
            p, full_name,
            CPL_TYPE_STRING,
            "Any plots produced by the recipe "
            "are redirected to the command specified "
            "by this parameter. The plotting command "
            "must contain the substring 'gnuplot' and "
            "must be able to parse gnuplot syntax on its "
            "standard input. "
            "Valid examples of such a command may include "
            "'gnuplot -persist' and 'cat > mygnuplot$$.gp'. "
            "A finer control of the plotting options can "
            "be obtained by writing an "
            "executable script, e.g. my_gnuplot.pl, that "
            "executes gnuplot after setting the desired gnuplot "
            "options (e.g. set terminal pslatex color). "
            "To turn off plotting, set this parameter to 'no'",
            context,
            "no");

        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);
    }



   {
    name = "process_chip";
    full_name = uves_sprintf("%s.%s", context, name);
    uves_parameter_new_enum(p, full_name,
                    CPL_TYPE_STRING,
                    "For RED arm data process the "
			     "redl, redu, or both chip(s)",
                    context,
                            "both",5,"both","redl","redu","REDL","REDU");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);
    cpl_free(full_name);
    }

    if (0)
        /* The meaning of this parameter often escapes the developers,
           so let's not expose it to the users */
        {
        name = "msginfolevel";
    full_name = uves_sprintf("%s.%s", context, name);
    uves_parameter_new_range(p, full_name,
                    CPL_TYPE_INT,
                    "This parameter controls the subdivision "
                    "of the 'info' message level (set e.g. with "
                    "esorex' --msg-level). The higher the value "
                    "of this parameter, the more messages are "
                    "printed at the info level. For minimum "
                    "output, set to zero. Increase the level "
                    "(to 1, 2, 3, ...) for more output. The "
                    "value -1 is a special value meaning maximum "
                    "output",
                    context,
                    -1,                 /* Default */
                    -1, INT_MAX);       /* Range   */
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);
    cpl_free(full_name);
    }

    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of global parameters failed: '%s'",
              cpl_error_get_where());
    }
    
    return cpl_error_get_code();
}
/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_extract_for_response_chain_parameters(cpl_parameterlist *parameters)
{

    const char *name = "";
    char *full_name = NULL;
    //char *context = NULL;
    cpl_parameter *p = NULL;
/*
    context = uves_sprintf("%s.%s.%s", make_str(UVES_REDCHAIN_ID), make_str(UVES_RESPONSE_ID), name);
*/  
    {
        name = "uves_cal_response.reduce.extract.method";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);

        uves_parameter_new_enum(p, full_name,
                                CPL_TYPE_STRING,
                                "Extraction method. (2d/optimal not supported by uves_cal_wavecal, weighted supported only by uves_cal_wavecal, 2d not supported by uves_cal_response)",
                                UVES_EXTRACT_ID,
                                "optimal",
                                5,
                                "average",
                                "linear",
                                "2d",
                                "weighted",
                                "optimal");
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    {
        name = "uves_cal_response.reduce.extract.kappa";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
        
        uves_parameter_new_range(p, full_name,
                                 CPL_TYPE_DOUBLE,
                                 "In optimal extraction mode, this is the "
                                 "threshold for bad (i.e. hot/cold) "
                                 "pixel rejection. If a pixel deviates more than "
                                 "kappa*sigma (where sigma is "
                                 "the uncertainty of the pixel flux) from "
                                 "the inferred spatial profile, its "
                                 "weight is set to zero. Range: [-1,100]. If this parameter "
                                 "is negative, no rejection is performed.",
                                 UVES_EXTRACT_ID,
                                 10.0,-1.,100.);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    {
        name = "uves_cal_response.reduce.extract.chunk";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
        
        uves_parameter_new_range(p, full_name,
                                 CPL_TYPE_INT,
                                 "In optimal extraction mode, the chunk size (in pixels) "
                                 "used for fitting the analytical profile (a fit of the "
                                 "analytical profile to single bins would suffer from "
                                 "low statistics).",
                                 UVES_EXTRACT_ID,
                                 32,
                                 1, INT_MAX);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }
    
    {
        name = "uves_cal_response.reduce.extract.profile";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
        
        uves_parameter_new_enum(p, full_name,
                                CPL_TYPE_STRING,
                                "In optimal extraction mode, the kind of profile to use. "
                                "'gauss' gives a Gaussian profile, 'moffat' gives "
                                "a Moffat profile with beta=4 and a possible linear sky "
                                "contribution. 'virtual' uses "
                                "a virtual resampling algorithm (i.e. measures and "
                                "uses the actual object profile). "
                                "'constant' assumes a constant spatial profile and "
                                "allows optimal extraction of wavelength "
                                "calibration frames. 'auto' will automatically "
                                "select the best method based on the estimated S/N of the "
                                "object. For low S/N, 'moffat' or 'gauss' are "
                                "recommended (for robustness). For high S/N, 'virtual' is "
                                "recommended (for accuracy). In the case of virtual resampling, "
                                "a precise determination of the order positions is required; "
                                "therefore the order-definition is repeated "
                                "using the (assumed non-low S/N) science frame",
                                UVES_EXTRACT_ID,
                "auto",
                                5,
                                "constant",
                                "gauss",
                                "moffat",
                                "virtual",
                                "auto");
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    {
        name = "uves_cal_response.reduce.extract.skymethod";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
        
        uves_parameter_new_enum(p, full_name,
                                CPL_TYPE_STRING,
                                "In optimal extraction mode, the sky subtraction method "
                "to use. 'median' estimates the sky as the median of pixels "
                "along the slit (ignoring pixels close to the object), whereas "
                "'optimal' does a chi square minimization along the slit "
                "to obtain the best combined object and sky levels. The optimal "
                "method gives the most accurate sky determination but is also "
                "a bit slower than the median method",
                                UVES_EXTRACT_ID,
                "optimal",
                                2,
                                "median",
                                "optimal");
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    {
        name = "uves_cal_response.reduce.extract.oversample";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
        
        uves_parameter_new_range(p, full_name,
                                 CPL_TYPE_INT,
                                 "The oversampling factor used for the virtual "
                                 "resampling algorithm. If negative, the value 5 is "
                                 "used for S/N <=200, and the value 10 is used if the estimated "
                                 "S/N is > 200",
                                 UVES_EXTRACT_ID,
                                 -1,
                                 -2, INT_MAX);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    {
        name = "uves_cal_response.reduce.extract.best";
        full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    
    uves_parameter_new_value(p, full_name,
                 CPL_TYPE_BOOL,
                 "(optimal extraction only) "
                 "If false (fastest), the spectrum is extracted only once. "
                 "If true (best), the spectrum is extracted twice, the "
                 "second time using improved variance estimates "
                 "based on the first iteration. Better variance "
                 "estimates slightly improve the obtained signal to "
                 "noise but at the cost of increased execution time",
                 UVES_EXTRACT_ID,
                 true);
    
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);
    }


    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of extraction parameters failed: '%s'",
              cpl_error_get_where());
    }
    return cpl_error_get_code();

}

/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_rebin_for_response_chain_parameters(cpl_parameterlist *parameters)
{







    
    {
    const char* name = "uves_cal_response.reduce.rebin.wavestep";
    char* full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
    cpl_parameter *p = NULL;
    uves_parameter_new_range(p, full_name,
                 CPL_TYPE_DOUBLE,
                 "The bin size (in w.l.u.) in wavelength space. "
                 "If negative, a step size of "
                 "2/3 * ( average pixel size ) is used.",
                 UVES_REBIN_ID,
                             -1.0,-1.0,DBL_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);

    name = "uves_cal_response.reduce.rebin.scale";
    full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
    uves_parameter_new_value(p, full_name,
                 CPL_TYPE_BOOL,
                 "Whether or not to multiply by the factor "
                 "dx/dlambda (pixels per wavelength) "
                 "during the rebinning. This option is disabled "
                 "as default in concordance with the "
                 "method used in the MIDAS pipeline. This "
                 "option should be set to true "
                 "to convert the observed flux (in pixel-space) "
                 "to a flux per wavelength (in "
                 "wavelength-space).",
                 UVES_REBIN_ID,
                 false);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);
    }
    
  if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of background parameters failed: '%s'",
              cpl_error_get_where());
    }
    

    return cpl_error_get_code();

}


/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_reduce_for_response_chain_parameters(cpl_parameterlist *parameters)
{

   const char *name = NULL;
   char *full_name = NULL;
   cpl_parameter *p;

  
    /******************
     *  Slit geometry *
     ******************/
    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
        name = "uves_cal_response.reduce.slitlength";
        full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_range(p, full_name,
                    CPL_TYPE_DOUBLE,
                    "Extraction slit length (in pixels). "
                    "If negative, the value "
                    "inferred from the raw frame header is used",
                    UVES_REDUCE_ID,
                    -1.0,
                    -2.0, DBL_MAX);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
        name = "uves_cal_response.reduce.skysub";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_value(p, full_name,
                     CPL_TYPE_BOOL,
                     "Do sky-subtraction (only applicable to linear "
                     "and average extractions)?",
                     UVES_REDUCE_ID,
                     true);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
        name = "uves_cal_response.reduce.objoffset";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_value(p, full_name,
                     CPL_TYPE_DOUBLE,
                     "Offset (in pixels) of extraction slit "
                                     "with respect to center of order. "
                                     "This parameter applies to linear/average/"
                                     "optimal extraction. "
                                     "For linear/average extraction, if the related "
                                     "parameter objslit is negative, the offset is "
                     "automatically determined by measuring the "
                     "actual object position. ",
                     UVES_REDUCE_ID,
                     0.0);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    if (cpl_error_get_code() == CPL_ERROR_NONE)
        {
            name = "uves_cal_response.reduce.objslit";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
            
            uves_parameter_new_range(p, full_name,
                                     CPL_TYPE_DOUBLE,
                                     "Object window size (in pixels). This must "
                                     "be less than the total slit length. If "
                                     "negative, the default value (half of full "
                                     "slit length) is used. The upper and lower "
                                     "sky windows are defined as the part of the "
                                     "full slit (if any) outside the object "
                                     "window. The center of the object window "
                                     "is determined by the offset parameter. "
                                     "This parameter does not apply to optimal "
                                     "extraction.",
                                     UVES_REDUCE_ID,
                                     -1.0,
                                     -2.0, DBL_MAX);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
        name = "uves_cal_response.reduce.tiltcorr";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_value(p, full_name,
                     CPL_TYPE_BOOL,
                     "If enabled (recommended), the provided "
                                     "dispersion solutions "
                                     "obtained at different slit positions are "
                                     "interpolated linearly at the actually "
                                     "measured position of the object/sky. "
                                     "Line tilt correction is currently not supported "
                                     "for 2d extraction, in which case the "
                                     "dispersion solution obtained at the middle of "
                                     "the slit is always used.",
                     UVES_REDUCE_ID,
                     true);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }



    /*****************
     *  Flatfielding *
     *****************/

    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
        name = "uves_cal_response.reduce.ffmethod";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_enum(p, full_name,
                    CPL_TYPE_STRING,
                    "Flat-fielding method. If set to 'pixel', "
                    "flat-fielding is done in pixel-pixel space "
                    "(before extraction); if set to 'extract', "
                    "flat-fielding is performed in pixel-order "
                    "space (i.e. after extraction). If set to "
                    "'no', no flat-field correction is done",
                    UVES_REDUCE_ID,
                    "extract",    /* 'Pixel' method is usually preferred,
                             but do like UVES/MIDAS */
                    3, 
                    "pixel", "extract", "no");
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
    }

    /*****************
     *  Blaze corr.  *
     *****************/

    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {

/*
        name = "uves_cal_response.reduce.blazecorr";
         full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
        uves_parameter_new_value(p, full_name,
                    CPL_TYPE_BOOL,
                    "(highly experimental, recommended=false) "
                    "Apply a correction for the different shapes "
                    "of flat-field and science blaze functions? "
                    "For this to be possible, flat-fielding method "
                    "must be different from 'no'.",
                    UVES_REDUCE_ID,
                    false);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parameters, p);
        cpl_free(full_name);
*/
    }

 

    /*****************
     *   Merging     *
     *****************/
    if (cpl_error_get_code() == CPL_ERROR_NONE)
    {
      name = "uves_cal_response.reduce.merge";
       full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
      uves_parameter_new_enum(p, full_name,
			      CPL_TYPE_STRING,
                              "Order merging method. If 'optimal', the "
                              "flux in the overlapping region is set "
                              "to the (optimally computed, using the "
                              "uncertainties) average of single order "
                              "spectra. If 'sum', the flux in the "
                              "overlapping region is computed as the "
                              "sum of the single order spectra. If 'noappend' "
                              "the spectrum is simply rebinned but not merged."
                              "If flat-fielding is done, method 'optimal' "
                              "is recommended, otherwise 'sum'.",
                              UVES_REDUCE_ID,
                              "optimal",
                              3, 
			      "optimal", "sum", "noappend");
        
      cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
      cpl_parameterlist_append(parameters, p);
      cpl_free(full_name);


      name = "uves_cal_response.reduce.merge_delt1";
       full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
        
      uves_parameter_new_range(p, full_name,
			       CPL_TYPE_DOUBLE,
                               "Order merging left hand (short wavelength) "
                               "cut. To reduce the amount of order "
                               "overlapping regions we allow to cut short and "
                               "long wavelength ranges. "
                               "This may reduce the ripple possibly "
                               "introduced by the order merging. "
                               "Suggested values are: "
                               "10 (W<=390), 12 (390<W<=437, 520<W<=564), "
			       "14 (437<W<=520, 564<W) ",
                               UVES_REDUCE_ID,
                               0.,0.,20.);
        
      cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
      cpl_parameterlist_append(parameters, p);
      cpl_free(full_name);


      name = "uves_cal_response.reduce.merge_delt2";
       full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
       
      uves_parameter_new_range(p, full_name,
			       CPL_TYPE_DOUBLE,
                               "Order merging right hand (long wavelength) "
                               "cut. To reduce the amount of order "
                               "overlapping regions we allow to cut short and "
                               "long wavelength ranges. "
                               "This may reduce the ripple possibly "
                               "introduced by the order merging. "
                               "Suggested values is 4",
                               UVES_REDUCE_ID,
                               0.,0.,20.);
        
      cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
      cpl_parameterlist_append(parameters, p);
      cpl_free(full_name);



    }


  if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of background parameters failed: '%s'",
              cpl_error_get_where());
    }
    

    return cpl_error_get_code();

}
/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_background_for_response_chain_parameters(cpl_parameterlist *parameters)
{


   //const char *context = "uves"; 
   const char *name = NULL;
   char *full_name = NULL;
   cpl_parameter *p;

 

    /**************************
     * Inter-order-background *
     **************************/

    name = "uves_cal_response.reduce.backsub.mmethod";
    full_name = uves_sprintf("%s.%s%s",  make_str(UVES_REDCHAIN_ID),"", name);
    
    uves_parameter_new_enum(p, full_name,
                   CPL_TYPE_STRING,
                   "Background measuring method. If equal to 'median' "
                   "the background is sampled using the median of a subwindow. "
                   "If 'minimum', the subwindow minimum value is used. "
                   "If 'no', no background subtraction is done.",
                   UVES_BACKSUB_ID,
                   "median",                        /* Default */
                   3,                               /* Number of options */
                   "median", "minimum", "no");      /* List of options */
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);

    //
    name = "uves_cal_response.reduce.backsub.npoints";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    uves_parameter_new_range(p, full_name,
                 CPL_TYPE_INT,
                 "This is the number of columns in interorder space "
                 "used to sample the background.",
                 UVES_BACKSUB_ID,
                 82, 0, INT_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);
    
    //
    name = "uves_cal_response.reduce.backsub.radiusy";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    uves_parameter_new_range(p, full_name,
                CPL_TYPE_INT,
                "The height (in pixels) of the background sampling "
                "window is (2*radiusy + 1). "
                "This parameter is not corrected for binning.",
                UVES_BACKSUB_ID,
                2, 0, INT_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);
    
    //
    name = "uves_cal_response.reduce.backsub.sdegree";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    uves_parameter_new_range(p, full_name,
                 CPL_TYPE_INT,
                 "Degree of interpolating splines. Currently "
                 "only degree = 1 is supported",
                 UVES_BACKSUB_ID,
                 1, 0, INT_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);

    //
    name = "uves_cal_response.reduce.backsub.smoothx";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    uves_parameter_new_range(p, full_name,
                 CPL_TYPE_DOUBLE,
                 "If spline interpolation is used to measure the background, "
                 "the x-radius of the post-smoothing window is "
                 "(smoothx * image_width). Here, 'image_width' is the image "
                 "width after binning. If negative, the default values are used: "
                 make_str(BACKSUB_FLAT_SMOOTHX_BLUE) " for blue flat-field frames, "
                 make_str(BACKSUB_FLAT_SMOOTHX_RED) " for red flat-field frames, "
                 make_str(BACKSUB_SCI_SMOOTHX_BLUE) " for blue science frames and "
                 make_str(BACKSUB_SCI_SMOOTHX_RED) " for red science frames.",
                 UVES_BACKSUB_ID,
                 -1.0, -DBL_MAX, DBL_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);
    
    //
    name = "uves_cal_response.reduce.backsub.smoothy";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
    uves_parameter_new_range(p, full_name,
                 CPL_TYPE_DOUBLE,
                 "If spline interpolation is used to measure the "
                 "background, the y-radius of the post-smoothing "
                 "window is (smoothy * image_height). Here, "
                 "'image_height' is the image height after binning. "
                 "If negative, the default values are used: "
                 make_str(BACKSUB_FLAT_SMOOTHY_BLUE) " for blue flat-field frames, "
                 make_str(BACKSUB_FLAT_SMOOTHY_RED) " for red flat-field frames, "
                 make_str(BACKSUB_SCI_SMOOTHY_BLUE) " for blue science frames and "
                 make_str(BACKSUB_SCI_SMOOTHY_RED) " for red science frames.",
                 UVES_BACKSUB_ID,
                 -1.0, -DBL_MAX, DBL_MAX);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parameters, p);
    cpl_free(full_name);


    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of background parameters failed: '%s'",
              cpl_error_get_where());
    }
    
    return cpl_error_get_code();

}



/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_efficiency_for_response_chain_parameters(cpl_parameterlist *parlist)
{

    char *full_name = NULL;
    cpl_parameter* p=NULL;
      const char* name = NULL;



    /**********************
     *  Extraction-Merge  *
     **********************/


    /* For the efficiency step: Set default extraction method to 'linear',
     * flatfield_method to 'no', blazecorrection to 'false' and merge to
     * 'sum' (because optimal merging doesn't make sense without flatfielding)
     */
    
    {

      name = "uves_cal_response.efficiency.reduce.extract.method";
      const char* value = "linear";

      full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                     "Extraction method."
                     "<average | linear | weighted | optimal>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);
    
      name = "uves_cal_response.efficiency.reduce.ffmethod";
      value = "no";

     full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                      "Flat-fielding method. If set to 'pixel', flat-fielding "
                      "is done in pixel-pixel space (before extraction); if "
                       "set to 'extract', flat-fielding is performed in "
                       "pixel-order space (i.e. after extraction). If set to "
                       "'no', no flat-field correction is done. <pixel | "
				"extract | no>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);

      name = "uves_cal_response.efficiency.reduce.merge";
      value = "sum";

      full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                     "Order merging method. If 'optimal', the flux in the "
                     "overlapping region is set to the (optimally computed, "
                     "using the uncertainties) average of single order "
                     "spectra. If 'sum', the flux in the overlapping region "
                     "is computed as the sum of the single order spectra."
                     "If 'noappend' the spectrum is simply rebinned but not "
                     "merged.If flat-fielding is done, method 'optimal' is "
                     "recommended, otherwise 'sum'. <optimal | sum | "
                     "noappend>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);



    const char *param = "linear";

    if (uves_set_parameter_default(parlist,
                       make_str(UVES_REDCHAIN_ID), "uves_cal_response.efficiency.reduce.extract.method",
                       CPL_TYPE_STRING, &param) != CPL_ERROR_NONE)
        {
        return -1;
        }


    }


   {

      name = "uves_cal_response.efficiency.reduce.best";
     full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);
     
    uves_parameter_new_value(p, full_name,
                 CPL_TYPE_BOOL,
                 "(optimal extraction only) "
                 "If false (fastest), the spectrum is extracted only once. "
                 "If true (best), the spectrum is extracted twice, the "
                 "second time using improved variance estimates "
                 "based on the first iteration. Better variance "
                 "estimates slightly improve the obtained signal to "
                 "noise but at the cost of increased execution time",
                 UVES_EXTRACT_ID,
                 true);
    
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);
    cpl_free(full_name);
    }



    /****************
     *  Efficiency  *
     ****************/

    {

    name = "uves_cal_response.efficiency.paccuracy";
    full_name = uves_sprintf("%s.%s%s", make_str(UVES_REDCHAIN_ID), "", name);

       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_DOUBLE,
              "The pointing accuracy (in arcseconds) used to "
               "identify the observed star with a "
               "catalogue star. If the angular separation is "
               "less than this number, the identification is made.",
                     UVES_REDUCE_ID,
                     60.0);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);

    }
    


    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of efficiency parameters failed: '%s'",
              cpl_error_get_where());
    }

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Define parameters common to all recipes
   @param    parlist             The parameter list where the new parameters
                                 are inserted
   @return  CPL_ERROR_NONE iff okay.

   The parameters are defined using the context 'uves'.
*/
/*----------------------------------------------------------------------------*/
cpl_error_code
uves_define_efficiency_parameters(cpl_parameterlist *parlist)
{

    char *full_name = NULL;
    cpl_parameter* p=NULL;
      const char* name = NULL;



    /**********************
     *  Extraction-Merge  *
     **********************/


    /* For the efficiency step: Set default extraction method to 'linear',
     * flatfield_method to 'no', blazecorrection to 'false' and merge to
     * 'sum' (because optimal merging doesn't make sense without flatfielding)
     */
    
    {


      name = "efficiency.reduce.extract.method";
      const char* value = "linear";

      full_name = uves_sprintf("%s.%s",  make_str(UVES_RESPONSE_ID), name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                     "Extraction method. "
                     "<average | linear | weighted | optimal>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);

    
      name = "efficiency.reduce.ffmethod";
      value = "no";

      full_name = uves_sprintf("%s.%s",  make_str(UVES_RESPONSE_ID), name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                      "Flat-fielding method. If set to 'pixel', flat-fielding "
                      "is done in pixel-pixel space (before extraction); if "
                       "set to 'extract', flat-fielding is performed in "
                       "pixel-order space (i.e. after extraction). If set to "
                       "'no', no flat-field correction is done. <pixel | "
				"extract | no>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);

      name = "efficiency.reduce.merge";
      value = "sum";

      full_name = uves_sprintf("%s.%s",  make_str(UVES_RESPONSE_ID), name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_STRING,
                     "Order merging method. If 'optimal', the flux in the "
                     "overlapping region is set to the (optimally computed, "
                     "using the uncertainties) average of single order "
                     "spectra. If 'sum', the flux in the overlapping region "
                     "is computed as the sum of the single order spectra."
                     "If 'noappend' the spectrum is simply rebinned but not "
                     "merged.If flat-fielding is done, method 'optimal' is "
                     "recommended, otherwise 'sum'. <optimal | sum | "
                     "noappend>",
                     UVES_REDUCE_ID,
                     value);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);




    const char *param = "linear";

    if (uves_set_parameter_default(parlist,
                       make_str(UVES_RESPONSE_ID), "efficiency.reduce.extract.method",
                       CPL_TYPE_STRING, &param) != CPL_ERROR_NONE)
        {
        return -1;
        }


/* 
    if (uves_set_parameter_default(parlist, 
                       make_str(UVES_RESPONSE_ID), "efficiency.reduce.extract.best",
                       CPL_TYPE_BOOL, &bool_param) != CPL_ERROR_NONE)
        {
        return -1;
        }
*/  

    }


   {

      name = "efficiency.reduce.best";
      full_name = uves_sprintf("%s.%s",  make_str(UVES_RESPONSE_ID), name);
     
    uves_parameter_new_value(p, full_name,
                 CPL_TYPE_BOOL,
                 "(optimal extraction only) "
                 "If false (fastest), the spectrum is extracted only once. "
                 "If true (best), the spectrum is extracted twice, the "
                 "second time using improved variance estimates "
                 "based on the first iteration. Better variance "
                 "estimates slightly improve the obtained signal to "
                 "noise but at the cost of increased execution time",
                 UVES_EXTRACT_ID,
                 true);
    
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameterlist_append(parlist, p);
    cpl_free(full_name);
    }



    /****************
     *  Efficiency  *
     ****************/

    {
       /* const char *recipe_id = make_str(UVES_RESPONSE_ID); */
    const char *subcontext = "efficiency";
    const char* name="paccuracy";
    char *context=uves_sprintf("%s.%s",make_str(UVES_RESPONSE_ID),subcontext);
    // paccuracy 


/*
    uves_par_new_value("paccuracy",
               CPL_TYPE_DOUBLE,
               "The pointing accuracy (in arcseconds) used to "
               "identify the observed star with a "
               "catalogue star. If the angular separation is "
               "less than this number, the identification is made.",
               60.0);
*/


    full_name = uves_sprintf("%s.%s", context,name);
       uves_parameter_new_value(p, full_name,
                     CPL_TYPE_DOUBLE,
              "The pointing accuracy (in arcseconds) used to "
               "identify the observed star with a "
               "catalogue star. If the angular separation is "
               "less than this number, the identification is made.",
                     context,
                     60.0);
        
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
        cpl_parameterlist_append(parlist, p);
        cpl_free(full_name);
        cpl_free(context);




    }
    


    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__, "Creation of efficiency parameters failed: '%s'",
              cpl_error_get_where());
    }

    return cpl_error_get_code();
}




/*----------------------------------------------------------------------------*/
/**
   @brief    Call a recipe
   @param    recipe_id      Name of recipe to execute
   @param    parameters     Parameter list
   @param    frames         Input frame set
   @param    caller_id      Name of caller recipe
   @param    context        An optional parent recipe context. (Ignored if NULL).

   @return   0 iff recipe execution succeeded


   The specified recipe is invoked with the provided frame set and the parameters
   from the provided list belonging to the specified context.
*/
/*----------------------------------------------------------------------------*/

int
uves_exec_recipe(int (*get_info)(cpl_pluginlist *), 
                 const char *recipe_domain,
                 const cpl_parameterlist *parameters, 
                 cpl_frameset *frames,
                 const char *caller_id, const char *context)
{
    cpl_pluginlist *list  = NULL;
    cpl_plugin *plugin    = NULL;
    cpl_recipe *recipe    = NULL;

    const char *recipe_id = NULL;
    cpl_parameter *p      = NULL;
    char *parent_name     = NULL;
    char *sub_domain      = NULL;
    int status = 0;

    bool must_destroy_plugin = false;   /* Indicates if recipe_create() 
                       has been called */

    /* Check input */
    assure(recipe_domain != NULL, CPL_ERROR_NULL_INPUT, "Null recipe message domain");
    assure(parameters != NULL, CPL_ERROR_NULL_INPUT, "Null parameter list");
    assure(frames != NULL, CPL_ERROR_NULL_INPUT, "Null frame set");
    assure(caller_id != NULL, CPL_ERROR_NULL_INPUT, "Null caller recipe name");
    /* 'context' may be NULL */    

    /* Get the sub-recipe plugin */
    check( list = cpl_pluginlist_new(),
       "Error allocating plugin list");

    /* Get info about recipe */
    status = get_info(list);

    assure( status == 0, CPL_ERROR_ILLEGAL_INPUT, 
        "Could not get info about recipe");

    /* Get default parameter list */
    check( plugin = cpl_pluginlist_get_first(list), "Error getting plugin");
    assure( plugin != NULL, CPL_ERROR_ILLEGAL_INPUT,
            "Plugin '%s' returned empty plugin list", recipe_id);
    assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE, 
            CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");
    recipe = (cpl_recipe *) plugin;
    
    recipe_id = cpl_strdup(cpl_plugin_get_name(plugin));

    /* Call initializer function */
    must_destroy_plugin = true;
    assure( cpl_plugin_get_init(plugin)(plugin) == 0, CPL_ERROR_ILLEGAL_INPUT,
        "Error initializing recipe");
    assure( recipe->parameters != NULL, CPL_ERROR_ILLEGAL_INPUT,
        "Recipe '%s' returned NULL parameter list", recipe_id);

    /* For each recipe parameter x:
       Set to value of C.x (from argument parameter list)

       Parameters in 'uves' context are simply overwritten
    */
    for (p = cpl_parameterlist_get_first(recipe->parameters); 
     p != NULL;
     p = cpl_parameterlist_get_next(recipe->parameters) )
    {
        const char *name          = cpl_parameter_get_name(p);   
        const char *subcontext    = cpl_parameter_get_context(p);
        cpl_type type             = cpl_parameter_get_type(p);
        
        const cpl_parameter *parent;
        
        if (strcmp(subcontext, "uves") == 0)
        {
            parent_name = uves_sprintf("%s", name);
        }
        else
        {
            if (context != NULL)
            {
                parent_name = uves_sprintf("%s.%s.%s", caller_id, context, name);
            }
            else
            {
                parent_name = uves_sprintf("%s.%s", caller_id, name);
            }
        }
        
        /* Const cast, we don't change the parameter list, parent is declared const */
        check( parent = cpl_parameterlist_find_const(parameters, parent_name),
           "Could not get parameter '%s' from provided parameter list", parent_name);
        /* PIPPO
        assure( parent != NULL, CPL_ERROR_DATA_NOT_FOUND,
            "Missing parameter '%s' needed to define '%s' in context '%s'",
            parent_name, name, context);
        
        assure( cpl_parameter_get_type(parent) == type, CPL_ERROR_TYPE_MISMATCH,
            "Parameter '%s' type is %s. Type %s needed for recipe parameter '%s'",
            parent_name, 
            uves_tostring_cpl_type(cpl_parameter_get_type(parent)),
            uves_tostring_cpl_type(type),
            name);
        */  
        switch (type)
        {
            int value_int;  
            bool value_bool;
            double value_double;
            const char *value_string;

        case CPL_TYPE_BOOL:
            check( value_bool = cpl_parameter_get_bool(parent), 
               "Error reading parameter '%s'", parent_name);
        
            check( cpl_parameter_set_bool(p, value_bool),
               "Error setting parameter '%s'", name);
            
            uves_msg_debug("Setting parameter '%s' <- '%s' = %s",
                   name, parent_name, (value_bool) ? "true" : "false");
            break;

        case CPL_TYPE_INT:
            check( value_int = cpl_parameter_get_int(parent),
               "Error reading parameter '%s'", parent_name);
        
            check( cpl_parameter_set_int(p, value_int),
               "Error setting parameter '%s'", name);
            
            uves_msg_debug("Setting parameter '%s' <- '%s' = %d", 
                   name, parent_name, value_int);
            break;
            
        case CPL_TYPE_DOUBLE:
            check( value_double = cpl_parameter_get_double(parent),
               "Error reading parameter '%s'", parent_name);
        
            check( cpl_parameter_set_double(p, value_double),
               "Error setting parameter '%s'", name);
            
            uves_msg_debug("Setting parameter '%s' <- '%s' = %e", 
                   name, parent_name, value_double);
            break;
            
        case CPL_TYPE_STRING:
            check( value_string = cpl_parameter_get_string(parent),
               "Error reading parameter '%s'", parent_name);
            
            check( cpl_parameter_set_string(p, value_string), 
               "Error setting parameter '%s'", name);
            
            uves_msg_debug("Setting parameter '%s' <- '%s' = '%s'", 
                   name, parent_name, value_string);
            break;
            
        default:
            assure(false, CPL_ERROR_UNSUPPORTED_MODE,
               "Parameter '%s' has type %s",
               name, uves_tostring_cpl_type(type));
        }  /* switch type */
        
        cpl_free(parent_name); parent_name = NULL;
        
    } /* Set each recipe parameter */

    /* Pass frame set without touching */
    recipe->frames = frames;

    /* 
     * Invoke recipe 
     *
     *  Remember message domain of caller,
     *   and number of warnings in caller.
     */

    {
    const char *domain = uves_msg_get_domain();
    int warnings_in_caller = uves_msg_get_warnings();

    sub_domain = uves_sprintf("%s.%s", domain, recipe_domain);
    uves_msg_set_domain(sub_domain);

    status = cpl_plugin_get_exec(plugin)(plugin);

    /* Reset state (domain+warnings) */

    uves_msg_set_domain(domain);

    /* Total number of warnings in caller is not
     * (previous warnings) + (subrecipe warnings)
     */
    uves_msg_add_warnings(warnings_in_caller);
    }
    
    /* On recipe failure: The recipe is responsible 
     *  for printing any error messages.
     *  A failing recipe is an unrecoverable error.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        /* Reset error stack but keep error code */
        cpl_error_code ec = cpl_error_get_code();
        uves_error_reset();
        assure( false, ec, "Recipe '%s' failed", recipe_id);
    }
    
    assure( status == 0, CPL_ERROR_ILLEGAL_OUTPUT,
        "Recipe '%s' failed with exit status %d", recipe_id, status);
    
    /* Call recipe_destroy */
    must_destroy_plugin = false;
    assure( cpl_plugin_get_deinit(plugin)(plugin) == 0, 
        CPL_ERROR_ILLEGAL_OUTPUT,
        "Error cleaning up recipe");
    
    uves_msg("Recipe '%s' succeeded", recipe_id);

  cleanup:
    uves_free_string_const(&recipe_id);
    cpl_free(parent_name); parent_name = NULL;
    cpl_free(sub_domain); sub_domain = NULL;
    if (must_destroy_plugin)
    {
        cpl_plugin_get_deinit(plugin)(plugin);
    }

    cpl_pluginlist_delete(list);
    
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Call a recipe
   @param    recipe_id      Name of recipe to execute
   @param    parameters     Parameter list
   @param    frames         Input frame set
   @param    caller_id      Name of caller recipe
   @param    context        An optional parent recipe context. (Ignored if NULL).

   @return   0 iff recipe execution succeeded


   The specified recipe is invoked with the provided frame set and the parameters
   from the provided list belonging to the specified context.
*/
/*----------------------------------------------------------------------------*/

int
uves_invoke_recipe(const char *recipe_id, const cpl_parameterlist *parameters, 
                   cpl_frameset *frames,
                   const char *caller_id, const char *context)
{
    assure(recipe_id != NULL, CPL_ERROR_NULL_INPUT, "Null recipe name");

    if      (strcmp(recipe_id, make_str(UVES_PHYSMOD_ID) ) == 0) return uves_exec_recipe(&uves_physmod_get_info, UVES_PHYSMOD_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_ORDERPOS_ID)) == 0) return uves_exec_recipe(&uves_orderpos_get_info, UVES_ORDERPOS_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_MBIAS_ID)   ) == 0) return uves_exec_recipe(&uves_mbias_get_info, UVES_MBIAS_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_MDARK_ID)   ) == 0) return uves_exec_recipe(&uves_mdark_get_info, UVES_MDARK_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_MFLAT_ID)   ) == 0) return uves_exec_recipe(&uves_mflat_get_info,  UVES_MFLAT_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_WAVECAL_ID) ) == 0) return uves_exec_recipe(&uves_wavecal_get_info,  UVES_WAVECAL_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_RESPONSE_ID)) == 0) return uves_exec_recipe(&uves_response_get_info, UVES_RESPONSE_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_SCIRED_ID)  ) == 0) return uves_exec_recipe(&uves_scired_get_info, UVES_SCIRED_DOM, parameters, frames, caller_id, context);
    else if (strcmp(recipe_id, make_str(UVES_REDCHAIN_ID)) == 0) return uves_exec_recipe(&uves_redchain_get_info, UVES_REDCHAIN_DOM, parameters, frames, caller_id, context);
    else
        {
            assure( false, CPL_ERROR_ILLEGAL_INPUT, "Unknown recipe: '%s'", recipe_id);
        }
  cleanup:
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Propagate a parameter list from a sub-recipe to a parent recipe
   @param    get_info       pointer to get_info function
   @param    parameters     Parent recipe parameter list
   @param    recipe_id      Name of recipe
   @param    context        An optional parent recipe context. (Ignored if NULL).

   @return   zero iff okay

   See the reduction chain recipe for examples of the use of this function.

   This function is called before recipe execution starts; therefore
   it does not use the uves_error module. Besides returning the success
   status, a cpl_error_code is set on error.

   Also see @c propagate().
*/
/*----------------------------------------------------------------------------*/
int
uves_prop_par(int (*get_info)(cpl_pluginlist *),
    cpl_parameterlist *parameters,
    const char *recipe_id, const char *context)
{
    cpl_plugin *plugin    = NULL;
    cpl_pluginlist *list  = NULL;
    cpl_recipe *subrecipe = NULL;
    char name[256];
    int status;

    /* Check input */
    if (get_info == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null function pointer");
    }
    /* context may be NULL */
    if (parameters == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null parameter list");
    }
    
    if (recipe_id == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null recipe id");
    }    

    /* Get the sub-recipe plugin */
    list = cpl_pluginlist_new();
    status = get_info(list);
    
    if (status != 0)
    {
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, "Could not get info about recipe");
    }

    /* Get first plugin in plugin list, it must be of type recipe */
    if ((plugin = cpl_pluginlist_get_first(list)) == NULL)
    {
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, "Error getting plugin");
    }
    if (cpl_plugin_get_name(plugin) == NULL) {
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, "Plugin name is NULL");
    }
    sprintf(name, "%s", cpl_plugin_get_name(plugin));
    
    if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE)
        {
            cpl_pluginlist_delete(list);
            FAIL(-1, CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");
        }
    subrecipe = (cpl_recipe *) plugin;
    
    /* Create parameter list by calling subrecipe initializer function */
    if( cpl_plugin_get_init(plugin)(plugin) != 0)
    {
        cpl_plugin_get_deinit(plugin)(plugin);
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, "Error getting '%s' parameter list",
             name);
    }

    if (subrecipe->parameters == NULL)
    {
        cpl_plugin_get_deinit(plugin)(plugin);
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Recipe '%s' returned NULL parameter list", 
             name);
    }
    
    if (propagate(cpl_plugin_get_name(plugin), subrecipe->parameters, parameters, recipe_id, context) != 0)
    {
        cpl_plugin_get_deinit(plugin)(plugin);
        cpl_pluginlist_delete(list);
        FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT, "Error propagating parameters from recipe '%s'", 
             name);
    }
    
    cpl_plugin_get_deinit(plugin)(plugin);
    cpl_pluginlist_delete(list);
   
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
   @see uves_prop_par()

   @param subrecipe       name of recipe
*/
/*----------------------------------------------------------------------------*/
int
uves_propagate_parameters(const char *subrecipe,
                          cpl_parameterlist *parameters,
                          const char *recipe_id, const char *context)
{
    if (subrecipe == NULL) {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null subrecipe id");
    }
    
    if      (strcmp(subrecipe, make_str(UVES_PHYSMOD_ID) ) == 0) return uves_prop_par(&uves_physmod_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_ORDERPOS_ID)) == 0) return uves_prop_par(&uves_orderpos_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_MBIAS_ID)   ) == 0) return uves_prop_par(&uves_mbias_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_MDARK_ID)   ) == 0) return uves_prop_par(&uves_mdark_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_MFLAT_ID)   ) == 0) return uves_prop_par(&uves_mflat_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_WAVECAL_ID) ) == 0) return uves_prop_par(&uves_wavecal_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_RESPONSE_ID)) == 0) return uves_prop_par(&uves_response_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_SCIRED_ID)  ) == 0) return uves_prop_par(&uves_scired_get_info, parameters, recipe_id, context);
    else if (strcmp(subrecipe, make_str(UVES_REDCHAIN_ID)) == 0) return uves_prop_par(&uves_redchain_get_info, parameters, recipe_id, context);
    else {
        FAIL(-1, CPL_ERROR_DATA_NOT_FOUND, "Unknown recipe: '%s'", subrecipe);
    }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Propagate a parameter list from a sub-step to a parent recipe
   @param    step_id        Name of sub-step
   @param    parameters     Parent recipe parameter list
   @param    recipe_id      Name of calling recipe
   @param    context        An optional parent recipe context. (Ignored if NULL).

   @return   zero iff okay

   This function should be called from within the recipe creator function.
   For example, to propagate the parameters from a substep named "uves_extract"
   to a recipe named "uves_cal_wavecal", call
   @code
       uves_propagate_parameters_step("uves_extract", parameters, "uves_cal_wavecal", NULL);
   @endcode

   When invoking the substep the parameter list as well as the context must 
   be passed along
   @code
        spectrum = uves_extract( ..., parameters, "uves_cal_wavecal", ...);
   @endcode

   If a recipe calls the same substep several times (with possibly different
   parameters values), it is useful to define a context within the calling
   recipe. As shown in this example:

   @code
       uves_propagate_parameters_step("uves_extract", parameters, "uves_cal_wavecal", "context1");
       uves_propagate_parameters_step("uves_extract", parameters, "uves_cal_wavecal", "context2");
       :
       :
       spectrum1 = uves_extract( ..., parameters, "uves_cal_wavecal.context1", ...);
       :
       spectrum2 = uves_extract( ..., parameters, "uves_cal_wavecal.context2", ...);
   @endcode

   To overrule a default parameter value of a substep, use @c uves_set_parameter_default().

   This function is called before recipe execution starts; 
   therefore it does not use the uves_error module. Besides returning the success
   status, a cpl_error_code is set on error.

   Also see @c propagate().
*/
/*----------------------------------------------------------------------------*/

int
uves_propagate_parameters_step(const char *step_id,
                   cpl_parameterlist *parameters,
                   const char *recipe_id, const char *context)
{
    cpl_parameterlist *subparameters = NULL;
    cpl_parameterlist *(*get_parameters)(void) = NULL; /* Pointer to function 
                              returning parameter list */
    
    /* Check input */
    if (step_id == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null parameter list");
    }
    
    if (parameters == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null parameter list");
    }
    
    if (recipe_id == NULL)
    {
        FAIL(-1, CPL_ERROR_NULL_INPUT, "Null recipe id");
    }    
    /* context may be NULL */
    
    /* Define which function to call */
           if (strcmp(step_id, UVES_BACKSUB_ID ) == 0) {
      get_parameters = uves_backsub_define_parameters;
    } else if (strcmp(step_id, UVES_QCDARK_ID ) == 0) {
      get_parameters = uves_qcdark_define_parameters;
    } else if (strcmp(step_id, UVES_EXTRACT_ID ) == 0) {
      get_parameters = uves_extract_define_parameters;
    } else if (strcmp(step_id, UVES_REBIN_ID   ) == 0) {
      get_parameters = uves_rebin_define_parameters;
    } else if (strcmp(step_id, UVES_REDUCE_ID  ) == 0) {
      get_parameters = uves_reduce_define_parameters;
    } else {
      FAIL(-1, CPL_ERROR_DATA_NOT_FOUND, "Unknown sub-step: '%s'", step_id);
    }

    /* Get sub-step parameters */
    if( (subparameters = get_parameters()) == NULL )
    {
        FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, "Error getting '%s' parameter list", step_id);
    }
    
    if ( propagate(step_id, subparameters, parameters, recipe_id, context) != 0)
    {
        cpl_parameterlist_delete(subparameters);
        FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT, "Error propagating '%s' parameters", step_id);
    }
    
    cpl_parameterlist_delete(subparameters);
    return 0;
}


/*
 * Create an enumeration parameter.
 * One function for each of  int, double, string
 *    size of values array must match 'size', returns NULL iff error. 
 *
 * It would be much nicer to use a cpl_parameter_duplicate(), but that
 * doesn't exist
 *
 * (These three functions could be reduced to one function by use
 * of void pointers, but that is also ugly)
 */
static cpl_parameter *
create_parameter_enum_int(const char *name, cpl_type type,
              const char *description, 
              const char *context, 
              int default_value, int size,
              int *values)
{
    /*  This is just ugly */

    cpl_parameter *result = NULL;
    
    if (! (1 <= size && size <= 10))
    {
        cpl_msg_error(__func__, "Unsupported enumeration size: %d (max is 10)", size);
        return NULL;
    }
    
    switch(size)
    {
    case 1:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0]);
        break;
    case 2:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1]);
        break;
    case 3:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1],
                          values[2]);
        break;
    case 4:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3]);
        break;
    case 5:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4]);
        break;
    case 6:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5]);
        break;
    case 7:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6]);
        break;
    case 8:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7]);
        break;
    case 9:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7], 
                          values[8]);
        break;
    case 10:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0], 
                       values[1], 
                       values[2], 
                       values[3], 
                       values[4], 
                       values[5], 
                       values[6], 
                       values[7], 
                       values[8], 
                       values[9]);
        break;
    } /* Switch size */
    return result;
}
static cpl_parameter *
create_parameter_enum_double(const char *name, cpl_type type,
                 const char *description, 
                 const char *context, double default_value,
                 int size, double *values)
{
    /*  This is very ugly */
    
    cpl_parameter *result = NULL; 

    if (! (1 <= size && size <= 10))
    {
        cpl_msg_error(__func__, "Unsupported enumeration size: %d (max is 10)", size);
        return NULL;
    }
    
    switch(size)
    {
    case 1:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0]);
        break;
    case 2:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1]);
        break;
    case 3:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1],
                          values[2]);
        break;
    case 4:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3]);
        break;
    case 5:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4]);
        break;
    case 6:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5]);
        break;
    case 7:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6]);
        break;
    case 8:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7]);
        break;
    case 9:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7], 
                          values[8]);
        break;
    case 10:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0], 
                       values[1], 
                       values[2], 
                       values[3], 
                       values[4], 
                       values[5], 
                       values[6], 
                       values[7], 
                       values[8], 
                       values[9]);
        break;
    } /* Switch size */
    return result;
}
static cpl_parameter *
create_parameter_enum_string(const char *name, cpl_type type,
                 const char *description, 
                 const char *context, 
                 const char *default_value, 
                 int size, const char **values)
{
    /*  This is extremely ugly */
    
    cpl_parameter *result = NULL;

    if (! (1 <= size && size <= 10))
    {
        cpl_msg_error(__func__, "Unsupported enumeration size: %d (max is 10)", size);
        return NULL;
    }
    
    switch(size)
    {
    case 1:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0]);
        break;
    case 2:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1]);
        break;
    case 3:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0],
                          values[1],
                          values[2]);
        break;
    case 4:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3]);
        break;
    case 5:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4]);
        break;
    case 6:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5]);
        break;
    case 7:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6]);
        break;
    case 8:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7]);
        break;
    case 9:
        uves_parameter_new_enum(result, name,
                          type,
                          description,
                          context,
                          default_value, size,
                          values[0], 
                          values[1], 
                          values[2], 
                          values[3], 
                          values[4], 
                          values[5], 
                          values[6], 
                          values[7], 
                          values[8]);
        break;
    case 10:
        uves_parameter_new_enum(result, name,
                       type,
                       description,
                       context,
                       default_value, size,
                       values[0], 
                       values[1], 
                       values[2], 
                       values[3], 
                       values[4], 
                       values[5], 
                       values[6], 
                       values[7], 
                       values[8], 
                       values[9]);
        break;
    } /* Switch size */
    return result;
}


/*----------------------------------------------------------------------------*/
/**
   @brief    Propagate a parameter list from a sub-recipe to a parent recipe
   @return   zero iff okay


   This function propagates the parameters of the specified sub-recipe
   to the provided parameter list.

   Parameters belonging to the special context 'uves' (i.e. global parameters) 
   are not propagated.

   Each parameter must be of the form

   name = S.x

   where @em S is the name of the subrecipe. Furthermore,
   S must be a prefix of the context, and context must be a prefix of the full
   name

   This parameter is propagated to

   name = R.c.S.x
   context = R.c
   alias = c.S.A

   Where @em R is the (parent) recipe id, @em A is the alias and @em c is the optional
   context. If the context is not specified, the parameter is propagated as

   name = R.S.x
   context = R
   alias = S.A

   (i.e. ".c" is omitted).  The alias is only propagated if a previous alias exists,
   otherwise no alias is set.
*/
/*----------------------------------------------------------------------------*/

static int
propagate(const char *substep_id, const cpl_parameterlist *sub_parameters, 
      cpl_parameterlist *parent_parameters,
      const char *parent_id, const char *context)
{
    const cpl_parameter *p = NULL;
    
    /* For each sub-recipe parameter:
       prefix with context and insert in parent parameter list

       Set (overwrite) default value as current value
    */
    for (p = cpl_parameterlist_get_first_const(sub_parameters);
         p != NULL;
         p = cpl_parameterlist_get_next_const(sub_parameters) )
    {
        const char *name          = cpl_parameter_get_name(p);   
        const char *description   = cpl_parameter_get_help(p);
        const char *subcontext    = cpl_parameter_get_context(p);
        const char *alias         = cpl_parameter_get_alias(p, 
                                                            CPL_PARAMETER_MODE_CLI);
        cpl_parameter_class class = cpl_parameter_get_class(p);
        cpl_type type             = cpl_parameter_get_type(p);

        /* Check that S <= name
         * and S <= c and c <= name,
         * where S is either subrecipe id or 'uves', c is the context,
         * and "<=" means "is substring of"
         */

        {
        const char *S;

        if (strstr(name, "uves.") == name)
            {
            S = "uves";
            }
        else
            {
            S = substep_id;

            /* Check S <= name */
            if (strstr(name, S) != name)
                {
                FAIL(-1, CPL_ERROR_ILLEGAL_INPUT,
                     "Recipe id '%s' is not prefix of parameter name '%s'",
                     S, name);
                }
            }
        
        /* Check S <= c */
        if (strstr(subcontext, S) != subcontext)
            {
            FAIL(-1, CPL_ERROR_ILLEGAL_INPUT, 
                 "Recipe id '%s' is not prefix of parameter context '%s'", 
                 S, subcontext);
            }
        
        /* Check c <= name */
        if (strstr(name, subcontext) != name)
            {
            FAIL(-1, CPL_ERROR_ILLEGAL_INPUT,
                 "Parameter context '%s' is not prefix of parameter name '%s'", 
                 subcontext, name);
            }
        }/* End check parameter format */
        
        if (strcmp(subcontext, "uves") != 0)
        {
            int enum_size;
            
            cpl_parameter *new_par  = NULL;
            char *new_name;
            char *new_context;
            char *new_alias;
            
            if (context == NULL)
            {
                new_name     = uves_sprintf("%s.%s", parent_id, name);     /* R.S.x */
                new_context  = uves_sprintf("%s", parent_id);              /* R     */
                if (alias != NULL)
                {
                    new_alias = uves_sprintf("%s.%s", substep_id, alias); /* S.A */
                }
                else
                {
                    new_alias = NULL;
                }
            }
            else
            {
                new_name     = uves_sprintf("%s.%s.%s", parent_id, context, name);  
                /* R.c.Sx */
                new_context  = uves_sprintf("%s.%s", parent_id, context); 
                /* R.c    */
                if (alias != NULL)
                {
                    new_alias = uves_sprintf("%s.%s.%s", 
                                 context, substep_id, alias); 
                    /* c.S.A */
                }
                else
                {
                    new_alias = NULL;
                }
            }
            
            if (new_name == NULL || new_context == NULL)
            {
                if (new_name    != NULL) cpl_free(new_name);
                if (new_context != NULL) cpl_free(new_context);
                if (new_alias   != NULL) cpl_free(new_alias);
                FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT, "Memory allocation failed");
            }
            
            
            /* Check for legal class/type before switch */
            if (class != CPL_PARAMETER_CLASS_VALUE &&
            class != CPL_PARAMETER_CLASS_RANGE &&
            class != CPL_PARAMETER_CLASS_ENUM)
            {
                cpl_free(new_name);
                cpl_free(new_context);
                if (new_alias != NULL) cpl_free(new_alias);
                FAIL(-1, CPL_ERROR_TYPE_MISMATCH,
                 "Unrecognized class of parameter '%s'", name);
            }
            
            if (type != CPL_TYPE_BOOL   &&
            type != CPL_TYPE_INT    &&
            type != CPL_TYPE_DOUBLE &&
            type != CPL_TYPE_STRING)
            {
                cpl_free(new_name);
                cpl_free(new_context);
                if (new_alias != NULL) cpl_free(new_alias);
                FAIL(-1, CPL_ERROR_UNSUPPORTED_MODE, "Unsupported type: %s",
                 uves_tostring_cpl_type(type));
            }
            
            /* Create a new parameter from the sub-parameter */
            switch (class)
            {
            case CPL_PARAMETER_CLASS_VALUE:
                switch (type)
                {
                case CPL_TYPE_BOOL:
                    uves_parameter_new_value(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_bool(p));
                    break;
                    
                case CPL_TYPE_INT:
                    uves_parameter_new_value(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_int(p));
                    break;
                    
                case CPL_TYPE_DOUBLE:
                    uves_parameter_new_value(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_double(p));
                    break;
                case CPL_TYPE_STRING:
                    uves_parameter_new_value(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_string(p));
                    break;
                default:
                    break; 
                }       /* switch type */
                
                break;   /* CLASS_VALUE */
                
            case CPL_PARAMETER_CLASS_RANGE:
                /* Range is either int or double */
                switch (type)
                {
                    int min_int, max_int;
                    double min_double, max_double;
                    
                case CPL_TYPE_INT:
                    min_int = cpl_parameter_get_range_min_int(p);
                    max_int = cpl_parameter_get_range_max_int(p);
                    
                    uves_parameter_new_range(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_int(p),
                                 min_int, max_int);
                    break;
                    
                case CPL_TYPE_DOUBLE:
                    min_double = cpl_parameter_get_range_min_double(p);
                    max_double = cpl_parameter_get_range_max_double(p);
                    
                    uves_parameter_new_range(new_par, new_name,
                                 type,
                                 description,
                                 new_context,
                                 cpl_parameter_get_default_double(p),
                                 min_double, max_double);
                    break;
                default:
                    break;
                }
        
                break;   /* CLASS_RANGE */
        
            case CPL_PARAMETER_CLASS_ENUM:
                enum_size = cpl_parameter_get_enum_size(p);
            
                /* Enum type is either int, double or string */
                switch (type)
                {
                    int        *values_int;        /* Arrays to hold enum values */
                    double     *values_double;
                    const char **values_string;
                    int i;
                    
                case CPL_TYPE_INT:
                    if ( (values_int = cpl_malloc(sizeof(int) * enum_size))
                     == NULL)
                    {
                        cpl_free(new_name);
                        cpl_free(new_context);
                        if (new_alias != NULL) cpl_free(new_alias);
                        FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT, 
                         "Memory allocation failed");
                    }
                    for (i = 0; i < enum_size; i++)
                    {
                        values_int[i] = cpl_parameter_get_enum_int(p, i);
                    }
                
                    new_par = create_parameter_enum_int(
                    new_name,
                    type,
                    description,
                    new_context,
                    cpl_parameter_get_default_int(p),
                    enum_size,
                    values_int);
                    cpl_free(values_int);
                    break;  /* Enum type int */
                    
                case CPL_TYPE_DOUBLE:
                    if ( (values_double = 
                      cpl_malloc(sizeof(double) * enum_size)) == NULL)
                    {
                        cpl_free(new_name);
                        cpl_free(new_context);
                        if (new_alias != NULL) cpl_free(new_alias);
                        FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT,
                         "Memory allocation failed");
                    }
                    for (i = 0; i < enum_size; i++)
                    {
                        values_double[i] = cpl_parameter_get_enum_double(p, i);
                    }
                
                    new_par = create_parameter_enum_double(
                    new_name,
                    type,
                    description,
                    new_context,
                    cpl_parameter_get_default_double(p),
                    enum_size,
                    values_double);
                    cpl_free(values_double);

                    break;  /* Enum type double */
            
                case CPL_TYPE_STRING:
                    if ( (values_string = 
                      cpl_malloc(sizeof(char *) * enum_size)) == NULL)
                    {
                        cpl_free(new_name);
                        cpl_free(new_context);
                        if (new_alias != NULL) cpl_free(new_alias);
                        FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT, 
                         "Memory allocation failed");
                    }
                    for (i = 0; i < enum_size; i++)
                    {
                        values_string[i] = cpl_parameter_get_enum_string(p, i);
                    }
                
                    new_par = create_parameter_enum_string(
                    new_name,
                    type,
                    description,
                    new_context,
                    cpl_parameter_get_default_string(p),
                    enum_size,
                    values_string);
                    cpl_free(values_string);
                    
                    break;  /* Enum type string */
                
                default:
                    break;
                
                } /* Switch enum type */
            
                break;   /* CLASS_ENUM */

            default:
                break;
            
            } /* Switch class */

            if (new_par == NULL)
            {
                cpl_free(new_name);
                cpl_free(new_context);
                if (new_alias != NULL) cpl_free(new_alias);
                FAIL(-1, CPL_ERROR_ILLEGAL_OUTPUT,
                 "Propagation of parameter '%s' failed", 
                 name);
            }
        
            /* Also propagate alias */
            if (alias != NULL)
            {
                cpl_parameter_set_alias(new_par, CPL_PARAMETER_MODE_CLI, new_alias);
            }

            /* Insert parameter in parent parameter list */
            cpl_parameterlist_append(parent_parameters, new_par);
            
            cpl_free(new_name);
            cpl_free(new_context);
            if (new_alias != NULL) cpl_free(new_alias);
            
        }  /* If parameter context was not 'uves' */
        
    } /* For each sub-recipe parameter */
    
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}


/**@}*/
