/* $Id: kmo_dev_setup.c,v 1.10 2013-06-18 07:56:47 aagudo Exp $
 *
 * This file is part of the KMOS Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: aagudo $
 * $Date: 2013-06-18 07:56:47 $
 * $Revision: 1.10 $
 * $Name: not supported by cvs2svn $
 */

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

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


#include <math.h>
#include <string.h>

#include <cpl.h>

#include "kmclipm_math.h"

#include "kmo_priv_reconstruct.h"
#include "kmo_priv_functions.h"
#include "kmo_priv_fits_stack.h"
#include "kmo_cpl_extensions.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_constants.h"
#include "kmo_debug.h"

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

static int kmo_dev_setup_create(cpl_plugin *);
static int kmo_dev_setup_exec(cpl_plugin *);
static int kmo_dev_setup_destroy(cpl_plugin *);
static int kmo_dev_setup(cpl_parameterlist *, cpl_frameset *);

/*-----------------------------------------------------------------------------
 *                          Static variables
 *----------------------------------------------------------------------------*/

static char kmo_dev_setup_description[] =
"     #############################################\n"
"     ### INTENDED FOR PIPELINE DEVELOPERS ONLY ###\n"
"     #############################################\n"
"\n"
"This recipe is intended to create KMOS conform files in a semi-automatic manner.\n"
"It is sufficient to provide a single FITS file and a few parameters to create\n"
"KMOS conform FITS files suited for different recipes. Internally it calls repea-\n"
"tedly the recipe kmo_fits_stack. There are also parameters that allow to prepare\n"
"the frames, i.e. early test out of the lab, in a way they can be processed.\n"
"\n"
"One extension from the input frame is taken, some noise is added automatically\n"
"in order to create similar frames for the other extensions.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--type\n"
"Defines for which recipe the files should be created ('DARK', \n"
"'FLAT_ON', 'FLAT_OFF', 'ARC_ON', 'ARC_OFF', 'STD', 'SKY').\n"
"\n"
"--extension\n"
"Defines which extension is used to craete frames\n"
"\n"
"--xshift\n"
"--yshift\n"
"Shift frames in x and y\n"
"\n"
"--rotangle\n"
"Sets the ESO OCS ROT NAANGLE keyword in the primary header.\n"
"\n"
"--topcrop\n"
"--bottomcrop\n"
"--leftcrop\n"
"--rightcrop\n"
"These are cropping the image (filled with 0).\n"
"\n"
"--mainkey\n"
"--subkey\n"
"Add individual keywords to primary- or sub-header\n"
"\n"
"--valid\n"
"Defines if IFUs are active or inactive\n"
"\n"
"--objects\n"
"Defines if IFUs contain object or sky.\n"
"\n"
"--date\n"
"Sets the DATE-OBS keword in the primary header.\n"
"\n"
"--filter\n"
"Sets the filter type for all extensions.\n"
"\n"
"--grating\n"
"Sets the grating type for all extensions.\n"
"\n"
"-------------------------------------------------------------------------------\n"
"  Input files:\n"
"\n"
"   DO                     KMOS                                                 \n"
"   category               Type  Explanation                    Required #Frames\n"
"   --------               ----- -----------                    -------- -------\n"
"    DARK        or        RAW   Frame to create Master dark or    Y        1   \n"
"    FLAT_ON     or              Flat-on or                                     \n"
"    FLAT_OFF    or              Flat-off or                                    \n"
"    ARC_ON      or              Arc-on or                                      \n"
"    ARC_OFF     or              Arc-off or                                     \n"
"    SKY         or              Sky or                                         \n"
"    STD         or              Std star or                                    \n"
"    GENERIC                     any other generic KMOS frame                   \n"
"\n"
"  Output files:\n"
"\n"
"   DO                    KMOS\n"
"   category              Type   Explanation\n"
"   --------              -----  -----------\n"
"   <see comment>         RAW    Named depending on --type parameter\n"
"-------------------------------------------------------------------------------\n"
"\n";

/*-----------------------------------------------------------------------------
 *                              Functions code
 *----------------------------------------------------------------------------*/

/**
 * @defgroup kmo_dev_setup kmo_dev_setup Create aligned KMOS files out of test frames
 *
 * See recipe description for details.
 */

/**@{*/

