/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set sw=2 sts=2 et cin: */
/* 
 * This file is part of the MUSE Instrument Pipeline
 * Copyright (C) 2005-2015 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/* This file was automatically generated */

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

/*----------------------------------------------------------------------------*
 *                              Includes                                      *
 *----------------------------------------------------------------------------*/
#include <string.h> /* strcmp(), strstr() */
#include <strings.h> /* strcasecmp() */
#include <cpl.h>

#include "muse_scipost_z.h" /* in turn includes muse.h */

/*----------------------------------------------------------------------------*/
/**
  @defgroup recipe_muse_scipost         Recipe muse_scipost: Prepare reduced and combined science products.
  @author Peter Weilbacher
  
        Sort input pixel tables into lists of files per exposure, merge pixel
        tables from all IFUs of each exposure.

        Correct each exposure for differential atmospheric refraction (unless
        --lambdaref is far outside the MUSE wavelength range).
        Then the flux calibration is carried out, if a response curve was given
        in the input; it includes a correction of telluric absorption, if a
        telluric absorption correction file was given.
        Then the sky subtraction is carried out (unless --skymethod="none"),
        either directly subtracting an input sky continuum and an input sky
        emission lines (for --skymethod="subtract-model"), or
        (--skymethod="model") create a sky spectrum from the darkest fraction
        (--skymodel_fraction, after ignoring the lowest --skymodel_ignore as
        artifacts) of the field of view, then fitting and subtracting
        sky emission lines using an initial estimate of the input sky lines;
        then the continuum (residuals after subtracting the sky lines from the
        sky spectrum) is subtracted as well. If --save contains "skymodel", all
        sky-related products are saved for each exposure.
        Afterwards the data is corrected for the radial velocity of the
        observer (--rvcorr), before the input (or a default) astrometric
        solution is applied.
        Now each individual exposure is fully reduced; the pixel tables at this
        stage can be saved by setting "individual" in --save.

        If multiple exposures were given, they are then combined. If --save
        contains "combined", this final merged pixel table is saved.

        Finally (if --save contains "cube"), the data is resampled into a
        datacube, using all parameters given to the recipe. The extent and
        orientation of the cube is normally computed from the data itself, but
        this can be overridden by passing a file with the output world
        coordinate system (OUTPUT_WCS), for example a MUSE cube. This can also
        be used to sample the wavelength axis logarithmically (in that file set
        "CTYPE3='AWAV-LOG'").
        As a last step, the computed cube is integrated over all filter
        functions given (--filter) that are also present in the input filter
        list table.
      
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*----------------------------------------------------------------------------*
 *                         Static variables                                   *
 *----------------------------------------------------------------------------*/
static const char *muse_scipost_help =
  "Sort input pixel tables into lists of files per exposure, merge pixel tables from all IFUs of each exposure. Correct each exposure for differential atmospheric refraction (unless --lambdaref is far outside the MUSE wavelength range). Then the flux calibration is carried out, if a response curve was given in the input; it includes a correction of telluric absorption, if a telluric absorption correction file was given. Then the sky subtraction is carried out (unless --skymethod=&none&), either directly subtracting an input sky continuum and an input sky emission lines (for --skymethod=&subtract-model&), or (--skymethod=&model&) create a sky spectrum from the darkest fraction (--skymodel_fraction, after ignoring the lowest --skymodel_ignore as artifacts) of the field of view, then fitting and subtracting sky emission lines using an initial estimate of the input sky lines; then the continuum (residuals after subtracting the sky lines from the sky spectrum) is subtracted as well. If --save contains &skymodel&, all sky-related products are saved for each exposure. Afterwards the data is corrected for the radial velocity of the observer (--rvcorr), before the input (or a default) astrometric solution is applied. Now each individual exposure is fully reduced; the pixel tables at this stage can be saved by setting &individual& in --save. If multiple exposures were given, they are then combined. If --save contains &combined&, this final merged pixel table is saved. Finally (if --save contains &cube&), the data is resampled into a datacube, using all parameters given to the recipe. The extent and orientation of the cube is normally computed from the data itself, but this can be overridden by passing a file with the output world coordinate system (OUTPUT_WCS), for example a MUSE cube. This can also be used to sample the wavelength axis logarithmically (in that file set &CTYPE3='AWAV-LOG'&). As a last step, the computed cube is integrated over all filter functions given (--filter) that are also present in the input filter list table.";

