/* 
 * 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
 */

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

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

#include <cpl.h>

#include "kmclipm_constants.h"
#include "kmclipm_functions.h"

#include "kmo_cpl_extensions.h"
#include "kmo_utils.h"
#include "kmo_dfs.h"
#include "kmo_debug.h"
#include "kmo_error.h"
#include "kmo_priv_copy.h"


static int kmo_copy_create(cpl_plugin *);
static int kmo_copy_exec(cpl_plugin *);
static int kmo_copy_destroy(cpl_plugin *);
static int kmo_copy(cpl_parameterlist *, cpl_frameset *);

static char kmo_copy_description[] =
"With this recipe a specified region of an IFU-based cube (F3I), image (F2I) or\n"
"vector (F1I) can be copied to a new FITS file. One can copy just a plane out\n"
"of a cube (any orientation) or a vector out of an image etc. By default the\n"
"operation applies to all IFUs. The input data can contain noise frames which\n"
"is then copied in the same manner as the input data.\n"
"It is also possible to extract a specific IFU out of a KMOS FITS structure\n"
"with 24 IFU extensions or 48 extensions if noise is present.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--ifu\n"
"Use this parameter to apply the operation to a specific IFU.\n"
"\n"
"--x\n"
"--y\n"
"--z\n"
"These are the start values in each dimension. The first pixel is adressed "
"with 1. \n"
"\n"
"--xsize\n"
"--ysize\n"
"--zsize\n"
"These are the extents in each dimension to copy.\n"
"\n"
"--autocrop\n"
"If set to TRUE all borders containing NaN values are cropped. Vectors will be\n"
"shortened, images and cubes can get smaller. In This special case following\n"
"parameters can be omitted: --x, --y, --z, --xsize, --ysize and --zsize.\n"
"\n"
"Examples:\n"
"---------\n"
"extract a cube-section of a cube: \n"
"esorex kmo_copy --x=3 --y=2 --z=1 --xsize=2 --ysize=3 --zsize=6 copy.sof\n"
"\n"
"extract plane:\n"
"esorex kmo_copy --x=3 --y=2 --z=1 --xsize=2 --ysize=3 copy.sof\n"
"\n"
"extract vector just of IFU 4:\n"
"esorex kmo_copy --x=3 --y=2 --z=1 --ysize=3 --ifu=4 copy.sof\n"
"\n"
"extract whole IFU 4:\n"
"esorex kmo_copy --x=1 --y=1 --z=1 --xsize=<NAXIS1> --ysize=<NAXIS2> \n"
"                                           --zsize=<NAXIS3> --ifu=4 copy.sof\n"
"\n"
"extract scalar:\n"
"esorex kmo_copy --x=3 --y=2 --z=1 copy.sof\n"
"\n"
"with copy.sof:\n"
"F3I.fits    DATA\n"
"\n"
"-------------------------------------------------------------------------------\n"
"  Input files:\n"
"\n"
"   DO                    KMOS                                                  \n"
"   category              Type   Explanation                    Required #Frames\n"
"   --------              -----  -----------                    -------- -------\n"
"   <none or any>         F3I    Data cube                         Y        1   \n"
"   or                                                                          \n"
"   <none or any>         F2I    Image                                          \n"
"   or                                                                          \n"
"   <none or any>         F1I    Vector                                         \n"
"                                (All inputs with or                            \n"
"                                without noise frame)                           \n"
"\n"
"  Output files:\n"
"\n"
"   DO                    KMOS\n"
"   category              Type   Explanation\n"
"   --------              -----  -----------\n"
"   COPY                  F3I or Cropped input data\n"
"                         F2I or                   \n"
"                         F1I                      \n"
"-------------------------------------------------------------------------------\n"
"\n";