/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

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

    cpl_plugin_init(plugin,
                        CPL_PLUGIN_API,
                        KMOS_BINARY_VERSION,
                        CPL_PLUGIN_TYPE_RECIPE,
                        "kmo_dev_setup",
                        "Create aligned KMOS files out of test frames",
                        kmo_dev_setup_description,
                        "Alex Agudo Berbel",
                        "kmos-spark@mpe.mpg.de",
                        kmos_get_license(),
                        kmo_dev_setup_create,
                        kmo_dev_setup_exec,
                        kmo_dev_setup_destroy);

    cpl_pluginlist_append(list, plugin);

    return 0;
}

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

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

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

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

     /* Fill the parameters list */
    /* --type */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.type",
                                CPL_TYPE_STRING,
                                "FITS type to create (DARK, FLAT_ON, "
                                "FLAT_OFF, ARC_ON, ARC_OFF, SKY, GENERIC, STD)",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "type");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --extension */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.extension",
                                CPL_TYPE_INT,
                                "FITS extension to process (0: primary, 1, 2,...)",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extension");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --xshift */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.xshift",
                                CPL_TYPE_INT,
                                "integer shift in x (to the right -> pos)",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xshift");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --yshift */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.yshift",
                                CPL_TYPE_INT,
                                "integer shift in y (to the top -> pos)",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "yshift");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

//    /* --rotation */
//    p = cpl_parameter_new_value("kmos.kmo_dev_setup.rotation",
//                                CPL_TYPE_DOUBLE,
//                                "rotation in degrees (CCW)",
//                                "kmos.kmo_dev_setup",
//                                0.0);
//    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rotation");
//    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
//    cpl_parameterlist_append(recipe->parameters, p);

    /* --rotangle */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.rotangle",
                                CPL_TYPE_DOUBLE,
                                "Rotator offset angle in degrees (CCW)",
                                "kmos.kmo_dev_setup",
                                -1.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rotangle");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --topcrop */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.topcrop",
                                CPL_TYPE_INT,
                                "number of rows to crop at top",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "topcrop");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --bottomcrop */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.bottomcrop",
                                CPL_TYPE_INT,
                                "number of rows to crop at bottom",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "bottomcrop");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --leftcrop */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.leftcrop",
                                CPL_TYPE_INT,
                                "number of columns to crop at left",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "leftcrop");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --rightcrop */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.rightcrop",
                                CPL_TYPE_INT,
                                "number of columns to crop at right",
                                "kmos.kmo_dev_setup",
                                0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rightcrop");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --mainkey */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.mainkey",
                             CPL_TYPE_STRING,
                             "Optional: Additional keywords for primary header",
                             "kmos.kmo_dev_setup",
                             "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mainkey");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --subkey */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.subkey",
                                CPL_TYPE_STRING,
                                "Optional: Additional keywords for sub headers",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "subkey");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --valid */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.valid",
                                CPL_TYPE_STRING,
                                "Optional: Specify which IFUs are active. "
                                "Either empty string or string with 8 elements"
                                " (ones or zeros) e.g: [1;0;1;0;0;...;1]",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "valid");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --objects */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.objects",
                                CPL_TYPE_STRING,
                                "Optional (STD only): Specify which IFUs contain"
                                " objects. Either empty string or string with 8"
                                " elements (ones or zeros) "
                                "e.g: [1;0;1;0;0;...;1]",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "objects");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --date */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.date",
                                CPL_TYPE_STRING,
                                "Optional (STD only): Specify the date to save "
                                "into DATE-OBS "
                                "e.g: [2010-01-31T11:53:15.9789]",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "date");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --filter */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.filter",
                                CPL_TYPE_STRING,
                                "filter type (K, H, HK, etc.)",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filter");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --grating */
    p = cpl_parameter_new_value("kmos.kmo_dev_setup.grating",
                                CPL_TYPE_STRING,
                                "grating type (K, H, HK, etc.)",
                                "kmos.kmo_dev_setup",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "grating");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    return 0;
}

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

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

    return kmo_dev_setup(recipe->parameters, recipe->frames);
}

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

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

    cpl_parameterlist_delete(recipe->parameters);
    return 0 ;
}

