/* $Id: visir_parameter.c,v 1.29 2012/02/02 10:30:05 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: jtaylor $
 * $Date: 2012/02/02 10:30:05 $
 * $Revision: 1.29 $
 * $Name: visir-3_5_1 $
 */

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

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

#include <string.h>
#include <math.h>
#include <float.h>
#include <assert.h>
#include <cpl.h>

#include "irplib_utils.h"

#include "visir_parameter.h"
#include "visir_dfs.h"
#include "visir_pfits.h"

/*-----------------------------------------------------------------------------
                                   Define
 -----------------------------------------------------------------------------*/

#define visir_plot_manpage                                      \
    "The recipe can produce a number of predefined plots. "     \
    "Zero means that none of the plots are produced, while "    \
    "increasing values (e.g. 1 or 2) increases the number "     \
    "of plots produced. If the plotting fails a warning is "    \
    "produced, and the recipe continues. "                      \
    "The default behaviour of the plotting is to use "          \
    "gnuplot (with option -persist). The recipe currently "     \
    "produces 1D-plots using gnuplot commands. The recipe "     \
    "user can control the actual plotting-command used by "     \
    "the recipe to create the plot by setting the "             \
    "environment variable CPL_PLOTTER. Currently, if "          \
    "CPL_PLOTTER "                                              \
    "is set it must contain the string 'gnuplot'. Setting "     \
    "it to 'cat > my_gnuplot_$$.txt' causes a number of "       \
    "ASCII-files to be created, which each produce a plot "     \
    "when given as standard input to gnuplot (e.g. later "      \
    "or on a different computer). 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) "                \
    "and then setting CPL_PLOTTER to my_gnuplot.pl. "           \
    "The predefined plots include plotting of images. "         \
    "Images can be plotted not only with gnuplot, but also "    \
    "using the pnm format. This is controlled with the "        \
    "environment variable CPL_IMAGER. If CPL_IMAGER "           \
    "is set to a string that does not contain the word "        \
    "gnuplot, the recipe will generate the plot in pnm "        \
    "format. E.g. setting CPL_IMAGER to "                       \
    "'display - &' will produce a gray-scale image "            \
    "using the image viewer display."



/* To be called from visir_parameter_set() */
#define VISIR_PARAMETER_SET(MASK, VARNAME, TYPE, MAN, DEFAULT, SHORT)          \
if (bitmask & MASK) {                                                          \
    char * paramname = cpl_sprintf(PACKAGE ".%s." VARNAME, recipe);            \
                                                                               \
    p = cpl_parameter_new_value(paramname, TYPE, MAN, context, DEFAULT);       \
    cpl_free(paramname);                                                       \
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, SHORT);                 \
    cpl_parameterlist_append(self, p);                                         \
                                                                               \
    (void)cpl_error_set_where(cpl_func); /* Propagate error, if any */         \
                                                                               \
    bitmask ^= MASK; /* Reset bit. At the end bitmask must be zero */          \
                                                                               \
    /* Verify that each mask value is unique */                                \
    if (chkmask & MASK)                                                        \
        (void)cpl_error_set(cpl_func, CPL_ERROR_UNSPECIFIED);                  \
    chkmask |= MASK;                                                           \
}

/* To be called from visir_parameterlist_get_bool() */
#define VISIR_PARAMETER_GET_BOOL(MASK, VARNAME)                                \
if (bitmask & MASK) {                                                          \
    value = irplib_parameterlist_get_bool(self, PACKAGE, recipe, VARNAME);     \
                                                                               \
    cpl_error_ensure(!cpl_error_get_code(), cpl_error_get_code(),              \
                     return(CPL_FALSE), "mask=0x%llx", MASK);                  \
    nbits++;         /* Total number of bits must be one  */                   \
    bitmask ^= MASK; /* - bitmask must be zero at the end */                   \
                                                                               \
}