static const char *muse_scipost_help_esorex =
  "\n\nInput frames for raw frame tag \"PIXTABLE_OBJECT\":\n"
  "\n Frame tag            Type Req #Fr Description"
  "\n -------------------- ---- --- --- ------------"
  "\n PIXTABLE_OBJECT      raw   Y      Pixel table of a science object"
  "\n EXTINCT_TABLE        calib Y    1 Atmospheric extinction table"
  "\n STD_RESPONSE         calib Y    1 Response curve as derived from standard star(s)"
  "\n STD_TELLURIC         calib .    1 Telluric absorption correction as derived from standard star(s)"
  "\n ASTROMETRY_WCS       calib .    1 Astrometric solution derived from astrometric science frame"
  "\n OFFSET_LIST          calib .    1 List of coordinate offsets (and optional flux scale factors)"
  "\n FILTER_LIST          calib .    1 File to be used to create field-of-view images."
  "\n OUTPUT_WCS           calib .    1 WCS to override output cube location / dimensions"
  "\n SKY_LINES            calib .    1 List of OH transitions and other sky lines"
  "\n SKY_CONTINUUM        calib .    1 Sky continuum to use"
  "\n LSF_PROFILE          calib .      Slice specific LSF parameters."
  "\n SKY_MASK             calib .    1 Sky mask to use"
  "\n\nProduct frames for raw frame tag \"PIXTABLE_OBJECT\":\n"
  "\n Frame tag            Level    Description"
  "\n -------------------- -------- ------------"
  "\n DATACUBE_FINAL       final    Output datacube"
  "\n IMAGE_FOV            final    Field-of-view images corresponding to the \"filter\" parameter."
  "\n OBJECT_RESAMPLED     final    Stacked image (if --save contains \"stacked\")"
  "\n PIXTABLE_REDUCED     intermed Fully reduced pixel tables for each exposure (if --save contains \"individual\")"
  "\n PIXTABLE_POSITIONED  intermed Fully reduced and positioned pixel table for each individual exposure (if --save contains \"positioned\")"
  "\n PIXTABLE_COMBINED    intermed Fully reduced and combined pixel table for the full set of exposures (if --save contains \"combined\")"
  "\n SKY_MASK             intermed Created sky mask (if --skymethod=model and --save contains \"skymodel\")"
  "\n SKY_SPECTRUM         intermed Sky spectrum within the sky mask (if --skymethod=model and --save contains \"skymodel\")"
  "\n SKY_LINES            final    Estimated sky line flux table (if --skymethod=model and --save contains \"skymodel\")"
  "\n SKY_CONTINUUM        final    Estimated continuum flux spectrum (if --skymethod=model and --save contains \"skymodel\")";

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Create the recipe config for this plugin.

  @remark The recipe config is used to check for the tags as well as to create
          the documentation of this plugin.
 */