cpl_frameset* dev_frameset(const char *p1, const char *p2, const char *p3) {
    cpl_frameset *frset = NULL;
    cpl_frame *fr = NULL;

    KMO_TRY
    {

        KMO_TRY_EXIT_IF_NULL(
            frset = cpl_frameset_new());

        KMO_TRY_EXIT_IF_NULL(
            fr = cpl_frame_new());
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_filename(fr, p1));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_tag(fr, FS_DATA));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_type(fr, CPL_FRAME_TYPE_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_group(fr, CPL_FRAME_GROUP_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_level(fr, CPL_FRAME_LEVEL_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frameset_insert(frset, fr));

        KMO_TRY_EXIT_IF_NULL(
            fr = cpl_frame_new());
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_filename(fr, p2));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_tag(fr, FS_DATA));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_type(fr, CPL_FRAME_TYPE_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_group(fr, CPL_FRAME_GROUP_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_level(fr, CPL_FRAME_LEVEL_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frameset_insert(frset, fr));

        KMO_TRY_EXIT_IF_NULL(
            fr = cpl_frame_new());
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_filename(fr, p3));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_tag(fr, FS_DATA));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_type(fr, CPL_FRAME_TYPE_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_group(fr, CPL_FRAME_GROUP_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frame_set_level(fr, CPL_FRAME_LEVEL_NONE));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frameset_insert(frset, fr));
    }
    KMO_CATCH
    {
        cpl_frameset_delete(frset); frset = NULL;
    }
    return frset;
}

cpl_frameset* dev_frameset_master(const char *p1) {
    cpl_frameset *frset = NULL;

    cpl_frame *fr = NULL;
    int i = 0;

    KMO_TRY
    {

        KMO_TRY_EXIT_IF_NULL(
            frset = cpl_frameset_new());
        for (i = 0; i < 3; i++) {
            KMO_TRY_EXIT_IF_NULL(
                fr = cpl_frame_new());
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_filename(fr, p1));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_tag(fr, FS_DATA));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_type(fr, CPL_FRAME_TYPE_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_group(fr, CPL_FRAME_GROUP_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_level(fr, CPL_FRAME_LEVEL_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frameset_insert(frset, fr));

            KMO_TRY_EXIT_IF_NULL(
                fr = cpl_frame_new());
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_filename(fr, p1));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_tag(fr, FS_NOISE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_type(fr, CPL_FRAME_TYPE_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_group(fr, CPL_FRAME_GROUP_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frame_set_level(fr, CPL_FRAME_LEVEL_NONE));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_frameset_insert(frset, fr));
        }
    }
    KMO_CATCH
    {
        cpl_frameset_delete(frset); frset = NULL;
    }
    return frset;
}

/**
  @brief    Interpret the command line options and execute the data processing
  @param    parlist     the parameters list
  @param    frameset   the frames list
  @return   0 if everything is ok

  Possible _cpl_error_code_ set in this function:

    @li CPL_ERROR_ILLEGAL_INPUT      if operator not valid,
                                     if first operand not 3d or
                                     if second operand not valid
    @li CPL_ERROR_INCOMPATIBLE_INPUT if the dimensions of the two operands
                                     do not match
 */
static int kmo_dev_setup(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    int         ret_val     = 0,
                ext         = 0,
                xshift      = 0,
                yshift      = 0,
                topcrop     = 0,
                bottomcrop  = 0,
                leftcrop    = 0,
                rightcrop   = 0,
                x           = 0,
                y           = 0,
                nx          = 0,
                ny          = 0,
                i           = 0;
//    double      rotation    = 0.0;
    double      rotangle    = 0.0;

    float       *pimg1      = NULL,
                *ptmp_img   = NULL;

    const char  *type       = NULL,
                *mainkey    = NULL,
                *subkey     = NULL,
                *valid      = NULL,
                *objects_txt= NULL,
                *date       = NULL,
                *filter     = NULL,
                *grating    = NULL;

    char        pmainkey[2048],
                psubkey[2048],
                pvalid[2048],
                pfilename[2048],
                tmp_str[256];

    const char *obsid        = "-2147",
               *exptime_min  = "2.47624",
               *exptime_long  = "600";


    cpl_frame   *frame      = NULL;

    cpl_image   *img1       = NULL,
                *img_copy   = NULL,
                *noise      = NULL,
                *noise_copy = NULL,
                *img_noise1 = NULL,
                *img_noise2 = NULL,
                *img_noise3 = NULL,
                *tmp_img    = NULL;

    cpl_vector  *objects    = NULL;

    cpl_frameset *frset     = NULL;

//    cpl_frame   *fr         = NULL;

    cpl_parameterlist *plist    = NULL;

    cpl_parameter *p        = NULL;

    KMO_TRY
    {
        strcpy(pmainkey, "");
        strcpy(psubkey, "");
        strcpy(pvalid, "");
        strcpy(pfilename, "");
        strcpy(tmp_str, "");

        /* --- check input --- */
        KMO_TRY_ASSURE((parlist != NULL) &&
                       (frameset != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        /* --- get parameters --- */
        cpl_msg_info("", "--- Parameter setup for kmo_dev_setup -----");

        KMO_TRY_EXIT_IF_NULL(
            type = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.type"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.type"));

        ext = kmo_dfs_get_parameter_int(parlist,
                                        "kmos.kmo_dev_setup.extension");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_ASSURE(ext >= 0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "ext >= 0!");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.extension"));

        xshift = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.xshift");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.xshift"));

        yshift = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.yshift");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.yshift"));