/* To be called from visir_parameterlist_get_int() */
#define VISIR_PARAMETER_GET_INT(MASK, VARNAME)                                 \
if (bitmask & MASK) {                                                          \
    value = irplib_parameterlist_get_int(self, PACKAGE, recipe, VARNAME);      \
                                                                               \
    cpl_error_ensure(!cpl_error_get_code(), cpl_error_get_code(), return(0),   \
                     "mask=0x%llx", MASK);                                     \
                                                                               \
    nbits++;         /* Total number of bits must be one  */                   \
    bitmask ^= MASK; /* - bitmask must be zero at the end */                   \
                                                                               \
}

/* To be called from visir_parameterlist_get_double() */
#define VISIR_PARAMETER_GET_DOUBLE(MASK, VARNAME)                              \
if (bitmask & MASK) {                                                          \
    value = irplib_parameterlist_get_double(self, PACKAGE, recipe, VARNAME);   \
                                                                               \
    cpl_error_ensure(!cpl_error_get_code(), cpl_error_get_code(), return(0.0), \
                     "mask=0x%llx", MASK);                                     \
                                                                               \
    nbits++;         /* Total number of bits must be one  */                   \
    bitmask ^= MASK; /* - bitmask must be zero at the end */                   \
                                                                               \
}

/* To be called from visir_parameterlist_get_string() */
#define VISIR_PARAMETER_GET_STRING(MASK, VARNAME)                              \
if (bitmask & MASK) {                                                          \
    value = irplib_parameterlist_get_string(self, PACKAGE, recipe, VARNAME);   \
                                                                               \
    cpl_error_ensure(!cpl_error_get_code(), cpl_error_get_code(), return(NULL),\
                     "mask=0x%llx", MASK);                                     \
                                                                               \
    nbits++;         /* Total number of bits must be one  */                   \
    bitmask ^= MASK; /* - bitmask must be zero at the end */                   \
                                                                               \
}


/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_parameter VISIR recipe parameters
 *
 * TBD
 */
/*----------------------------------------------------------------------------*/

/**@{*/


/*----------------------------------------------------------------------------*/
/**
  @brief    Define the specified parameters
  @param    self     The parameterlist to set
  @param    recipe   The recipe name
  @param    bitmask  parameter specification
  @return   CPL_ERROR_NONE or the relevant CPL error code on error

 */