/**
 * @defgroup kmo_copy kmo_copy Copy section of 1D, 2D, 3D KMOS 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_copy",
                        "Copy a section of a cube to another cube, "
                            "image or spectrum",
                        kmo_copy_description,
                        "Alex Agudo Berbel",
                        "usd-help@eso.org",
                        kmos_get_license(),
                        kmo_copy_create,
                        kmo_copy_exec,
                        kmo_copy_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_copy_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 */

    /* --ifu */
    p = cpl_parameter_new_value("kmos.kmo_copy.ifu",
                                CPL_TYPE_INT,
                                "Specific IFU to process",
                                "kmos.kmo_copy",
                                -1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ifu");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --autocrop */
    p = cpl_parameter_new_value("kmos.kmo_copy.autocrop",
                                CPL_TYPE_BOOL,
                                "Crop automatically NaN values at borders",
                                "kmos.kmo_copy",
                                FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "autocrop");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --x, --y, --z */
    p = cpl_parameter_new_value("kmos.kmo_copy.x",
                                CPL_TYPE_INT,
                                "Start value in first dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "x");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    p = cpl_parameter_new_value("kmos.kmo_copy.y",
                                CPL_TYPE_INT,
                                "Start value in second dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "y");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    p = cpl_parameter_new_value("kmos.kmo_copy.z",
                                CPL_TYPE_INT,
                                "Start value in third dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "z");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --xsize, --ysize, --zsize */
    p = cpl_parameter_new_value("kmos.kmo_copy.xsize",
                                CPL_TYPE_INT,
                                "Length in first dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xsize");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    p = cpl_parameter_new_value("kmos.kmo_copy.ysize",
                                CPL_TYPE_INT,
                                "Length in second dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ysize");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    p = cpl_parameter_new_value("kmos.kmo_copy.zsize",
                                CPL_TYPE_INT,
                                "Length in third dimension (pixels).",
                                "kmos.kmo_copy",
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "zsize");
    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_copy_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_copy(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_copy_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 ;
}

/**
  @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_copy(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    int                 ret_val         = 0,
                        i               = 0,
                        x1              = 0,
                        y1              = 0,
                        z1              = 0,
                        x2              = 0,
                        y2              = 0,
                        z2              = 0,
                        xsize           = 0,
                        ysize           = 0,
                        zsize           = 0,
                        ifu             = 0,
                        autocrop        = 0,
                        nx              = 0,
                        ny              = 0,
                        nz              = 0,
                        allNaN          = 0,
                        ix              = 0,
                        iy              = 0,
                        iz              = 0;
    double              crpix1          = 0.,
                        crpix2          = 0.,
                        crpix3          = 0.,
                        crpix1_new      = 0.,
                        crpix2_new      = 0.,
                        crval1_new      = 0.,
                        crval2_new      = 0.,
                        crval3          = 0.,
                        cdelt3          = 0.,
                        xshift          = 0.,
                        yshift          = 0.,
                        crpix3_new      = 0.;
    float               *pimg           = NULL;
    cpl_wcs             *wcs            = NULL;
    cpl_matrix          *phys           = NULL,
                        *world          = NULL;
    cpl_array           *status         = NULL;
    cpl_propertylist    *sub_header     = NULL;
    cpl_imagelist       *imglist        = NULL,
                        *res_imglist    = NULL;
    cpl_image           *img            = NULL,
                        *res_img        = NULL;
    kmclipm_vector      *vec            = NULL,
                        *res_vec        = NULL;
    cpl_frame           *frame          = NULL;
    main_fits_desc      desc;

    KMO_TRY
    {
        kmo_init_fits_desc(&desc);

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

        KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
                       CPL_ERROR_NULL_INPUT,
                       "A fits-file must be provided!");

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

        desc = kmo_identify_fits_header(
                    cpl_frame_get_filename(frame));
        KMO_TRY_CHECK_ERROR_STATE_MSG("Provided fits file doesn't seem to be "
                                      "in KMOS-format!");

        KMO_TRY_ASSURE((desc.fits_type == f1i_fits) ||
                       (desc.fits_type == f2i_fits) ||
                       (desc.fits_type == f3i_fits),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Input data hasn't correct data type "
                       "(KMOSTYPE must be F1I, F2I or F3I)!");

        KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset) == 1,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Cannot identify RAW and CALIB frames!");

        /* get & check ifu-parameter (optional)*/
        cpl_msg_info("", "--- Parameter setup for kmo_copy ----------");

        ifu = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.ifu");
        KMO_TRY_ASSURE((ifu == -1) ||
                       ((ifu >= 0)/* && (ifu <= desc.nr_ext)*/),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "ifu is out of range!");
        if (ifu != -1) {
            ix = FALSE;
            for (i = 0; i < desc.nr_ext; i++) {
                if (ifu == desc.sub_desc[i].device_nr) {
                    ix = TRUE;
                }
            }
            KMO_TRY_ASSURE(ix == TRUE,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "ifu #%d doesn't exist in this frame!", ifu);
        }

        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.ifu"));

        autocrop = kmo_dfs_get_parameter_bool(parlist, "kmos.kmo_copy.autocrop");
        KMO_TRY_ASSURE((autocrop == TRUE) || (autocrop == FALSE),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "autocrop must be TZRUE or FALSE!");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.autocrop"));

        if (!autocrop) {
            /* get & check x-, y-, z-parameters (mandatory)*/
            x1 = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.x");

            KMO_TRY_ASSURE(x1 > 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "x is smaller than 1!");

            KMO_TRY_ASSURE(x1 <= desc.naxis1,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "x is larger than corresponding dimension of "
                           "input data cube!");
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.x"));

            if ((desc.fits_type == f2i_fits) || (desc.fits_type == f3i_fits)) {
                y1 = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.y");

                KMO_TRY_ASSURE(y1 > 0,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "y is smaller than 1!");

                KMO_TRY_ASSURE(y1 <= desc.naxis2,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "y is larger than corresponding dimension of "
                               "input data cube!");
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.y"));

                if (desc.fits_type == f3i_fits) {
                    z1 = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.z");

                    KMO_TRY_ASSURE(z1 > 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "z is smaller than 1!");

                    KMO_TRY_ASSURE(z1 <= desc.naxis3,
                                   CPL_ERROR_ILLEGAL_INPUT,
                                   "z is larger than corresponding dimension of "
                                   "input data cube!");
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.z"));
                }
            }

            /* get & check x2-, y2-, z2-parameters (optional) */
            xsize = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.xsize");

            KMO_TRY_ASSURE(xsize > 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "xsize is smaller than 1!");
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.xsize"));

            x2 = x1 - 1 + xsize;

            KMO_TRY_ASSURE(x2 > 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "End value in 1st dimension smaller than 0!");

            KMO_TRY_ASSURE(x2 <= desc.naxis1,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "xsize is too large (xsize <= %d)",
                           desc.naxis1 - x1 + 1);

            if ((desc.fits_type == f2i_fits) || (desc.fits_type == f3i_fits)) {
                ysize = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_copy.ysize");

                KMO_TRY_ASSURE(ysize > 0,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "ysize is smaller than 1!");
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.ysize"));

                y2 = y1 - 1 + ysize;

                KMO_TRY_ASSURE(y2 > 0,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "End value in 2nd dimension smaller than 0!");

                KMO_TRY_ASSURE(y2 <= desc.naxis2,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "ysize is too large (ysize <= %d)",
                               desc.naxis2 - y1 + 1);

                if (desc.fits_type == f3i_fits) {
                    zsize = kmo_dfs_get_parameter_int(parlist,
                                                      "kmos.kmo_copy.zsize");

                    KMO_TRY_ASSURE(zsize > 0,
                                   CPL_ERROR_ILLEGAL_INPUT,
                                   "zsize is smaller than 1!");

                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_print_parameter_help(parlist, "kmos.kmo_copy.zsize"));

                    z2 = z1 - 1 + zsize;

                    KMO_TRY_ASSURE(z2 > 0,
                                   CPL_ERROR_ILLEGAL_INPUT,
                                   "End value in 3rd dimension smaller than 0!");

                    KMO_TRY_ASSURE(z2 <= desc.naxis3,
                                   CPL_ERROR_ILLEGAL_INPUT,
                                   "zsize is too large (zsize <= %d)",
                                   desc.naxis3 - z1 + 1);
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();
        }

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

        /* --- load, update & save primary header --- */
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_save_main_header(frameset, COPY, "", frame, NULL, parlist, cpl_func));

        //
        // --- copy data ----
        //
        for (i = 0; i < desc.nr_ext; i++) {
            if ((ifu == desc.sub_desc[i].device_nr) ||
                (ifu == -1)) {

                KMO_TRY_EXIT_IF_NULL(
                    sub_header = kmo_dfs_load_sub_header(frameset,
                                                    "0",
                                                    desc.sub_desc[i].device_nr,
                                                    desc.sub_desc[i].is_noise));

                //
                // --- IFU is valid -> copy header and data ---
                //
                if (desc.sub_desc[i].valid_data == TRUE) {

                    switch (desc.fits_type) {
                        case f1i_fits:
                            KMO_TRY_EXIT_IF_NULL(
                                vec = kmo_dfs_load_vector(frameset,
                                                "0",
                                                desc.sub_desc[i].device_nr,
                                                desc.sub_desc[i].is_noise));

                            if (autocrop && !desc.sub_desc[i].is_noise) {
                                x1 = 1;
                                x2 = kmclipm_vector_get_size(vec);
                                while (kmclipm_vector_is_rejected(vec, x1-1)) {
                                    x1++;
                                }
                                while (kmclipm_vector_is_rejected(vec, x2-1)) {
                                    x2--;
                                }

                                if (x1 > x2) {
                                    /* invalid IFU, just save sub_header */
                                    KMO_TRY_EXIT_IF_ERROR(
                                        kmo_dfs_save_sub_header(COPY, "",
                                                                sub_header));
                                    continue; // with next IFU
                                }
                            }

                            // extract scalar (F1I)
                            if ((x1 == x2) || (x2 == INT_MIN))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmclipm_vector_new(1));

                                KMO_TRY_EXIT_IF_ERROR(
                                    kmclipm_vector_set(res_vec, 0,
                                                 kmo_copy_scalar_F1I(vec, x1)));
                            }
                            // extract x-vector (F1I)
                            else if ((x2!= INT_MIN) && (x1 != x2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F1I(vec, x1, x2));
                            }

                            kmclipm_vector_delete(vec); vec = NULL;

                            break;
                        case f2i_fits:
                            KMO_TRY_EXIT_IF_NULL(
                                img = kmo_dfs_load_image(frameset,
                                                "0",
                                                desc.sub_desc[i].device_nr,
                                                desc.sub_desc[i].is_noise, FALSE, NULL));

                            if (autocrop && !desc.sub_desc[i].is_noise) {
                                nx = cpl_image_get_size_x(img);
                                ny = cpl_image_get_size_y(img);
                                x1 = 1;
                                x2 = nx;
                                y1 = 1;
                                y2 = ny;

                                KMO_TRY_EXIT_IF_NULL(
                                    pimg = cpl_image_get_data_float(img));

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iy = 0; iy < ny; iy++) {
                                        if(!isnan(pimg[x1-1+iy*nx])) {
                                            allNaN = FALSE;
                                            break;
                                        }
                                    }
                                    if (allNaN) {
                                        x1++;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iy = 0; iy < ny; iy++) {
                                        if(!isnan(pimg[x2-1+iy*nx])) {
                                            allNaN = FALSE;
                                            break;
                                        }
                                    }
                                    if (allNaN) {
                                        x2--;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (ix = 0; ix < nx; ix++) {
                                        if(!isnan(pimg[ix+(y1-1)*nx])) {
                                            allNaN = FALSE;
                                            break;
                                        }
                                    }
                                    if (allNaN) {
                                        y1++;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (ix = 0; ix < nx; ix++) {
                                        if(!isnan(pimg[ix+(y2-1)*nx])) {
                                            allNaN = FALSE;
                                            break;
                                        }
                                    }
                                    if (allNaN) {
                                        y2--;
                                    }
                                }

                                if ((x1 > x2) || (y1 > y2)) {
                                    /* invalid IFU, just save sub_header */
                                    KMO_TRY_EXIT_IF_ERROR(
                                        kmo_dfs_save_sub_header(COPY, "",
                                                                sub_header));
                                    continue; // with next IFU
                                }
                            }

                            // extract scalar (F2I)
                            if (((x1 == x2) || (x2 == INT_MIN)) &&
                                ((y1 == y2) || (y2 == INT_MIN)))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmclipm_vector_new(1));

                                KMO_TRY_EXIT_IF_ERROR(
                                    kmclipm_vector_set(res_vec, 0,
                                             kmo_copy_scalar_F2I(img, x1, y1)));
                            }
                            // extract x-vector (F2I)
                            else if (((y1 == y2) || (y2 == INT_MIN)) &&
                                       (x2 != INT_MIN) &&
                                       (x1 != x2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F2I_x(img,
                                                                   x1, x2, y1));
                            }
                            // extract y-vector (F2I)
                            else if (((x1 == x2) || (x2 == INT_MIN)) &&
                                       (y2 != INT_MIN) &&
                                       (y1 != y2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F2I_y(img,
                                                                   x1, y1, y2));
                            }
                            // extract plane (F2I)
                            else if ((x2 != INT_MIN) && (x1 != x2) &&
                                       (y2 != INT_MIN) && (y1 != y2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_img = kmo_copy_image_F2I(img,
                                                               x1, x2, y1, y2));
                            }

                            cpl_image_delete(img); img = NULL;

                            break;
                        case f3i_fits:
                            KMO_TRY_EXIT_IF_NULL(
                                imglist = kmo_dfs_load_cube(frameset,
                                                "0",
                                                desc.sub_desc[i].device_nr,
                                                desc.sub_desc[i].is_noise));

                            if (autocrop && !desc.sub_desc[i].is_noise) {
                                img = cpl_imagelist_get(imglist, 0);
                                nx = cpl_image_get_size_x(img);
                                ny = cpl_image_get_size_y(img);
                                nz = cpl_imagelist_get_size(imglist);
                                x1 = 1;
                                x2 = nx;
                                y1 = 1;
                                y2 = ny;
                                z1 = 1;
                                z2 = nz;

                                while (kmo_image_get_rejected(img) == nx*ny) {
                                    z1++;
                                    img = cpl_imagelist_get(imglist, z1-1);
                                }

                                img = cpl_imagelist_get(imglist, z2-1);
                                while (kmo_image_get_rejected(img) == nx*ny) {
                                    z2--;
                                    img = cpl_imagelist_get(imglist, z2-1);
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iz = z1-1; iz < z2; iz++) {
                                        img = cpl_imagelist_get(imglist, iz);
                                        KMO_TRY_EXIT_IF_NULL(
                                            pimg = cpl_image_get_data_float(img));
                                        if (allNaN == FALSE) {
                                            break;
                                        }
                                        for (iy = 0; iy < ny; iy++) {
                                            if(!isnan(pimg[x1-1+iy*nx])) {
                                                allNaN = FALSE;
                                                break;
                                            }
                                        }
                                    }
                                    if (allNaN) {
                                        x1++;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iz = z1-1; iz < z2; iz++) {
                                        img = cpl_imagelist_get(imglist, iz);
                                        KMO_TRY_EXIT_IF_NULL(
                                            pimg = cpl_image_get_data_float(img));
                                        if (allNaN == FALSE) {
                                            break;
                                        }
                                        for (iy = 0; iy < ny; iy++) {
                                            if(!isnan(pimg[x2-1+iy*nx])) {
                                                allNaN = FALSE;
                                                break;
                                            }
                                        }
                                    }
                                    if (allNaN) {
                                        x2--;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iz = z1-1; iz < z2; iz++) {
                                        img = cpl_imagelist_get(imglist, iz);
                                        KMO_TRY_EXIT_IF_NULL(
                                            pimg = cpl_image_get_data_float(img));
                                        if (allNaN == FALSE) {
                                            break;
                                        }
                                        for (ix = 0; ix < nx; ix++) {
                                            if(!isnan(pimg[ix+(y1-1)*nx])) {
                                                allNaN = FALSE;
                                                break;
                                            }
                                        }
                                    }
                                    if (allNaN) {
                                        y1++;
                                    }
                                }

                                allNaN = TRUE;
                                while (allNaN) {
                                    for (iz = z1-1; iz < z2; iz++) {
                                        img = cpl_imagelist_get(imglist, iz);
                                        KMO_TRY_EXIT_IF_NULL(
                                            pimg = cpl_image_get_data_float(img));
                                        if (allNaN == FALSE) {
                                            break;
                                        }
                                        for (ix = 0; ix < nx; ix++) {
                                            if(!isnan(pimg[ix+(y2-1)*nx])) {
                                                allNaN = FALSE;
                                                break;
                                            }
                                        }
                                    }
                                    if (allNaN) {
                                        y2--;
                                    }
                                }

                                img = NULL;

                                if ((x1 > x2) || (y1 > y2) || (z1 > z2)) {
                                    /* invalid IFU, just save sub_header */
                                    KMO_TRY_EXIT_IF_ERROR(
                                        kmo_dfs_save_sub_header(COPY, "",
                                                            sub_header));
                                    continue; // with next IFU
                                }
                            }

                            // extract scalar (F3I)
                            if (((x1 == x2) || (x2 == INT_MIN)) &&
                                ((y1 == y2) || (y2 == INT_MIN)) &&
                                ((z1 == z2) || (z2 == INT_MIN)))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmclipm_vector_new(1));

                                KMO_TRY_EXIT_IF_ERROR(
                                    kmclipm_vector_set(res_vec, 0,
                                     kmo_copy_scalar_F3I(imglist, x1, y1, z1)));
                            }
                            // extract x-vector (F3I)
                            else if ((x2 != INT_MIN) && (x1 != x2) &&
                                       ((y1 == y2) || (y2 == INT_MIN)) &&
                                       ((z1 == z2) || (z2 == INT_MIN)))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F3I_x(imglist,
                                                               x1, x2, y1, z1));
                            }
                            // extract y-vector (F3I)
                            else if (((x1 == x2) || (x2 == INT_MIN)) &&
                                       (y2!= INT_MIN) && (y1 != y2) &&
                                       ((z1 == z2) || (z2 == INT_MIN)))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F3I_y(imglist,
                                                               x1, y1, y2, z1));
                            }
                            // extract z-vector (F3I)
                            else if (((x1 == x2) || (x2 == INT_MIN)) &&
                                       ((y1 == y2) || (y2 == INT_MIN)) &&
                                       (z2 != INT_MIN) && (z1 != z2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_vec = kmo_copy_vector_F3I_z(imglist,
                                                               x1, y1, z1, z2));
                            }
                            // extract x-plane (F3I)
                            else if (((x1 == x2) || (x2 == INT_MIN)) &&
                                       (y2 != INT_MIN) && (y1 != y2) &&
                                       (z2 != INT_MIN) && (z1 != z2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_img = kmo_copy_image_F3I_x(imglist,
                                                           x1, y1, y2, z1, z2));
                            }
                            // extract y-plane (F3I)
                            else if ((x2 != INT_MIN) && (x1 != x2) &&
                                       ((y1 == y2) || (y2 == INT_MIN)) &&
                                       (z2 != INT_MIN) && (z1 != z2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_img = kmo_copy_image_F3I_y(imglist,
                                                           x1, x2, y1, z1, z2));
                            }
                            // extract z-plane (F3I)
                            else if ((x2 != INT_MIN) && (x1 != x2) &&
                                       (y2 != INT_MIN) && (y1 != y2) &&
                                       ((z1 == z2) || (z2 == INT_MIN)))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_img = kmo_copy_image_F3I_z(imglist,
                                                           x1, x2, y1, y2, z1));
                            }
                            // extract cube (F3I)
                            else if ((x2!= INT_MIN) && (x1 != x2) &&
                                       (y2!= INT_MIN) && (y1 != y2) &&
                                       (z2!= INT_MIN) && (z1 != z2))
                            {
                                KMO_TRY_EXIT_IF_NULL(
                                    res_imglist = kmo_copy_cube_F3I(imglist,
                                                       x1, x2, y1, y2, z1, z2));
                            }

                            cpl_imagelist_delete(imglist); imglist = NULL;

                            break;
                        default:
                            break;
                    }

                    //
                    // --- save and delete copied data, delete sub-header ---
                    //
                    if (res_vec != NULL) {
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(sub_header,
                                                           CRPIX1,
                                                           1,
                                             "[pix] Reference pixel in x"));
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(sub_header,
                                                           CRVAL1,
                                                           1,
                                          "[um] Wavelength at ref. pixel"));
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(sub_header,
                                                           CDELT1,
                                                           1,
                                               "[um] Spectral resolution"));
                        if (cpl_propertylist_has(sub_header, CUNIT1)) {
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_string(
                                        sub_header,
                                        CUNIT1,
                                        "",
                                        ""));
                        }

                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_string(
                                    sub_header,
                                    CTYPE1,
                                    "",
                                    "Coordinate system of x-axis"));

                        // going to save vector extracted from cube along lambda-axis
                        // (put dim3 keywords into dim1-keywords)
                        if ((desc.fits_type == f3i_fits) &&
                            (cpl_propertylist_has(sub_header, CRPIX3)) &&
                            (cpl_propertylist_has(sub_header, CRVAL3)) &&
                            ((x1 == x2) && (y1 == y2)))
                        {
                            crpix3 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX3);
                            crval3 = cpl_propertylist_get_double(sub_header,
                                                                 CRVAL3);
                            cdelt3 = cpl_propertylist_get_double(sub_header,
                                                                 CDELT3);
                            KMO_TRY_CHECK_ERROR_STATE();

                            // update WCS in z-direction, because starting point
                            // isn't 1 anymore
                            if (z1 != 1)
                            {
                                crpix3 = crpix3 - z1 + 1;
                            }

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX1,
                                                               crpix3,
                                                 "[pix] Reference pixel in x"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL1,
                                                               crval3,
                                              "[um] Wavelength at ref. pixel"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CDELT1,
                                                               cdelt3,
                                                   "[um] Spectral resolution"));
                            if (cpl_propertylist_has(sub_header, CUNIT3)) {
                                KMO_TRY_EXIT_IF_ERROR(
                                    kmclipm_update_property_string(
                                            sub_header,
                                            CUNIT1,
                                            cpl_propertylist_get_string(sub_header,
                                                                        CUNIT3),
                                            cpl_propertylist_get_comment(sub_header,
                                                                         CUNIT3)));
                            }

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_string(
                                        sub_header,
                                        CTYPE1,
                                        cpl_propertylist_get_string(
                                                                sub_header,
                                                                CTYPE3),
                                        "Coordinate system of x-axis"));
                        }
                        if (cpl_propertylist_has(sub_header, CRPIX2))
                            cpl_propertylist_erase(sub_header, CRPIX2);
                        if (cpl_propertylist_has(sub_header, CRVAL2))
                            cpl_propertylist_erase(sub_header, CRVAL2);
                        if (cpl_propertylist_has(sub_header, CDELT2))
                            cpl_propertylist_erase(sub_header, CDELT2);
                        if (cpl_propertylist_has(sub_header, CTYPE2))
                            cpl_propertylist_erase(sub_header, CTYPE2);
                        if (cpl_propertylist_has(sub_header, CUNIT2))
                            cpl_propertylist_erase(sub_header, CUNIT2);
                        if (cpl_propertylist_has(sub_header, CRPIX3))
                            cpl_propertylist_erase(sub_header, CRPIX3);
                        if (cpl_propertylist_has(sub_header, CRVAL3))
                            cpl_propertylist_erase(sub_header, CRVAL3);
                        if (cpl_propertylist_has(sub_header, CDELT3))
                            cpl_propertylist_erase(sub_header, CDELT3);
                        if (cpl_propertylist_has(sub_header, CTYPE3))
                            cpl_propertylist_erase(sub_header, CTYPE3);
                        if (cpl_propertylist_has(sub_header, CUNIT3))
                            cpl_propertylist_erase(sub_header, CUNIT3);
                        if (cpl_propertylist_has(sub_header, CD1_1))
                            cpl_propertylist_erase(sub_header, CD1_1);
                        if (cpl_propertylist_has(sub_header, CD1_2))
                            cpl_propertylist_erase(sub_header, CD1_2);
                        if (cpl_propertylist_has(sub_header, CD1_3))
                            cpl_propertylist_erase(sub_header, CD1_3);
                        if (cpl_propertylist_has(sub_header, CD2_1))
                            cpl_propertylist_erase(sub_header, CD2_1);
                        if (cpl_propertylist_has(sub_header, CD2_2))
                            cpl_propertylist_erase(sub_header, CD2_2);
                        if (cpl_propertylist_has(sub_header, CD2_3))
                            cpl_propertylist_erase(sub_header, CD2_3);
                        if (cpl_propertylist_has(sub_header, CD3_1))
                            cpl_propertylist_erase(sub_header, CD3_1);
                        if (cpl_propertylist_has(sub_header, CD3_2))
                            cpl_propertylist_erase(sub_header, CD3_2);
                        if (cpl_propertylist_has(sub_header, CD3_3))
                            cpl_propertylist_erase(sub_header, CD3_3);
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_dfs_save_vector(res_vec, COPY, "",
                                                sub_header, 0./0.));

                        kmclipm_vector_delete(res_vec); res_vec = NULL;
                    } else if (res_img != NULL) {
                        // going to save image extracted from cube along lambda-axis
                        // (put dim3 keywords into dim1-keywords)
                        if ((desc.fits_type == f3i_fits) &&
                            (cpl_propertylist_has(sub_header, CRPIX3)) &&
                            (cpl_propertylist_has(sub_header, CRVAL3)) &&
                            ((x1 == x2) || (y1 == y2)))
                        {
                            crpix3 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX3);
                            crval3 = cpl_propertylist_get_double(sub_header,
                                                                 CRVAL3);
                            cdelt3 = cpl_propertylist_get_double(sub_header,
                                                                 CDELT3);
                            KMO_TRY_CHECK_ERROR_STATE();

                            // update WCS in z-direction, because starting point
                            // isn't 1 anymore
                            if (z1 != 1)
                            {
                                crpix3 = crpix3 - z1 + 1;
                            }

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX1,
                                                               crpix3,
                                                 "[pix] Reference pixel in x"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX2,
                                                               1,
                                                 "[pix] Reference pixel in y"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL1,
                                                               crval3,
                                              "[um] Wavelength at ref. pixel"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL2,
                                                               1,
                                                               "[pix]"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CDELT1,
                                                               cdelt3,
                                                   "[um] Spectral resolution"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CDELT2,
                                                               1,
                                                               "[pix]"));
                            if (cpl_propertylist_has(sub_header, CUNIT3)) {
                                KMO_TRY_EXIT_IF_ERROR(
                                    kmclipm_update_property_string(
                                            sub_header,
                                            CUNIT1,
                                            cpl_propertylist_get_string(sub_header,
                                                                        CUNIT3),
                                            cpl_propertylist_get_comment(sub_header,
                                                                         CUNIT3)));
                            }

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_string(
                                        sub_header,
                                        CTYPE1,
                                        cpl_propertylist_get_string(
                                                                sub_header,
                                                                CTYPE3),
                                        "Coordinate system of x-axis"));

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_string(
                                            sub_header,
                                            CTYPE2,
                                            "",
                                            "Coordinate system of y-axis"));
                            if (cpl_propertylist_has(sub_header, CD1_1))
                                cpl_propertylist_erase(sub_header, CD1_1);
                            if (cpl_propertylist_has(sub_header, CD1_2))
                                cpl_propertylist_erase(sub_header, CD1_2);
                            if (cpl_propertylist_has(sub_header, CD2_1))
                                cpl_propertylist_erase(sub_header, CD2_1);
                            if (cpl_propertylist_has(sub_header, CD2_2))
                                cpl_propertylist_erase(sub_header, CD2_2);
                        }

                        // erase any still existing 3rd-dimension keywords
                        if (cpl_propertylist_has(sub_header, CRPIX3))
                            cpl_propertylist_erase(sub_header, CRPIX3);
                        if (cpl_propertylist_has(sub_header, CRVAL3))
                            cpl_propertylist_erase(sub_header, CRVAL3);
                        if (cpl_propertylist_has(sub_header, CDELT3))
                            cpl_propertylist_erase(sub_header, CDELT3);
                        if (cpl_propertylist_has(sub_header, CTYPE3))
                            cpl_propertylist_erase(sub_header, CTYPE3);
                        if (cpl_propertylist_has(sub_header, CUNIT3))
                            cpl_propertylist_erase(sub_header, CUNIT3);
                        if (cpl_propertylist_has(sub_header, CD1_3))
                            cpl_propertylist_erase(sub_header, CD1_3);
                        if (cpl_propertylist_has(sub_header, CD2_3))
                            cpl_propertylist_erase(sub_header, CD2_3);
                        if (cpl_propertylist_has(sub_header, CD3_1))
                            cpl_propertylist_erase(sub_header, CD3_1);
                        if (cpl_propertylist_has(sub_header, CD3_2))
                            cpl_propertylist_erase(sub_header, CD3_2);
                        if (cpl_propertylist_has(sub_header, CD3_3))
                            cpl_propertylist_erase(sub_header, CD3_3);


                        // update WCS in x- and y-direction because it got smaller
                        if ((desc.fits_type == f3i_fits) &&
                            (desc.fits_type == f2i_fits) &&
                            (cpl_propertylist_has(sub_header, CRPIX1)) &&
                            (cpl_propertylist_has(sub_header, CRPIX2)) &&
                            ((x1 != 1) || (y1 != 1)))
                        {
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_int(sub_header,
                                                               NAXIS,
                                                               2,
                                                               ""));
                            cpl_propertylist_erase(sub_header, NAXIS3);

                            crpix1 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX1);
                            crpix2 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX2);
                            KMO_TRY_CHECK_ERROR_STATE();
                            crpix3 = 1;
                            KMO_TRY_CHECK_ERROR_STATE();

                            xshift = x1 - 1;
                            yshift = y1 - 1;

                            crpix1_new = crpix1 - xshift;
                            crpix2_new = crpix2 - yshift;

                            phys = cpl_matrix_new (2, 2);
                            cpl_matrix_set(phys, 0, 0, crpix1);
                            cpl_matrix_set(phys, 0, 1, crpix2);
                            cpl_matrix_set(phys, 1, 0, crpix1_new);
                            cpl_matrix_set(phys, 1, 1, crpix2_new);

                            KMO_TRY_EXIT_IF_NULL(
                                wcs = cpl_wcs_new_from_propertylist(sub_header));

                            KMO_TRY_EXIT_IF_ERROR(
                                cpl_wcs_convert(wcs, phys, &world, &status,
                                                CPL_WCS_PHYS2WORLD));

                            crval1_new = cpl_matrix_get(world, 1, 0);
                            crval2_new = cpl_matrix_get(world, 1, 1);
                            crpix1_new = crpix1-2*xshift;
                            crpix2_new = crpix2-2*yshift;

                            // update WCS
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX1,
                                                               crpix1_new,
                                                 "[pix] Reference pixel in x"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX2,
                                                               crpix2_new,
                                                 "[pix] Reference pixel in y"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL1,
                                                               crval1_new,
                                                     "[deg] RA at ref. pixel"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL2,
                                                               crval2_new,
                                                    "[deg] DEC at ref. pixel"));

                            cpl_matrix_delete(phys); phys = NULL;
                            cpl_matrix_delete(world); world = NULL;
                            cpl_array_delete(status); status = NULL;
                            cpl_wcs_delete(wcs); wcs = NULL;
                        }

                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_dfs_save_image(res_img, COPY, "", sub_header, 0./0.));
                        cpl_image_delete(res_img); res_img = NULL;
                    } else if (res_imglist != NULL) {
                        // update WCS in x- and y-direction because it got smaller
                        if ((desc.fits_type == f3i_fits) &&
                            (cpl_propertylist_has(sub_header, CRPIX1)) &&
                            (cpl_propertylist_has(sub_header, CRPIX2)) &&
                            ((x1 != 1) || (y1 != 1)))
                        {
                            crpix1 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX1);
                            crpix2 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX2);
                            crpix3 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX3);
                            KMO_TRY_CHECK_ERROR_STATE();

                            xshift = x1 - 1;
                            yshift = y1 - 1;

                            crpix1_new = crpix1 - xshift;
                            crpix2_new = crpix2 - yshift;

                            phys = cpl_matrix_new (2, 3);
                            cpl_matrix_set(phys, 0, 0, crpix1);
                            cpl_matrix_set(phys, 0, 1, crpix2);
                            cpl_matrix_set(phys, 0, 2, crpix3);
                            cpl_matrix_set(phys, 1, 0, crpix1_new);
                            cpl_matrix_set(phys, 1, 1, crpix2_new);
                            cpl_matrix_set(phys, 1, 2, crpix3);

                            KMO_TRY_EXIT_IF_NULL(
                                wcs = cpl_wcs_new_from_propertylist(sub_header));

                            KMO_TRY_EXIT_IF_ERROR(
                                cpl_wcs_convert(wcs, phys, &world, &status,
                                                CPL_WCS_PHYS2WORLD));

                            crval1_new = cpl_matrix_get(world, 1, 0);
                            crval2_new = cpl_matrix_get(world, 1, 1);
                            crpix1_new = crpix1-2*xshift;
                            crpix2_new = crpix2-2*yshift;

                            // update WCS
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX1,
                                                               crpix1_new,
                                                 "[pix] Reference pixel in x"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX2,
                                                               crpix2_new,
                                                 "[pix] Reference pixel in y"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL1,
                                                               crval1_new,
                                                     "[deg] RA at ref. pixel"));
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRVAL2,
                                                               crval2_new,
                                                    "[deg] DEC at ref. pixel"));

                            cpl_matrix_delete(phys); phys = NULL;
                            cpl_matrix_delete(world); world = NULL;
                            cpl_array_delete(status); status = NULL;
                            cpl_wcs_delete(wcs); wcs = NULL;
                        }

                        // update WCS in z-direction, because starting point
                        // isn't 1 anymore
                        if ((cpl_propertylist_has(sub_header, CRPIX3)) &&
                            (z1 != 1))
                        {
                            crpix3 = cpl_propertylist_get_double(sub_header,
                                                                 CRPIX3);
                            KMO_TRY_CHECK_ERROR_STATE();

                            crpix3_new = crpix3 - z1 + 1;
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header,
                                                               CRPIX3,
                                                               crpix3_new,
                                                 "[pix] Reference pixel in z"));
                        }
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_dfs_save_cube(res_imglist, COPY, "",
                                              sub_header, 0./0.));

                        cpl_imagelist_delete(res_imglist); res_imglist = NULL;
                    }
                /* --- IFU is invalid --> copy only header --- */
                } else {
                    /* invalid IFU, just save sub_header */
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_sub_header(COPY, "", sub_header));
                }

                cpl_propertylist_delete(sub_header); sub_header = NULL;
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();

        ret_val = -1;
    }

    cpl_propertylist_delete(sub_header); sub_header = NULL;
    kmo_free_fits_desc(&desc);
    kmclipm_vector_delete(vec); vec = NULL;
    kmclipm_vector_delete(res_vec); res_vec = NULL;
    cpl_image_delete(img); img = NULL;
    cpl_image_delete(res_img); res_img = NULL;
    cpl_imagelist_delete(imglist); imglist = NULL;
    cpl_imagelist_delete(res_imglist); res_imglist = NULL;

    return ret_val;
}

/**@}*/