//        rotation = kmo_dfs_get_parameter_double(parlist,
//                                           "kmos.kmo_dev_setup.rotation");
//        KMO_TRY_CHECK_ERROR_STATE();
//        KMO_TRY_EXIT_IF_ERROR(
//            kmo_dfs_print_parameter_help(parlist,
//                                        "kmos.kmo_dev_setup.rotation"));

        topcrop = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.topcrop");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.topcrop"));

        bottomcrop = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.bottomcrop");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.bottomcrop"));

        leftcrop = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.leftcrop");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.leftcrop"));

        rightcrop = kmo_dfs_get_parameter_int(parlist,
                                           "kmos.kmo_dev_setup.rightcrop");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.rightcrop"));

        KMO_TRY_EXIT_IF_NULL(
            mainkey = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.mainkey"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.mainkey"));

        KMO_TRY_EXIT_IF_NULL(
            subkey = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.subkey"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.subkey"));

        KMO_TRY_EXIT_IF_NULL(
            valid = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.valid"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.valid"));

        KMO_TRY_EXIT_IF_NULL(
            objects_txt = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.objects"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.objects"));

        KMO_TRY_EXIT_IF_NULL(
            date = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.date"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.date"));

        KMO_TRY_EXIT_IF_NULL(
            filter = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.filter"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.filter"));

        KMO_TRY_EXIT_IF_NULL(
            grating = kmo_dfs_get_parameter_string(parlist,
                                            "kmos.kmo_dev_setup.grating"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_dev_setup.grating"));

        cpl_msg_info("", "-------------------------------------------");

        KMO_TRY_ASSURE((strcmp(type, "DARK") == 0) ||
                       (strcmp(type, "FLAT_ON") == 0) ||
                       (strcmp(type, "FLAT_OFF") == 0) ||
                       (strcmp(type, "ARC_ON") == 0) ||
                       (strcmp(type, "ARC_OFF") == 0) ||
                       (strcmp(type, "SKY") == 0) ||
                       (strcmp(type, "STD") == 0) ||
                       (strcmp(type, "MASTER_FLAT") == 0) ||
                       (strcmp(type, "GENERIC") == 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Wrong type!");

        // get frame
        KMO_TRY_EXIT_IF_NULL(
            frame = kmo_dfs_get_frame(frameset, "0"));

        // load data
        KMO_TRY_EXIT_IF_NULL(
            img1 = kmclipm_image_load(cpl_frame_get_filename(frame),
                                CPL_TYPE_FLOAT,
                                0,
                                ext));

        //
        // SHIFT
        //
        if ((xshift != 0) || (yshift != 0)) {
            cpl_msg_info(cpl_func, "shift: x=%d, y=%d", xshift, yshift);
            KMO_TRY_EXIT_IF_ERROR(
                cpl_image_shift(img1, xshift, yshift));
        } else {
            cpl_msg_info(cpl_func, "no shift applied.");
        }
//cpl_image_save(img1, "dev_shift.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);

//        //
//        // ROTATION
//        //
//        if (fabs(rotation) >= 0.0001) {
//            cpl_msg_info(cpl_func, "rotation: r=%g CCW", rotation);
//            KMO_TRY_EXIT_IF_NULL(
//                    img2 = kmo_dev_rotate(img1, rotation,
//                                          1, "BCS", NONE_NANS));
//        } else {
//            cpl_msg_info(cpl_func, "no rotation applied.");
//            img2 = img1;
//        }
//cpl_image_save(img2, "dev_rotation.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);

        nx = cpl_image_get_size_x(img1);
        ny = cpl_image_get_size_y(img1);

        //
        // CROP
        //
        if ((topcrop != 0) || (bottomcrop != 0) ||
            (leftcrop != 0) || (rightcrop != 0)) {
            cpl_msg_info(cpl_func, "crop: top=%d, bottom=%d, left=%d, right=%d",
                         topcrop, bottomcrop, leftcrop, rightcrop);

            KMO_TRY_EXIT_IF_NULL(
                pimg1 = cpl_image_get_data_float(img1));

            // bottomcrop
            for (y = 0; y < bottomcrop; y++) {
                for (x = 0; x < nx; x++) {
                    pimg1[x+y*nx] = 0.0;
                }
            }

            // topcrop
            for (y = nx-topcrop; y < nx; y++) {
                for (x = 0; x < nx; x++) {
                    pimg1[x+y*nx] = 0.0;
                }
            }

            // leftcrop
            for (y = 0; y < ny; y++) {
                for (x = 0; x < leftcrop; x++) {
                    pimg1[x+y*nx] = 0.0;
                }
            }

            // rightcrop
            for (y = 0; y < ny; y++) {
                for (x = nx-rightcrop; x < nx; x++) {
                    pimg1[x+y*nx] = 0.0;
                }
            }
        } else {
            cpl_msg_info(cpl_func, "no crop applied.");
        }
//cpl_image_save(img1, "dev_crop.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);

        //
        // NOISIFY
        //
        KMO_TRY_EXIT_IF_NULL(
            noise = cpl_image_new(nx, ny, CPL_TYPE_FLOAT));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_image_fill_noise_uniform(noise, -1.0, 1.0));

        // calculate level
        float level = 0.0;
        if ((strcmp(type, "DARK") == 0) ||
            (strcmp(type, "SKY") == 0))
        {
            KMO_TRY_EXIT_IF_NULL(
                tmp_img = cpl_image_duplicate(img1));
            KMO_TRY_EXIT_IF_NULL(
                ptmp_img = cpl_image_get_data(tmp_img));
            for (i = 0; i < nx*ny; i++) {
                if (kmclipm_is_nan_or_inf(ptmp_img[i])) {
                    ptmp_img[i] = 0;
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();
            level = cpl_image_get_stdev(tmp_img);
            KMO_TRY_CHECK_ERROR_STATE();
            cpl_image_delete(tmp_img); tmp_img = NULL;

            KMO_TRY_EXIT_IF_ERROR(
                cpl_image_multiply_scalar(noise, level));

            // img_noise1:
            img_noise1 = img1;

            // img_noise2: just add noise
            KMO_TRY_EXIT_IF_NULL(
                img_noise2 = cpl_image_add_create(img1, noise));

            // img_noise3: add noise flipped horizontally
            cpl_image_flip(noise, 0);
            KMO_TRY_EXIT_IF_NULL(
                img_noise3 = cpl_image_add_create(img1, noise));
        } else {
            KMO_TRY_EXIT_IF_NULL(
                img_copy = cpl_image_duplicate(img1));

            KMO_TRY_EXIT_IF_NULL(
                noise_copy = cpl_image_duplicate(noise));

            // img_noise1:
            img_noise1 = img1;

            // img_noise2: add noise level 1
            KMO_TRY_EXIT_IF_ERROR(
                cpl_image_multiply_scalar(noise, 10));
            KMO_TRY_EXIT_IF_NULL(
                img_noise2 = cpl_image_add_create(img1, noise));

            // img_noise3: add noise level 2
            KMO_TRY_EXIT_IF_ERROR(
                cpl_image_multiply_scalar(noise_copy, 15));
            KMO_TRY_EXIT_IF_NULL(
                img_noise3 = cpl_image_add_create(img_copy, noise_copy));

            cpl_image_delete(img_copy); img_copy = NULL;
            cpl_image_delete(noise_copy); noise_copy = NULL;
        }
        cpl_image_delete(noise); noise = NULL;

        // these have to be stored!
        kmclipm_image_save(img_noise1, "tmp_delete1.fits", CPL_BPP_IEEE_FLOAT,
                           NULL, CPL_IO_CREATE, 0);
        kmclipm_image_save(img_noise2, "tmp_delete2.fits", CPL_BPP_IEEE_FLOAT,
                           NULL, CPL_IO_CREATE, 0);
        kmclipm_image_save(img_noise3, "tmp_delete3.fits", CPL_BPP_IEEE_FLOAT,
                           NULL, CPL_IO_CREATE, 0);
        cpl_image_delete(img_noise2); img_noise2 = NULL;
        cpl_image_delete(img_noise3); img_noise3 = NULL;

        //
        // STACK
        //

        // setup parameterlist

// all types
        /* --mainkey */

        if (strcmp(mainkey, "") != 0) {
            strcpy(pmainkey, mainkey);
            strcat(pmainkey, ";");
            strcat(pmainkey,"ESO OBS ID;int;");strcat(pmainkey, obsid);
        } else {
            strcpy(pmainkey,"ESO OBS ID;int;");strcat(pmainkey, obsid);
        }
//        strcat(pmainkey, ";");
        if (rotangle != -1) {
            char *nr = NULL;
            KMO_TRY_EXIT_IF_NULL(
                nr = cpl_sprintf("%g", rotangle));
            strcat(pmainkey,";ESO OCS ROT NAANGLE;double;");strcat(pmainkey, nr);
            cpl_free(nr); nr = NULL;
        }

        if (strcmp(filter, "") != 0) {
            strcat(pmainkey, ";ESO INS FILT1 ID;string;");strcat(pmainkey, filter);
            strcat(pmainkey,";ESO INS FILT2 ID;string;");strcat(pmainkey, filter);
            strcat(pmainkey,";ESO INS FILT3 ID;string;");strcat(pmainkey, filter);
        }

        if (strcmp(grating, "") != 0) {
            strcat(pmainkey,";ESO INS GRAT1 ID;string;");strcat(pmainkey, grating);
            strcat(pmainkey,";ESO INS GRAT2 ID;string;");strcat(pmainkey, grating);
            strcat(pmainkey,";ESO INS GRAT3 ID;string;");strcat(pmainkey, grating);
        }
        KMO_TRY_CHECK_ERROR_STATE();

        /* --subkey */
        if (strcmp(subkey, "") != 0) {
            strcpy(psubkey, subkey);
        }
        KMO_TRY_CHECK_ERROR_STATE();

//        if (strcmp(type, "DARK") == 0) {
            /* --mainkey */
            strcat(pmainkey, ";ESO DET SEQ1 MINDIT;double;");strcat(pmainkey, exptime_min);
            strcat(pmainkey, ";ESO DET NDIT;int;1");
            strcat(pmainkey, ";EXPTIME;double;");strcat(pmainkey, exptime_long);
            strcat(pmainkey, ";ESO DET READ CURNAME;string;Double");

            /* --subkey */
            if (strcmp(subkey, "") != 0) strcat(psubkey, ";");
            strcat(psubkey, "EXPTIME;double;");strcat(psubkey, exptime_long);
//        }

        if (strcmp(type, "FLAT_ON") == 0) {
            /* --mainkey */
            strcat(pmainkey,";ESO INS LAMP3 ST;bool;1");
//            strcat(pmainkey, "EXPTIME;double;");strcat(pmainkey, exptime_long);

            /* --subkey */
//            if (strcmp(subkey, "") != 0)  strcat(psubkey, ";");
//            strcat(psubkey, "EXPTIME;double;");strcat(psubkey, exptime_long);
        }
        KMO_TRY_CHECK_ERROR_STATE();

        if (strcmp(type, "ARC_ON") == 0)
        {
            strcat(pmainkey,";ESO INS LAMP1 ST;bool;1");
//            strcat(pmainkey, "EXPTIME;double;");strcat(pmainkey, exptime_long);

            /* --subkey */
            if (strcmp(subkey, "") != 0)  strcat(psubkey, ";");
//            strcat(psubkey, "EXPTIME;double;");strcat(psubkey, exptime_long);
        }
        KMO_TRY_CHECK_ERROR_STATE();

        if ((strcmp(type, "FLAT_OFF") == 0) ||
            (strcmp(type, "ARC_OFF") == 0)) {
            /* --mainkey */
//            strcat(pmainkey, "EXPTIME;double;");strcat(pmainkey, exptime_long);

            /* --subkey */
            if (strcmp(subkey, "") != 0)  strcat(psubkey, ";");
//            strcat(psubkey, "EXPTIME;double;");strcat(psubkey, exptime_long);
        }
        KMO_TRY_CHECK_ERROR_STATE();

        if ((strcmp(type, "SKY") == 0) ||
            (strcmp(type, "MASTER_FLAT") == 0) ||
            (strcmp(type, "STD") == 0) ||
            (strcmp(type, "GENERIC") == 0))
        {
            /* --mainkey */
            if (strcmp(type, "STD") == 0) {
                // add DATE-OBS keyword
//                strcat(pmainkey,";");
                strcat(pmainkey,";DATE-OBS;string;");strcat(pmainkey, date);
//                strcat(pmainkey, ";EXPTIME;double;");strcat(pmainkey, exptime_long);

                // add ESO OCS ARMi TYPE keywords
                objects = kmo_identify_values(objects_txt);
                KMO_TRY_ASSURE(cpl_vector_get_size(objects) == KMOS_IFUS_PER_DETECTOR,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "valid parameter must have 8 elements!");

                for (i = 0; i < cpl_vector_get_size(objects)*3; i++) {
                    strcat(pmainkey,";");
                    strcat(pmainkey,"ESO OCS ARM");
                    sprintf(tmp_str, "%d ", i+1);
                    strcat(pmainkey,tmp_str);
                    strcat(pmainkey,"TYPE;string;");

                    if (fabs(cpl_vector_get(objects, i%KMOS_IFUS_PER_DETECTOR)-1) > 0.01 ) {
                        // sky
                        strcat(pmainkey,"S");
                    } else {
                        //object
                        strcat(pmainkey,"O");
                    }
                }
                cpl_vector_delete(objects); objects = NULL;
            }
//printf("----------\n");
//printf("%s\n", pmainkey);
//printf("----------\n");
//            /* --subkey */
//            if (strcmp(subkey, "") != 0) {
//                strcat(psubkey, ";");
//            }
//            strcat(psubkey, ";EXPTIME;double;");
//            strcat(psubkey, exptime_long);
        }
        KMO_TRY_CHECK_ERROR_STATE();

// SETUP PARAMETERLIST
        plist = cpl_parameterlist_new();

        /* --valid */
        strcpy(pvalid, valid);
        strcat(pvalid, ";");
        strcat(pvalid, valid);
        strcat(pvalid, ";");
        strcat(pvalid, valid);

        p = cpl_parameter_new_value("kmos.kmo_fits_stack.valid",
                                    CPL_TYPE_STRING,
                                    "Optional: Specify which IFUs are active. "
                                    "Either empty string or string with 24 elements"
                                    " (ones or zeros) e.g: [1;0;1;0;0;...;1]",
                                    "kmos.kmo_fits_stack",
                                    pvalid);
        cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "valid");
        cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
        cpl_parameterlist_append(plist, p);

        if (strcmp(type, "MASTER_FLAT") != 0) {
            // all types except MASTER_FLAT

            /* --type (RAW, F1D, F2D, F1I, F2I, F3I) */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.type",
                                        CPL_TYPE_STRING,
                                        "The KMOS data format type (either \"RAW\", "
                                        "\"F1D\", \"F2D\", \"F1I\", \"F2I\", \"F3I\", "
                                        "\"F1S\", \"F1L\", \"F2L\")",
                                        "kmos.kmo_fits_stack",
                                        RAW);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "type");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);
            KMO_TRY_CHECK_ERROR_STATE();

            /* --mainkey */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.mainkey",
                                     CPL_TYPE_STRING,
                                     "Optional: Additional keywords for primary header",
                                     "kmos.kmo_fits_stack",
                                     pmainkey);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mainkey");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);

            /* --subkey */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.subkey",
                                        CPL_TYPE_STRING,
                                        "Optional: Additional keywords for sub headers",
                                        "kmos.kmo_fits_stack",
                                        psubkey);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "subkey");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);
            KMO_TRY_CHECK_ERROR_STATE();

            // stack 1st
            KMO_TRY_EXIT_IF_NULL(
                frset = dev_frameset("tmp_delete1.fits", "tmp_delete2.fits", "tmp_delete3.fits"));
            KMO_TRY_CHECK_ERROR_STATE();

            /* --filename */
            strcpy(pfilename, type);
            strcat(pfilename, "_123");
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.filename",
                                        CPL_TYPE_STRING,
                                        "Optional: The output filename (.fits will be "
                                                                    "added as postfix)",
                                        "kmos.kmo_fits_stack",
                                        pfilename);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filename");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);

            kmo_priv_fits_stack(plist, frset);
            KMO_TRY_CHECK_ERROR_STATE();
            cpl_frameset_delete(frset); frset = NULL;

            if ((strcmp(type, "DARK") == 0) ||
                (strcmp(type, "FLAT_ON") == 0) ||
                (strcmp(type, "FLAT_OFF") == 0) ||
                (strcmp(type, "SKY") == 0))
            {
                // stack 2nd
                KMO_TRY_EXIT_IF_NULL(
                    frset = dev_frameset("tmp_delete2.fits", "tmp_delete3.fits", "tmp_delete1.fits"));
                strcpy(pfilename, type);
                strcat(pfilename, "_231");
                KMO_TRY_EXIT_IF_NULL(
                    p = cpl_parameterlist_find(plist, "kmos.kmo_fits_stack.filename"));
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_parameter_set_string(p, pfilename));

                kmo_priv_fits_stack(plist, frset);
                KMO_TRY_CHECK_ERROR_STATE();
                cpl_frameset_delete(frset); frset = NULL;

                // stack 3rd
                KMO_TRY_EXIT_IF_NULL(
                    frset = dev_frameset("tmp_delete3.fits", "tmp_delete1.fits", "tmp_delete2.fits"));
                strcpy(pfilename, type);
                strcat(pfilename, "_312");
                KMO_TRY_EXIT_IF_NULL(
                    p = cpl_parameterlist_find(plist, "kmos.kmo_fits_stack.filename"));
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_parameter_set_string(p, pfilename));

                kmo_priv_fits_stack(plist, frset);
                KMO_TRY_CHECK_ERROR_STATE();
                cpl_frameset_delete(frset); frset = NULL;
            }
        } else{
            /* --type (RAW, F1D, F2D, F1I, F2I, F3I) */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.type",
                                        CPL_TYPE_STRING,
                                        "The KMOS data format type (either \"RAW\", "
                                        "\"F1D\", \"F2D\", \"F1I\", \"F2I\", \"F3I\", "
                                        "\"F1S\", \"F1L\", \"F2L\")",
                                        "kmos.kmo_fits_stack",
                                        F2D);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "type");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);
            KMO_TRY_CHECK_ERROR_STATE();

            /* --mainkey */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.mainkey",
                                     CPL_TYPE_STRING,
                                     "Optional: Additional keywords for primary header",
                                     "kmos.kmo_fits_stack",
                                     pmainkey);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mainkey");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);

            /* --subkey */
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.subkey",
                                        CPL_TYPE_STRING,
                                        "Optional: Additional keywords for sub headers",
                                        "kmos.kmo_fits_stack",
                                        psubkey);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "subkey");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);
            KMO_TRY_CHECK_ERROR_STATE();

            /* --filename */
            strcpy(pfilename, "master_flat_ones");
            p = cpl_parameter_new_value("kmos.kmo_fits_stack.filename",
                                        CPL_TYPE_STRING,
                                        "Optional: The output filename (.fits will be "
                                                                    "added as postfix)",
                                        "kmos.kmo_fits_stack",
                                        pfilename);
            cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filename");
            cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
            cpl_parameterlist_append(plist, p);

            KMO_TRY_EXIT_IF_NULL(
                frset = dev_frameset_master(cpl_frame_get_filename(frame)));
            KMO_TRY_CHECK_ERROR_STATE();

            kmo_priv_fits_stack(plist, frset);
            KMO_TRY_CHECK_ERROR_STATE();
            cpl_frameset_delete(frset); frset = NULL;
        }

        KMO_TRY_CHECK_ERROR_STATE();
        cpl_msg_info("", "*******************************************");
        cpl_msg_info("", "Saved a set of KMOS frames for recipe %s.", type );
        cpl_msg_info("", "*******************************************");
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -1;
    }

    cpl_image_delete(img1); img1 = NULL;
    cpl_parameterlist_delete(plist); plist = NULL;

    return ret_val;
}

/**@}*/