/*----------------------------------------------------------------------------*/
cpl_error_code visir_parameter_set(cpl_parameterlist * self,
                                   const char * recipe,
                                   visir_parameter bitmask)
{

    cpl_parameter * p;
    char          * context;
    visir_parameter chkmask  = 0;    /* Verify that each mask value is unique */
    cpl_boolean     zerodist = CPL_FALSE;
    cpl_boolean     dostrip  = CPL_TRUE;


    cpl_ensure_code(self,   CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(recipe, CPL_ERROR_NULL_INPUT);

    context = cpl_sprintf(PACKAGE ".%s", recipe);

    /* --nod */
    VISIR_PARAMETER_SET(VISIR_PARAM_NODPOS, "nodding", CPL_TYPE_STRING,
                        "An optional ASCII specification of the nodding positions "
                        "(in case they are missing from the FITS-file). "
                        "The file must consist of one line per input FITS-file "
                        "and each line must consist of an integer (which is "
                        "ignored) followed by a 0 or 1 (to indicate object or sky). ",
                        "", "nod");

    /* --auto_bpm */
    VISIR_PARAMETER_SET(VISIR_PARAM_AUTOBPM, "auto_bpm", CPL_TYPE_BOOL,
                        "Automatic detection and correction of bad pixels",
                        TRUE, "auto_bpm");

    /* --g */
    VISIR_PARAMETER_SET(VISIR_PARAM_GLITCH, "rem_glitch", CPL_TYPE_BOOL,
                        "Automatic filtering of glitches", FALSE, "g");

    /* --p */
    VISIR_PARAMETER_SET(VISIR_PARAM_PURGE, "purge_bad", CPL_TYPE_BOOL,
                        "Automatic purging of half-cycle images whose median "
                        "deviates more than a factor three from the mean of "
                        "the medians of half-cycle images or whose standard "
                        "deviation deviates more than a factor three from the "
                        "mean of their standard deviations", FALSE, "p");

    /* --union */
    VISIR_PARAMETER_SET(VISIR_PARAM_UNION, "union", CPL_TYPE_BOOL,
                        "Combine images using their union, as opposed to their "
                        "intersection (deprecated and ignored, "
                        "see --combine_method)", TRUE, "union");

    /* --rej */
    VISIR_PARAMETER_SET(VISIR_PARAM_REJECT, "rej", CPL_TYPE_STRING,
                        "Each resulting pixel is the average of the "
                        "corresponding (interpolated) pixel value in each "
                        "jittered image. A positive value, n1, for the first "
                        "of the two integers specifies that for each pixel the "
                        "smallest n1 pixel values shall be ignored in the "
                        "averaging. Similarly, a positive value, n2, for the "
                        "second of the two integers specifies that for each "
                        "pixel the largest n2 pixel values shall be ignored in "
                        "the averaging.", "0-0", "rej");

    /* --plot */
    VISIR_PARAMETER_SET(VISIR_PARAM_PLOT, "plot", CPL_TYPE_INT,
                        visir_plot_manpage, 0, "plot");

    if (bitmask & VISIR_PARAM_ZERODIST) {
        bitmask ^= VISIR_PARAM_ZERODIST;
        /* The is not a real option - its presence means that the default value
           for the distortion correction parameters are all zero */
        zerodist = CPL_TRUE;
    }


    /* --slit_skew */
    /* The detector skew in pixels over the hole detector */
    /* phi = atan(28.6/1024); */
    VISIR_PARAMETER_SET(VISIR_PARAM_SLITSKEW, "phi", CPL_TYPE_DOUBLE,
                        "Distortion correction: Skew of slit (degrees) "
                        "(clockwise)", zerodist ? 0.0 : 1.6, "slit_skew");

    /* --spectrum_skew */
    /* The detector skew in pixels over the hole detector */
    /* ksi = atan(12.5/1024); */
    VISIR_PARAMETER_SET(VISIR_PARAM_SPECSKEW, "ksi", CPL_TYPE_DOUBLE,
                        "Distortion correction: LMR Skew of spectrum (degrees) "
                        "(counter-clockwise). Not used in High Resolution",
                        zerodist ? 0.0 : 0.7, "spectrum_skew");

    /* --vert_arc */
    /* LR eps = 0.052mm/0.050mm  [pixel] (PXSPACE = 50 micron) */
    VISIR_PARAMETER_SET(VISIR_PARAM_VERTARC, "eps", CPL_TYPE_DOUBLE,
                        "Distortion correction: LR Detector vertical curvature "
                        "(pixel). Reduced by a factor 4 in MR. Not used in HR "
                        "A-side. Increased by a factor 115/52 in HR B-side",
                        zerodist ? 0.0 : 1.04, "vert_arc");

    /* --hori_arc */
    /* delta = 0.004mm/0.050mm [pixel] (PXSPACE = 50 micron) */
    VISIR_PARAMETER_SET(VISIR_PARAM_HORIARC, "delta", CPL_TYPE_DOUBLE,
                        "Distortion correction: LMR Detector horizontal "
                        "curvature (pixel). Increased by a factor 1.5 in HR "
                        "A-side. Reduced by a factor 2 in HR B-side",
                        zerodist ? 0.0 : 0.08, "hori_arc");

    /* --orderoffset */
    VISIR_PARAMETER_SET(VISIR_PARAM_ORDEROFF, "orderoffset", CPL_TYPE_INT,
                        "Echelle order offset. The offset is relative to the "
                        "main order. The allowed range of offsets depend on "
                        "the selected grism. The offset can never exceed +/-4. "
                        "If the main order is e.g. 8 an order offset of +1 "
                        "will cause the recipe to base the data reduction on "
                        "order 9. With a positive order offset the central "
                        "wavelength becomes smaller while for a negative "
                        "order offset the central wavelength becomes larger.", 0,
                        "orderoffset");

    /* --off */
    VISIR_PARAMETER_SET(VISIR_PARAM_OFFSETS, "offsets",  CPL_TYPE_STRING,
                        "An optional ASCII specification of the offsets "
                        "in case those in FITS-headers are missing or wrong. "
                        "The file must consist of one line per input pair of "
                        "FITS-files, and each line must consist of two "
                        "numbers which represent the shift in pixels of that "
                        "image relative to the first image. The first line "
                        "should thus comprise two zeros. Correct FITS-header "
                        "offsets mean that the i'th X offset can be gotten "
                        "from Xoffset_0 - Xoffset_i, where Xoffset_i is the "
                        "value of " VISIR_PFITS_DOUBLE_CUMOFFSETX " and "
                        "likewise for Y.", "", "off");

    VISIR_PARAMETER_SET(VISIR_PARAM_REFINE, "refine", CPL_TYPE_BOOL,
                        "User-defined refining of the image offsets. See "
                        "options objs and xcorr", FALSE, "ref");

    VISIR_PARAMETER_SET(VISIR_PARAM_OBJECTS, "objects", CPL_TYPE_STRING,
                        "The shift and add of images needs anchor points that "
                        "typically are bright objects. These are normally "
                        "detected automatically but with user-defined refining "
                        "of offsets enabled, they must be provided by the user "
                        "through an ASCII file containing one line per anchor "
                        "point with each line consisting of its x and y "
                        "coordinate (in pixels). This file is ignored with "
                        "user-defined refining of offsets disabled.",
                        "", "objs");


    /* --xcorr */
    VISIR_PARAMETER_SET(VISIR_PARAM_XCORR, "xcorr", CPL_TYPE_STRING,
                        "If user-defined refining of offsets is enabled a "
                        "cross-correlation of the images is performed. In "
                        "order to speed up this process, this cross-"
                        "correlation is performed only on smaller rectangles "
                        "around the anchor points. The first two parameters "
                        "is the half-size of this rectangle in pixels. The "
                        "second pair is the maximum shift in x and y (pixels) "
                        "evaluated by the cross-correlation on the rectangle. "
                        "Used only if user-defined refining of offsets is enabled.",
                        "10-10-25-25", "xcorr");

    /* --jy_val */
    VISIR_PARAMETER_SET(VISIR_PARAM_JYVAL, "jy_val", CPL_TYPE_DOUBLE,
                        "The flux of the standard star in Jansky",
                        -999.0, "jy_val");

    /* --radii */
    VISIR_PARAMETER_SET(VISIR_PARAM_RADII, "radii", CPL_TYPE_STRING,
                        "Radii : star_max bg_int bg_ext",
                        "20-20-30", "radii");

    /* --low */
    VISIR_PARAMETER_SET(VISIR_PARAM_LOWLIM, "low", CPL_TYPE_DOUBLE,
                        "Low threshold for the bad pixel map",
                        0.2, "low");

    /* --high */
    VISIR_PARAMETER_SET(VISIR_PARAM_HIGHLIM, "high", CPL_TYPE_DOUBLE,
                        "High threshold for the bad pixel map",
                        5.0, "high");

    /* --fixcombi */
    VISIR_PARAMETER_SET(VISIR_PARAM_FIXCOMBI, "fixcombi", CPL_TYPE_BOOL,
                        "Perform the distortion correction on the combined "
                        "image, and not on each of the jittered images. "
                        "This will reduce excution time and degrade the quality "
                        "of the combined image",
                        FALSE, "fixcombi");

    /* --emis_tol */
    VISIR_PARAMETER_SET(VISIR_PARAM_EMIS_TOL, "emis_tol", CPL_TYPE_DOUBLE,
                        "The computation of the mean and standard deviation "
                        "of the sensitivity is done for wavelengths with an "
                        "atmospheric emissivity of at most "
                        "emis_min + emis_tol * (emis_max - emis_min), where "
                        "emis_min is the minimum emissivity in the observed "
                        "wavelength range and emis_max is the ditto maximum. "
                        "Thus emis_tol = 1 means that all wavelengths are "
                        "included.",
                        1.0, "emis_tol");

    /* --qeff */
    VISIR_PARAMETER_SET(VISIR_PARAM_QEFF, "qeff", CPL_TYPE_DOUBLE,
                        "The overall Quantum-efficiency. The default value "
                        "comes from PH. Galdemard measurements. This option "
                        "is ignored for all but " VISIR_SPC_QEFF_ASCII
                        "-tagged files, while the efficiencies in such files "
                        "are multiplied by this overall efficiency",
                        0.72, "qeff");

    /* --r */
    VISIR_PARAMETER_SET(VISIR_PARAM_REJBORD, "rej_bord", CPL_TYPE_STRING,
                       "Rejected left right bottom and top border (pixel)",
                       "50 50 50 50", "r");

    /* --hot_t */
    VISIR_PARAMETER_SET(VISIR_PARAM_HOT_LIM, "hot_threshold", CPL_TYPE_DOUBLE,
                       "Hot pixel map threshold", 10.0, "hot_t");

    /* --cold_t */
    VISIR_PARAMETER_SET(VISIR_PARAM_COLD_LIM, "cold_threshold", CPL_TYPE_DOUBLE,
                       "Cold pixel map threshold", 6.0, "cold_t");

    /* --dev_t */
    VISIR_PARAMETER_SET(VISIR_PARAM_DEV_LIM, "dev_threshold", CPL_TYPE_DOUBLE,
                       "Deviant pixel map threshold", 5.0, "dev_t");

    /* --nsamples */
    VISIR_PARAMETER_SET(VISIR_PARAM_NSAMPLES, "nsamples", CPL_TYPE_INT,
                       "Number of samples for Read-Out Noise (RON) computation",
                       100, "nsamples");

    /* --hsize */
    VISIR_PARAMETER_SET(VISIR_PARAM_HALFSIZE, "hsize", CPL_TYPE_INT,
                       "Half size of the window for Read-Out Noise (RON) "
                        "computation", 2, "hsize");

    /* --comb_meth */
    /* FIXME: Use cpl_parameter_new_enum() */
    VISIR_PARAMETER_SET(VISIR_PARAM_COMBINE, "comb_meth", CPL_TYPE_STRING,
                        "Combine images using one of: 1) Onto the first image "
                        "(first); 2) Their union (union); 3) Their intersection"
                        " (inter). NB: Only the 'first'-method produces an "
                        "image product with WCS coordinates. A successful "
                        "'first'-method always produces a combined image with "
                        "dimensions equal to those of the input images. "
                        "For the 'union'-method the result image is at least "
                        "as large as the input images while for the 'inter'-"
                        "method the result image is at most as large as the "
                        "input images", "union", "combine_method");

    if (bitmask & VISIR_PARAM_STRIPNON) {
        bitmask ^= VISIR_PARAM_STRIPNON;
        /* The is not a real option - its presence means that the default value
           for the destriping iterations is zero */
        dostrip = CPL_FALSE;
    }

    /* --nstripe */
    VISIR_PARAMETER_SET(VISIR_PARAM_STRIPITE, "nstripe",
                        CPL_TYPE_INT, "Max number of destriping iterations "
                        "(0 to disable destriping). Horizontal destriping is "
                        "done first and if no horizontal striping is detected, "
                        "vertical destriping is performed", dostrip ? 15 : 0,
                        "destripe_iterations");

    /* --mstripe */
    VISIR_PARAMETER_SET(VISIR_PARAM_STRIPMOR, "mstripe", CPL_TYPE_BOOL,
                       "Destripe with morphological cleaning", FALSE,
                        "destripe_morpho");

    /* --rl */
    VISIR_PARAMETER_SET(VISIR_PARAM_REJLEFT, "reject_left", CPL_TYPE_INT,
                        "Reject leftmost columns in spectrum extraction, zero "
                        "means all columns on the left are used. In cross-"
                        "dispersion mode a (small) negative number may be used "
                        "(pixel)", 0, "rl");

    /* --rr */
    VISIR_PARAMETER_SET(VISIR_PARAM_REJRIGHT, "reject_right", CPL_TYPE_INT,
                        "Reject rightmost columns in spectrum extraction, zero "
                        "means all columns on the right are used. In cross-"
                        "dispersion mode a (small) negative number may be used "
                        "(pixel)", 0, "rr");

    /* --eccmax */
    VISIR_PARAMETER_SET(VISIR_PARAM_ECCMAX, "eccmax", CPL_TYPE_DOUBLE,
                       "The maximum eccentricity allowed in the combination "
                        "of the three (in parallel nod/chopping) or four (in "
                        "perpendicular nod/chopping) beams. In parallel mode, "
                        "three perfectly aligned points spaced with the "
                        "chopnod throw will have eccentricity 0, while in "
                        "perpedicular mode a square with the chopnod throw as "
                        "the side length will have eccentricity 0",
                        0.25, "eccmax");

    cpl_free(context);

    cpl_ensure_code(!cpl_error_get_code(), cpl_error_get_code());
    cpl_ensure_code(bitmask == 0, CPL_ERROR_UNSUPPORTED_MODE);

    return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Retrieve the value of a VISIR boolean parameter 
  @param    self     The parameterlist to get from
  @param    recipe   The recipe name
  @param    bitmask  parameter specification
  @return   The boolean value, or CPL_FALSE on error
  @note     If called with a pre-existing error, the function will do nothing.

 */
/*----------------------------------------------------------------------------*/
cpl_boolean visir_parameterlist_get_bool(const cpl_parameterlist * self,
                                         const char * recipe,
                                         visir_parameter bitmask)
{

    int nbits = 0;
    cpl_boolean value = CPL_FALSE; /* Avoid (false) uninit warning */


    cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), CPL_FALSE);
    cpl_ensure(self,   CPL_ERROR_NULL_INPUT, CPL_FALSE);
    cpl_ensure(recipe, CPL_ERROR_NULL_INPUT, CPL_FALSE);

    /* --auto_bpm */
    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_AUTOBPM, "auto_bpm");

    /* --g */
    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_GLITCH, "rem_glitch");

    /* --p */
    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_PURGE, "purge_bad");

    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_REFINE, "refine");

    /* --fixcombi */
    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_FIXCOMBI, "fixcombi");

    /* --mstripe */
    VISIR_PARAMETER_GET_BOOL(VISIR_PARAM_STRIPMOR, "mstripe");


    cpl_ensure(bitmask == 0, CPL_ERROR_UNSUPPORTED_MODE, CPL_FALSE);
    cpl_ensure(nbits == 1,   CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);

    return value;

}