/*----------------------------------------------------------------------------*/
static cpl_recipeconfig *
muse_scipost_new_recipeconfig(void)
{
  cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
      
  cpl_recipeconfig_set_tag(recipeconfig, "PIXTABLE_OBJECT", 1, -1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "EXTINCT_TABLE", 1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "STD_RESPONSE", 1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "STD_TELLURIC", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "ASTROMETRY_WCS", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "OFFSET_LIST", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "FILTER_LIST", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "OUTPUT_WCS", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "SKY_LINES", 0, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "SKY_CONTINUUM", 0, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "LSF_PROFILE", 0, -1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "SKY_MASK", 0, 1);
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "SKY_MASK");
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "SKY_SPECTRUM");
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "SKY_LINES");
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "SKY_CONTINUUM");
    
  return recipeconfig;
} /* muse_scipost_new_recipeconfig() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return a new header that shall be filled on output.
  @param   aFrametag   tag of the output frame
  @param   aHeader     the prepared FITS header
  @return  CPL_ERROR_NONE on success another cpl_error_code on error

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code
muse_scipost_prepare_header(const char *aFrametag, cpl_propertylist *aHeader)
{
  cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
  cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST NDET",
                                     CPL_TYPE_INT,
                                     "Number of detected sources in output cube.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST LAMBDA",
                                     CPL_TYPE_FLOAT,
                                     "[Angstrom] Wavelength of plane in combined cube that was used for object detection.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST POS[0-9]+ X",
                                     CPL_TYPE_FLOAT,
                                     "[pix] Position of source k in x-direction in output cube. If the FWHM measurement fails, this value will be -1.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST POS[0-9]+ Y",
                                     CPL_TYPE_FLOAT,
                                     "[pix] Position of source k in y-direction in output cube. If the FWHM measurement fails, this value will be -1.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM[0-9]+ X",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] FWHM of source k in x-direction in output cube. If the FWHM measurement fails, this value will be -1.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM[0-9]+ Y",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] FWHM of source k in y-direction in output cube. If the FWHM measurement fails, this value will be -1.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM NVALID",
                                     CPL_TYPE_INT,
                                     "Number of detected sources with valid FWHM in output cube.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM MEDIAN",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] Median FWHM of all sources with valid FWHM measurement (in x- and y-direction) in output cube. If less than three sources with valid FWHM are detected, this value is zero.");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM MAD",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] Median absolute deviation of the FWHM of all sources with valid FWHM measurement (in x- and y-direction) in output cube. If less than three sources with valid FWHM are detected, this value is zero.");
  } else if (!strcmp(aFrametag, "IMAGE_FOV")) {
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST POS[0-9]+ X",
                                     CPL_TYPE_FLOAT,
                                     "[pix] Position of source k in x-direction in combined frame");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST POS[0-9]+ Y",
                                     CPL_TYPE_FLOAT,
                                     "[pix] Position of source k in y-direction in combined frame");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM[0-9]+ X",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] FWHM of source k in x-direction in combined frame");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST FWHM[0-9]+ Y",
                                     CPL_TYPE_FLOAT,
                                     "[arcsec] FWHM of source k in y-direction in combined frame");
  } else if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
  } else if (!strcmp(aFrametag, "PIXTABLE_REDUCED")) {
  } else if (!strcmp(aFrametag, "PIXTABLE_POSITIONED")) {
  } else if (!strcmp(aFrametag, "PIXTABLE_COMBINED")) {
  } else if (!strcmp(aFrametag, "SKY_MASK")) {
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST THRESHOLD",
                                     CPL_TYPE_DOUBLE,
                                     "Threshold in the white light considered as sky, used to create this mask");
  } else if (!strcmp(aFrametag, "SKY_SPECTRUM")) {
  } else if (!strcmp(aFrametag, "SKY_LINES")) {
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST LINE[0-9]+ NAME",
                                     CPL_TYPE_STRING,
                                     "Name of the strongest line in group l");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST LINE[0-9]+ AWAV",
                                     CPL_TYPE_DOUBLE,
                                     "[Angstrom] Wavelength (air) of the strongest line of group l");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST LINE[0-9]+ FLUX",
                                     CPL_TYPE_DOUBLE,
                                     "[erg/(s cm2 arcsec2)] Flux of the strongest line of group l");
  } else if (!strcmp(aFrametag, "SKY_CONTINUUM")) {
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST CONT FLUX",
                                     CPL_TYPE_DOUBLE,
                                     "[erg/(s cm2 arcsec2)] Total flux of the continuum");
    muse_processing_prepare_property(aHeader, "ESO QC SCIPOST CONT MAXDEV",
                                     CPL_TYPE_DOUBLE,
                                     "[erg/(s cm2 arcsec2 Angstrom)] Maximum (absolute value) of the derivative of the continuum spectrum");
  } else {
    cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
    return CPL_ERROR_ILLEGAL_INPUT;
  }
  return CPL_ERROR_NONE;
} /* muse_scipost_prepare_header() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return the level of an output frame.
  @param   aFrametag   tag of the output frame
  @return  the cpl_frame_level or CPL_FRAME_LEVEL_NONE on error

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static cpl_frame_level
muse_scipost_get_frame_level(const char *aFrametag)
{
  if (!aFrametag) {
    return CPL_FRAME_LEVEL_NONE;
  }
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "IMAGE_FOV")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "PIXTABLE_REDUCED")) {
    return CPL_FRAME_LEVEL_INTERMEDIATE;
  }
  if (!strcmp(aFrametag, "PIXTABLE_POSITIONED")) {
    return CPL_FRAME_LEVEL_INTERMEDIATE;
  }
  if (!strcmp(aFrametag, "PIXTABLE_COMBINED")) {
    return CPL_FRAME_LEVEL_INTERMEDIATE;
  }
  if (!strcmp(aFrametag, "SKY_MASK")) {
    return CPL_FRAME_LEVEL_INTERMEDIATE;
  }
  if (!strcmp(aFrametag, "SKY_SPECTRUM")) {
    return CPL_FRAME_LEVEL_INTERMEDIATE;
  }
  if (!strcmp(aFrametag, "SKY_LINES")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "SKY_CONTINUUM")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  return CPL_FRAME_LEVEL_NONE;
} /* muse_scipost_get_frame_level() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return the mode of an output frame.
  @param   aFrametag   tag of the output frame
  @return  the muse_frame_mode

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static muse_frame_mode
muse_scipost_get_frame_mode(const char *aFrametag)
{
  if (!aFrametag) {
    return MUSE_FRAME_MODE_ALL;
  }
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "IMAGE_FOV")) {
    return MUSE_FRAME_MODE_SEQUENCE;
  }
  if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "PIXTABLE_REDUCED")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  if (!strcmp(aFrametag, "PIXTABLE_POSITIONED")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  if (!strcmp(aFrametag, "PIXTABLE_COMBINED")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "SKY_MASK")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  if (!strcmp(aFrametag, "SKY_SPECTRUM")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  if (!strcmp(aFrametag, "SKY_LINES")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  if (!strcmp(aFrametag, "SKY_CONTINUUM")) {
    return MUSE_FRAME_MODE_DATEOBS;
  }
  return MUSE_FRAME_MODE_ALL;
} /* muse_scipost_get_frame_mode() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Setup the recipe options.
  @param   aPlugin   the plugin
  @return  0 if everything is ok, -1 if not called as part of a recipe.

  Define the command-line, configuration, and environment parameters for the
  recipe.
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_create(cpl_plugin *aPlugin)
{
  /* Check that the plugin is part of a valid recipe */
  cpl_recipe *recipe;
  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
    recipe = (cpl_recipe *)aPlugin;
  } else {
    return -1;
  }

  /* register the extended processing information (new FITS header creation, *
   * getting of the frame level for a certain tag)                           */
  muse_processinginfo_register(recipe,
                               muse_scipost_new_recipeconfig(),
                               muse_scipost_prepare_header,
                               muse_scipost_get_frame_level,
                               muse_scipost_get_frame_mode);

  /* XXX initialize timing in messages                                       *
   *     since at least esorex is too stupid to turn it on, we have to do it */
  if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
    cpl_msg_set_time_on();
  }

  /* Create the parameter list in the cpl_recipe object */
  recipe->parameters = cpl_parameterlist_new();
  /* Fill the parameters list */
  cpl_parameter *p;
      
  /* --save: Select output product(s) to save. Can contain one or more of "cube", "skymodel", "individual", "positioned", "combined", and "stacked". If several options are given, they have to be comma-separated.
          ("cube": output cube and associated images, if this is not given, no final resampling is done at all --
          "skymodel": up to four additional output products about the effectively used sky that was subtracted with the "model" method --
          "individual": fully reduced pixel table for each individual exposure --
          "positioned": fully reduced and positioned pixel table for each individual exposure, the difference to "individual" is that here, the output pixel tables have coordinates in RA and DEC; this is only useful, if both the relative exposure weighting and the final resampling are to be done externally --
          "combined": fully reduced and combined pixel table for the full set of exposures, the difference to "positioned" is that all pixel tables are combined into one, with an added weight column; this is useful, if only the final resampling step is to be done separately --
          "stacked": an additional output file in form of a 2D column-stacked image, i.e. x direction is pseudo-spatial, y direction is wavelength.)
         */
  p = cpl_parameter_new_value("muse.muse_scipost.save",
                              CPL_TYPE_STRING,
                             "Select output product(s) to save. Can contain one or more of \"cube\", \"skymodel\", \"individual\", \"positioned\", \"combined\", and \"stacked\". If several options are given, they have to be comma-separated. (\"cube\": output cube and associated images, if this is not given, no final resampling is done at all -- \"skymodel\": up to four additional output products about the effectively used sky that was subtracted with the \"model\" method -- \"individual\": fully reduced pixel table for each individual exposure -- \"positioned\": fully reduced and positioned pixel table for each individual exposure, the difference to \"individual\" is that here, the output pixel tables have coordinates in RA and DEC; this is only useful, if both the relative exposure weighting and the final resampling are to be done externally -- \"combined\": fully reduced and combined pixel table for the full set of exposures, the difference to \"positioned\" is that all pixel tables are combined into one, with an added weight column; this is useful, if only the final resampling step is to be done separately -- \"stacked\": an additional output file in form of a 2D column-stacked image, i.e. x direction is pseudo-spatial, y direction is wavelength.)",
                              "muse.muse_scipost",
                              (const char *)"cube,skymodel");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "save");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "save");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --resample: The resampling technique to use for the final output cube. */
  p = cpl_parameter_new_enum("muse.muse_scipost.resample",
                             CPL_TYPE_STRING,
                             "The resampling technique to use for the final output cube.",
                             "muse.muse_scipost",
                             (const char *)"drizzle",
                             6,
                             (const char *)"nearest",
                             (const char *)"linear",
                             (const char *)"quadratic",
                             (const char *)"renka",
                             (const char *)"drizzle",
                             (const char *)"lanczos");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "resample");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "resample");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dx: Horizontal step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units. */
  p = cpl_parameter_new_value("muse.muse_scipost.dx",
                              CPL_TYPE_DOUBLE,
                             "Horizontal step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units.",
                              "muse.muse_scipost",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dx");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dx");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dy: Vertical step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units. */
  p = cpl_parameter_new_value("muse.muse_scipost.dy",
                              CPL_TYPE_DOUBLE,
                             "Vertical step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units.",
                              "muse.muse_scipost",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dy");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dy");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dlambda: Wavelength step size (in Angstrom). Natural instrument sampling is used, if this is 0.0 */
  p = cpl_parameter_new_value("muse.muse_scipost.dlambda",
                              CPL_TYPE_DOUBLE,
                             "Wavelength step size (in Angstrom). Natural instrument sampling is used, if this is 0.0",
                              "muse.muse_scipost",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dlambda");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dlambda");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --crtype: Type of statistics used for detection of cosmic rays during final resampling. "iraf" uses the variance information, "mean" uses standard (mean/stdev) statistics, "median" uses median and the median median of the absolute median deviation. */
  p = cpl_parameter_new_enum("muse.muse_scipost.crtype",
                             CPL_TYPE_STRING,
                             "Type of statistics used for detection of cosmic rays during final resampling. \"iraf\" uses the variance information, \"mean\" uses standard (mean/stdev) statistics, \"median\" uses median and the median median of the absolute median deviation.",
                             "muse.muse_scipost",
                             (const char *)"median",
                             3,
                             (const char *)"iraf",
                             (const char *)"mean",
                             (const char *)"median");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "crtype");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "crtype");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --crsigma: Sigma rejection factor to use for cosmic ray rejection during final resampling. A zero or negative value switches cosmic ray rejection off. */
  p = cpl_parameter_new_value("muse.muse_scipost.crsigma",
                              CPL_TYPE_DOUBLE,
                             "Sigma rejection factor to use for cosmic ray rejection during final resampling. A zero or negative value switches cosmic ray rejection off.",
                              "muse.muse_scipost",
                              (double)15.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "crsigma");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "crsigma");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --rc: Critical radius for the "renka" resampling method. */
  p = cpl_parameter_new_value("muse.muse_scipost.rc",
                              CPL_TYPE_DOUBLE,
                             "Critical radius for the \"renka\" resampling method.",
                              "muse.muse_scipost",
                              (double)1.25);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "rc");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rc");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --pixfrac: Pixel down-scaling factor for the "drizzle" resampling method. */
  p = cpl_parameter_new_value("muse.muse_scipost.pixfrac",
                              CPL_TYPE_DOUBLE,
                             "Pixel down-scaling factor for the \"drizzle\" resampling method.",
                              "muse.muse_scipost",
                              (double)0.8);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "pixfrac");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pixfrac");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --ld: Number of adjacent pixels to take into account during resampling in all three directions (loop distance); this affects all resampling methods except "nearest". */
  p = cpl_parameter_new_value("muse.muse_scipost.ld",
                              CPL_TYPE_INT,
                             "Number of adjacent pixels to take into account during resampling in all three directions (loop distance); this affects all resampling methods except \"nearest\".",
                              "muse.muse_scipost",
                              (int)1);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ld");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ld");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --format: Type of output file format, "Cube" is a standard FITS cube with NAXIS=3 and multiple extensions (for data and variance). The extended "x" formats include the reconstructed image(s) in FITS image extensions within the same file. "sdpCube" does some extra calculations to create FITS keywords for the ESO Science Data Products. */
  p = cpl_parameter_new_enum("muse.muse_scipost.format",
                             CPL_TYPE_STRING,
                             "Type of output file format, \"Cube\" is a standard FITS cube with NAXIS=3 and multiple extensions (for data and variance). The extended \"x\" formats include the reconstructed image(s) in FITS image extensions within the same file. \"sdpCube\" does some extra calculations to create FITS keywords for the ESO Science Data Products.",
                             "muse.muse_scipost",
                             (const char *)"Cube",
                             5,
                             (const char *)"Cube",
                             (const char *)"Euro3D",
                             (const char *)"xCube",
                             (const char *)"xEuro3D",
                             (const char *)"sdpCube");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "format");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "format");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --weight: Type of weighting scheme to use when combining multiple exposures. "exptime" just uses the exposure time to weight the exposures, "fwhm" uses the best available seeing information from the headers as well, "none" preserves an existing weight column in the input pixel tables without changes. */
  p = cpl_parameter_new_enum("muse.muse_scipost.weight",
                             CPL_TYPE_STRING,
                             "Type of weighting scheme to use when combining multiple exposures. \"exptime\" just uses the exposure time to weight the exposures, \"fwhm\" uses the best available seeing information from the headers as well, \"none\" preserves an existing weight column in the input pixel tables without changes.",
                             "muse.muse_scipost",
                             (const char *)"exptime",
                             3,
                             (const char *)"exptime",
                             (const char *)"fwhm",
                             (const char *)"none");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "weight");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "weight");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --filter: The filter name(s) to be used for the output field-of-view image. Each name has to correspond to an EXTNAME in an extension of the FILTER_LIST file. If an unsupported filter name is given, creation of the respective image is omitted. If multiple filter names are given, they have to be comma separated. */
  p = cpl_parameter_new_value("muse.muse_scipost.filter",
                              CPL_TYPE_STRING,
                             "The filter name(s) to be used for the output field-of-view image. Each name has to correspond to an EXTNAME in an extension of the FILTER_LIST file. If an unsupported filter name is given, creation of the respective image is omitted. If multiple filter names are given, they have to be comma separated.",
                              "muse.muse_scipost",
                              (const char *)"white");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "filter");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filter");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --skymethod: The method used to subtract the sky background (spectrum).
          Option "model" should work in all kinds of science fields: it uses a global sky spectrum model with a local LSF.
          "model" uses fluxes indicated in the SKY_LINES file as starting estimates, but re-fits them on the global sky spectrum created from the science exposure.
          If SKY_CONTINUUM is given, it is directly subtracted, otherwise it is created from the sky region of the science exposure.
          Option "subtract-model" uses the input SKY_LINES and SKY_CONTINUUM, subtracting them directly without re-fitting the fluxes, but still makes use of the local LSF, hence LSF_PROFILE is required.
          The inputs LSF_PROFILE and SKY_LINES are necessary for these two model-based methods; SKY_CONTINUUM is required for "subtract-model" and optional for "model"; SKY_MASK is optional for "model".
          Finally, option "simple" creates a sky spectrum from the science data, and directly subtracts it, without taking the LSF into account (LSF_PROFILE and input SKY files are ignored). It works on data that was not flux calibrated.
         */
  p = cpl_parameter_new_enum("muse.muse_scipost.skymethod",
                             CPL_TYPE_STRING,
                             "The method used to subtract the sky background (spectrum). Option \"model\" should work in all kinds of science fields: it uses a global sky spectrum model with a local LSF. \"model\" uses fluxes indicated in the SKY_LINES file as starting estimates, but re-fits them on the global sky spectrum created from the science exposure. If SKY_CONTINUUM is given, it is directly subtracted, otherwise it is created from the sky region of the science exposure. Option \"subtract-model\" uses the input SKY_LINES and SKY_CONTINUUM, subtracting them directly without re-fitting the fluxes, but still makes use of the local LSF, hence LSF_PROFILE is required. The inputs LSF_PROFILE and SKY_LINES are necessary for these two model-based methods; SKY_CONTINUUM is required for \"subtract-model\" and optional for \"model\"; SKY_MASK is optional for \"model\". Finally, option \"simple\" creates a sky spectrum from the science data, and directly subtracts it, without taking the LSF into account (LSF_PROFILE and input SKY files are ignored). It works on data that was not flux calibrated.",
                             "muse.muse_scipost",
                             (const char *)"model",
                             4,
                             (const char *)"none",
                             (const char *)"subtract-model",
                             (const char *)"model",
                             (const char *)"simple");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "skymethod");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymethod");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --lambdamin: Cut off the data below this wavelength after loading the pixel table(s). */
  p = cpl_parameter_new_value("muse.muse_scipost.lambdamin",
                              CPL_TYPE_DOUBLE,
                             "Cut off the data below this wavelength after loading the pixel table(s).",
                              "muse.muse_scipost",
                              (double)4000.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamin");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamin");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --lambdamax: Cut off the data above this wavelength after loading the pixel table(s). */
  p = cpl_parameter_new_value("muse.muse_scipost.lambdamax",
                              CPL_TYPE_DOUBLE,
                             "Cut off the data above this wavelength after loading the pixel table(s).",
                              "muse.muse_scipost",
                              (double)10000.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamax");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamax");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --lambdaref: Reference wavelength used for correction of differential atmospheric refraction. The R-band (peak wavelength ~7000 Angstrom) that is usually used for guiding, is close to the central wavelength of MUSE, so a value of 7000.0 Angstrom should be used if nothing else is known. A value less than zero switches DAR correction off. */
  p = cpl_parameter_new_value("muse.muse_scipost.lambdaref",
                              CPL_TYPE_DOUBLE,
                             "Reference wavelength used for correction of differential atmospheric refraction. The R-band (peak wavelength ~7000 Angstrom) that is usually used for guiding, is close to the central wavelength of MUSE, so a value of 7000.0 Angstrom should be used if nothing else is known. A value less than zero switches DAR correction off.",
                              "muse.muse_scipost",
                              (double)7000.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdaref");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdaref");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --darcheck: Carry out a check of the theoretical DAR correction using source centroiding. If "correct" it will also apply an empirical correction. */
  p = cpl_parameter_new_enum("muse.muse_scipost.darcheck",
                             CPL_TYPE_STRING,
                             "Carry out a check of the theoretical DAR correction using source centroiding. If \"correct\" it will also apply an empirical correction.",
                             "muse.muse_scipost",
                             (const char *)"none",
                             3,
                             (const char *)"none",
                             (const char *)"check",
                             (const char *)"correct");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "darcheck");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "darcheck");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --skymodel_fraction: Fraction of the image (without the ignored part) to be considered as sky. If an input sky mask is provided, the fraction is applied to the regions within the mask. If the whole sky mask should be used, set this parameter to 1. */
  p = cpl_parameter_new_value("muse.muse_scipost.skymodel_fraction",
                              CPL_TYPE_DOUBLE,
                             "Fraction of the image (without the ignored part) to be considered as sky. If an input sky mask is provided, the fraction is applied to the regions within the mask. If the whole sky mask should be used, set this parameter to 1.",
                              "muse.muse_scipost",
                              (double)0.10);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "skymodel_fraction");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymodel_fraction");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --skymodel_ignore: Fraction of the image to be ignored. If an input sky mask is provided, the fraction is applied to the regions within the mask. If the whole sky mask should be used, set this parameter to 0. */
  p = cpl_parameter_new_value("muse.muse_scipost.skymodel_ignore",
                              CPL_TYPE_DOUBLE,
                             "Fraction of the image to be ignored. If an input sky mask is provided, the fraction is applied to the regions within the mask. If the whole sky mask should be used, set this parameter to 0.",
                              "muse.muse_scipost",
                              (double)0.05);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "skymodel_ignore");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymodel_ignore");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --skymodel_sampling: Spectral sampling of the sky spectrum [Angstrom]. */
  p = cpl_parameter_new_value("muse.muse_scipost.skymodel_sampling",
                              CPL_TYPE_DOUBLE,
                             "Spectral sampling of the sky spectrum [Angstrom].",
                              "muse.muse_scipost",
                              (double)0.3125);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "skymodel_sampling");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymodel_sampling");
  if (!getenv("MUSE_EXPERT_USER")) {
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
  }

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --skymodel_csampling: Spectral sampling of the continuum spectrum [Angstrom]. */
  p = cpl_parameter_new_value("muse.muse_scipost.skymodel_csampling",
                              CPL_TYPE_DOUBLE,
                             "Spectral sampling of the continuum spectrum [Angstrom].",
                              "muse.muse_scipost",
                              (double)0.3125);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "skymodel_csampling");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymodel_csampling");
  if (!getenv("MUSE_EXPERT_USER")) {
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
  }

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --sky_crsigma: Sigma level clipping for cube-based and spectrum-based CR rejection when creating the sky spectrum. This has to be a string of two comma-separated floating-point numbers. The first value gives the sigma-level rejection for cube-based CR rejection (using "median"), the second value the sigma-level for spectrum-based CR cleaning. Both can be switched off, by passing zero or a negative value; by default, the spectrum-based rejection is switched off. */
  p = cpl_parameter_new_value("muse.muse_scipost.sky_crsigma",
                              CPL_TYPE_STRING,
                             "Sigma level clipping for cube-based and spectrum-based CR rejection when creating the sky spectrum. This has to be a string of two comma-separated floating-point numbers. The first value gives the sigma-level rejection for cube-based CR rejection (using \"median\"), the second value the sigma-level for spectrum-based CR cleaning. Both can be switched off, by passing zero or a negative value; by default, the spectrum-based rejection is switched off.",
                              "muse.muse_scipost",
                              (const char *)"15.,15.");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "sky_crsigma");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sky_crsigma");
  if (!getenv("MUSE_EXPERT_USER")) {
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
  }

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --rvcorr: Correct the radial velocity of the telescope with reference to either the barycenter of the Solar System (bary), the center of the Sun (helio), or to the center of the Earth (geo). */
  p = cpl_parameter_new_enum("muse.muse_scipost.rvcorr",
                             CPL_TYPE_STRING,
                             "Correct the radial velocity of the telescope with reference to either the barycenter of the Solar System (bary), the center of the Sun (helio), or to the center of the Earth (geo).",
                             "muse.muse_scipost",
                             (const char *)"bary",
                             4,
                             (const char *)"bary",
                             (const char *)"helio",
                             (const char *)"geo",
                             (const char *)"none");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "rvcorr");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rvcorr");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --astrometry: If false, skip any astrometric calibration, even if one was passed in the input set of files. This causes creation of an output cube with a linear WCS and may result in errors. If you want to use a sensible default, leave this true but do not pass an ASTROMETRY_WCS. */
  p = cpl_parameter_new_value("muse.muse_scipost.astrometry",
                              CPL_TYPE_BOOL,
                             "If false, skip any astrometric calibration, even if one was passed in the input set of files. This causes creation of an output cube with a linear WCS and may result in errors. If you want to use a sensible default, leave this true but do not pass an ASTROMETRY_WCS.",
                              "muse.muse_scipost",
                              (int)TRUE);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "astrometry");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "astrometry");

  cpl_parameterlist_append(recipe->parameters, p);
    
  return 0;
} /* muse_scipost_create() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Fill the recipe parameters into the parameter structure
  @param   aParams       the recipe-internal structure to fill
  @param   aParameters   the cpl_parameterlist with the parameters
  @return  0 if everything is ok, -1 if something went wrong.

  This is a convienience function that centrally fills all parameters into the
  according fields of the recipe internal structure.
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_params_fill(muse_scipost_params_t *aParams, cpl_parameterlist *aParameters)
{
  cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
  cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
  cpl_parameter *p;
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.save");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->save = cpl_parameter_get_string(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.resample");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->resample_s = cpl_parameter_get_string(p);
  aParams->resample =
    (!strcasecmp(aParams->resample_s, "nearest")) ? MUSE_SCIPOST_PARAM_RESAMPLE_NEAREST :
    (!strcasecmp(aParams->resample_s, "linear")) ? MUSE_SCIPOST_PARAM_RESAMPLE_LINEAR :
    (!strcasecmp(aParams->resample_s, "quadratic")) ? MUSE_SCIPOST_PARAM_RESAMPLE_QUADRATIC :
    (!strcasecmp(aParams->resample_s, "renka")) ? MUSE_SCIPOST_PARAM_RESAMPLE_RENKA :
    (!strcasecmp(aParams->resample_s, "drizzle")) ? MUSE_SCIPOST_PARAM_RESAMPLE_DRIZZLE :
    (!strcasecmp(aParams->resample_s, "lanczos")) ? MUSE_SCIPOST_PARAM_RESAMPLE_LANCZOS :
      MUSE_SCIPOST_PARAM_RESAMPLE_INVALID_VALUE;
  cpl_ensure_code(aParams->resample != MUSE_SCIPOST_PARAM_RESAMPLE_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.dx");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dx = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.dy");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dy = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.dlambda");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dlambda = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.crtype");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->crtype_s = cpl_parameter_get_string(p);
  aParams->crtype =
    (!strcasecmp(aParams->crtype_s, "iraf")) ? MUSE_SCIPOST_PARAM_CRTYPE_IRAF :
    (!strcasecmp(aParams->crtype_s, "mean")) ? MUSE_SCIPOST_PARAM_CRTYPE_MEAN :
    (!strcasecmp(aParams->crtype_s, "median")) ? MUSE_SCIPOST_PARAM_CRTYPE_MEDIAN :
      MUSE_SCIPOST_PARAM_CRTYPE_INVALID_VALUE;
  cpl_ensure_code(aParams->crtype != MUSE_SCIPOST_PARAM_CRTYPE_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.crsigma");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->crsigma = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.rc");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->rc = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.pixfrac");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->pixfrac = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.ld");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->ld = cpl_parameter_get_int(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.format");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->format_s = cpl_parameter_get_string(p);
  aParams->format =
    (!strcasecmp(aParams->format_s, "Cube")) ? MUSE_SCIPOST_PARAM_FORMAT_CUBE :
    (!strcasecmp(aParams->format_s, "Euro3D")) ? MUSE_SCIPOST_PARAM_FORMAT_EURO3D :
    (!strcasecmp(aParams->format_s, "xCube")) ? MUSE_SCIPOST_PARAM_FORMAT_XCUBE :
    (!strcasecmp(aParams->format_s, "xEuro3D")) ? MUSE_SCIPOST_PARAM_FORMAT_XEURO3D :
    (!strcasecmp(aParams->format_s, "sdpCube")) ? MUSE_SCIPOST_PARAM_FORMAT_SDPCUBE :
      MUSE_SCIPOST_PARAM_FORMAT_INVALID_VALUE;
  cpl_ensure_code(aParams->format != MUSE_SCIPOST_PARAM_FORMAT_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.weight");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->weight_s = cpl_parameter_get_string(p);
  aParams->weight =
    (!strcasecmp(aParams->weight_s, "exptime")) ? MUSE_SCIPOST_PARAM_WEIGHT_EXPTIME :
    (!strcasecmp(aParams->weight_s, "fwhm")) ? MUSE_SCIPOST_PARAM_WEIGHT_FWHM :
    (!strcasecmp(aParams->weight_s, "none")) ? MUSE_SCIPOST_PARAM_WEIGHT_NONE :
      MUSE_SCIPOST_PARAM_WEIGHT_INVALID_VALUE;
  cpl_ensure_code(aParams->weight != MUSE_SCIPOST_PARAM_WEIGHT_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.filter");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->filter = cpl_parameter_get_string(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.skymethod");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->skymethod_s = cpl_parameter_get_string(p);
  aParams->skymethod =
    (!strcasecmp(aParams->skymethod_s, "none")) ? MUSE_SCIPOST_PARAM_SKYMETHOD_NONE :
    (!strcasecmp(aParams->skymethod_s, "subtract-model")) ? MUSE_SCIPOST_PARAM_SKYMETHOD_SUBTRACT_MODEL :
    (!strcasecmp(aParams->skymethod_s, "model")) ? MUSE_SCIPOST_PARAM_SKYMETHOD_MODEL :
    (!strcasecmp(aParams->skymethod_s, "simple")) ? MUSE_SCIPOST_PARAM_SKYMETHOD_SIMPLE :
      MUSE_SCIPOST_PARAM_SKYMETHOD_INVALID_VALUE;
  cpl_ensure_code(aParams->skymethod != MUSE_SCIPOST_PARAM_SKYMETHOD_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.lambdamin");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->lambdamin = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.lambdamax");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->lambdamax = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.lambdaref");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->lambdaref = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.darcheck");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->darcheck_s = cpl_parameter_get_string(p);
  aParams->darcheck =
    (!strcasecmp(aParams->darcheck_s, "none")) ? MUSE_SCIPOST_PARAM_DARCHECK_NONE :
    (!strcasecmp(aParams->darcheck_s, "check")) ? MUSE_SCIPOST_PARAM_DARCHECK_CHECK :
    (!strcasecmp(aParams->darcheck_s, "correct")) ? MUSE_SCIPOST_PARAM_DARCHECK_CORRECT :
      MUSE_SCIPOST_PARAM_DARCHECK_INVALID_VALUE;
  cpl_ensure_code(aParams->darcheck != MUSE_SCIPOST_PARAM_DARCHECK_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.skymodel_fraction");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->skymodel_fraction = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.skymodel_ignore");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->skymodel_ignore = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.skymodel_sampling");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->skymodel_sampling = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.skymodel_csampling");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->skymodel_csampling = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.sky_crsigma");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->sky_crsigma = cpl_parameter_get_string(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.rvcorr");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->rvcorr_s = cpl_parameter_get_string(p);
  aParams->rvcorr =
    (!strcasecmp(aParams->rvcorr_s, "bary")) ? MUSE_SCIPOST_PARAM_RVCORR_BARY :
    (!strcasecmp(aParams->rvcorr_s, "helio")) ? MUSE_SCIPOST_PARAM_RVCORR_HELIO :
    (!strcasecmp(aParams->rvcorr_s, "geo")) ? MUSE_SCIPOST_PARAM_RVCORR_GEO :
    (!strcasecmp(aParams->rvcorr_s, "none")) ? MUSE_SCIPOST_PARAM_RVCORR_NONE :
      MUSE_SCIPOST_PARAM_RVCORR_INVALID_VALUE;
  cpl_ensure_code(aParams->rvcorr != MUSE_SCIPOST_PARAM_RVCORR_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost.astrometry");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->astrometry = cpl_parameter_get_bool(p);
    
  return 0;
} /* muse_scipost_params_fill() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Execute the plugin instance given by the interface
  @param    aPlugin   the plugin
  @return   0 if everything is ok, -1 if not called as part of a recipe
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_exec(cpl_plugin *aPlugin)
{
  if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
    return -1;
  }
  muse_processing_recipeinfo(aPlugin);
  cpl_recipe *recipe = (cpl_recipe *)aPlugin;
  cpl_msg_set_threadid_on();

  cpl_frameset *usedframes = cpl_frameset_new(),
               *outframes = cpl_frameset_new();
  muse_scipost_params_t params;
  muse_scipost_params_fill(&params, recipe->parameters);

  cpl_errorstate prestate = cpl_errorstate_get();

  muse_processing *proc = muse_processing_new("muse_scipost",
                                              recipe);
  int rc = muse_scipost_compute(proc, &params);
  cpl_frameset_join(usedframes, proc->usedframes);
  cpl_frameset_join(outframes, proc->outframes);
  muse_processing_delete(proc);
  
  if (!cpl_errorstate_is_equal(prestate)) {
    /* dump all errors from this recipe in chronological order */
    cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
    /* reset message level to not get the same errors displayed again by esorex */
    cpl_msg_set_level(CPL_MSG_INFO);
  }
  /* clean up duplicates in framesets of used and output frames */
  muse_cplframeset_erase_duplicate(usedframes);
  muse_cplframeset_erase_duplicate(outframes);

  /* to get esorex to see our classification (frame groups etc.), *
   * replace the original frameset with the list of used frames   *
   * before appending product output frames                       */
  /* keep the same pointer, so just erase all frames, not delete the frameset */
  muse_cplframeset_erase_all(recipe->frames);
  cpl_frameset_join(recipe->frames, usedframes);
  cpl_frameset_join(recipe->frames, outframes);
  cpl_frameset_delete(usedframes);
  cpl_frameset_delete(outframes);
  return rc;
} /* muse_scipost_exec() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Destroy what has been created by the 'create' function
  @param    aPlugin   the plugin
  @return   0 if everything is ok, -1 if not called as part of a recipe
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_destroy(cpl_plugin *aPlugin)
{
  /* Get the recipe from the plugin */
  cpl_recipe *recipe;
  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
    recipe = (cpl_recipe *)aPlugin;
  } else {
    return -1;
  }

  /* Clean up */
  cpl_parameterlist_delete(recipe->parameters);
  muse_processinginfo_delete(recipe);
  return 0;
} /* muse_scipost_destroy() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Add this recipe to the list of available plugins.
  @param    aList   the plugin list
  @return   0 if everything is ok, -1 otherwise (but this cannot happen)

  Create the recipe instance and make it available to the application using the
  interface. This function is exported.
 */
/*----------------------------------------------------------------------------*/
int
cpl_plugin_get_info(cpl_pluginlist *aList)
{
  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
  cpl_plugin *plugin = &recipe->interface;

  char *helptext;
  if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
    helptext = cpl_sprintf("%s%s", muse_scipost_help,
                           muse_scipost_help_esorex);
  } else {
    helptext = cpl_sprintf("%s", muse_scipost_help);
  }

  /* Initialize the CPL plugin stuff for this module */
  cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
                  CPL_PLUGIN_TYPE_RECIPE,
                  "muse_scipost",
                  "Prepare reduced and combined science products.",
                  helptext,
                  "Peter Weilbacher",
                  "usd-help@eso.org",
                  muse_get_license(),
                  muse_scipost_create,
                  muse_scipost_exec,
                  muse_scipost_destroy);
  cpl_pluginlist_append(aList, plugin);
  cpl_free(helptext);

  return 0;
} /* cpl_plugin_get_info() */

/**@}*/