/*----------------------------------------------------------------------------*/
/**
  @brief    Retrieve the value of a VISIR integer parameter 
  @param    self     The parameterlist to get from
  @param    recipe   The recipe name
  @param    bitmask  parameter specification
  @return   The integer value, or zero on error
  @note     If called with a pre-existing error, the function will do nothing.

 */
/*----------------------------------------------------------------------------*/
int visir_parameterlist_get_int(const cpl_parameterlist * self,
                                const char * recipe,
                                visir_parameter bitmask)
{

    int nbits = 0;
    int value = 0; /* Avoid (false) uninit warning */


    cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), 0);
    cpl_ensure(self,   CPL_ERROR_NULL_INPUT, 0);
    cpl_ensure(recipe, CPL_ERROR_NULL_INPUT, 0);


    /* --plot */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_PLOT, "plot");

    /* --orderoffset */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_ORDEROFF, "orderoffset");

    /* --nsamples */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_NSAMPLES, "nsamples");

    /* --hsize */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_HALFSIZE, "hsize");

    /* --nstripe */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_STRIPITE, "nstripe");

    /* --rl */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_REJLEFT, "reject_left");

    /* --rr */
    VISIR_PARAMETER_GET_INT(VISIR_PARAM_REJRIGHT, "reject_right");


    cpl_ensure(bitmask == 0, CPL_ERROR_UNSUPPORTED_MODE, 0);
    cpl_ensure(nbits == 1,   CPL_ERROR_ILLEGAL_INPUT, 0);

    return value;

}

/*----------------------------------------------------------------------------*/
/**
  @brief    Retrieve the value of a VISIR parameter of type double
  @param    self     The parameterlist to get from
  @param    recipe   The recipe name
  @param    bitmask  parameter specification
  @return   The value, or undefined on error
  @note     If called with a pre-existing error, the function will do nothing.

 */
/*----------------------------------------------------------------------------*/
double visir_parameterlist_get_double(const cpl_parameterlist * self,
                                      const char * recipe,
                                      visir_parameter bitmask)
{

    int nbits = 0;
    double value = DBL_MAX; /* Avoid (false) uninit warning */


    cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), 0.0);
    cpl_ensure(self,   CPL_ERROR_NULL_INPUT, 0.0);
    cpl_ensure(recipe, CPL_ERROR_NULL_INPUT, 0.0);

    /* --slit_skew */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_SLITSKEW, "phi");

    /* --spectrum_skew */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_SPECSKEW, "ksi");

    /* --vert_arc */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_VERTARC, "eps");

    /* --hori_arc */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_HORIARC, "delta");

    /* --jy_val */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_JYVAL, "jy_val");

    /* --low */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_LOWLIM, "low");

    /* --high */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_HIGHLIM, "high");

    /* --emis_tol */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_EMIS_TOL, "emis_tol");

    /* --qeff */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_QEFF, "qeff");

    /* --hot_t */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_HOT_LIM, "hot_threshold");

    /* --cold_t */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_COLD_LIM, "cold_threshold");

    /* --dev_t */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_DEV_LIM, "dev_threshold");

    /* --eccmax */
    VISIR_PARAMETER_GET_DOUBLE(VISIR_PARAM_ECCMAX, "eccmax");

    cpl_ensure(bitmask == 0, CPL_ERROR_UNSUPPORTED_MODE, 0.0);
    cpl_ensure(nbits == 1,   CPL_ERROR_ILLEGAL_INPUT, 0.0);

    return value;

}



/*----------------------------------------------------------------------------*/
/**
  @brief    Retrieve the value of a VISIR string parameter 
  @param    self     The parameterlist to get from
  @param    recipe   The recipe name
  @param    bitmask  parameter specification
  @return   The string, or NULL on error

 */
/*----------------------------------------------------------------------------*/
const char * visir_parameterlist_get_string(const cpl_parameterlist * self,
                                            const char * recipe,
                                            visir_parameter bitmask)
{

    int nbits = 0;
    const char * value = NULL; /* Avoid (false) uninit warning */
    const cpl_boolean is_combine
        = bitmask & VISIR_PARAM_COMBINE ? CPL_TRUE : CPL_FALSE;

    cpl_ensure(self,   CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(recipe, CPL_ERROR_NULL_INPUT, NULL);

    /* --nod */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_NODPOS, "nodding");

    /* --rej */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_REJECT, "rej");

    /* --off */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_OFFSETS, "offsets");

    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_REFINE, "refine");

    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_OBJECTS, "objects");

    /* --xcorr */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_XCORR, "xcorr");

    /* --radii */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_RADII, "radii");

    /* --r */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_REJBORD, "rej_bord");

    /* --combine */
    VISIR_PARAMETER_GET_STRING(VISIR_PARAM_COMBINE, "comb_meth");

    cpl_ensure(bitmask == 0, CPL_ERROR_UNSUPPORTED_MODE, NULL);
    cpl_ensure(nbits == 1,   CPL_ERROR_ILLEGAL_INPUT, NULL);

    assert(value != NULL);

    /* FIXME: This should be handled by the enum */
    if (is_combine)
        cpl_ensure(strcmp(value, "first") == 0 || strcmp(value, "union") == 0 ||
                   strcmp(value, "intersect") == 0, CPL_ERROR_UNSUPPORTED_MODE,
                   NULL);

    return value;

}

/**@}*/
