/* $Id: kmo_priv_copy.c,v 1.2 2012-12-07 12:58:44 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: 2012-12-07 12:58:44 $
 * $Revision: 1.2 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include <string.h>

#include <cpl.h>

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

/*----------------------------------------------------------------------------*/
/**
    @defgroup kmos_priv_copy     Helper functions for recipe kmo_copy.

    @{
 */
/*----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 *                              extract scalar
 *----------------------------------------------------------------------------*/
/**
    @brief
        Copies a scalar from a vector.

    @param data         The input vector.
    @param x            The position.

    @return             The copied value.
*/
double kmo_copy_scalar_F1I(kmclipm_vector *data,
                          int x)
{
    double      ret_val         = -DBL_MAX;
    int         rej             = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_vector_get_size(data->data) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of vector! x = %d", x);

        ret_val = kmclipm_vector_get(data, x - 1, &rej);
        KMO_TRY_CHECK_ERROR_STATE();
        if (rej == 1) {
            ret_val = 0./0.;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = 0./0.;
    }

    return ret_val;
}

/**
    @brief
        Copies a scalar from an image.

    @param data         The input image.
    @param x            The position in the 1st dimension.
    @param y            The position in the 2nd dimension.

    @return             The copied value.
*/
float kmo_copy_scalar_F2I(cpl_image *data,
                          int x,
                          int y)
{
    float           ret_val         = -FLT_MAX;
    int             rej             = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_image_get_size_x(data) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of image! x = %d", x);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y) &&
                       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of image! y = %d", y);

        ret_val = cpl_image_get(data, x, y, &rej);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -FLT_MAX;
    }

    return ret_val;
}

/**
    @brief
        Copies a scalar from a cube.

    @param data         The input cube.
    @param x            The position in the 1st dimension.
    @param y            The position in the 2nd dimension.
    @param z            The position in the 3rd dimension.

    @return             The copied value.
*/
float kmo_copy_scalar_F3I(cpl_imagelist *data,
                          int x,
                          int y,
                          int z)
{
    float           ret_val         = -FLT_MAX;
    cpl_image       *img            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z) &&
                       (z > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z < 1 or z > size of cube! z = %d", z);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get(data, z - 1));

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of cube! x = %d", x);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y) &&
                       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of cube! y = %d", y);

        ret_val = kmo_copy_scalar_F2I(img, x, y);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -FLT_MAX;
    }

    return ret_val;
}

/*-----------------------------------------------------------------------------
 *                              extract vector
 *----------------------------------------------------------------------------*/
/**
    @brief
        Copies a vector from a vector.

    @param data         The input vector.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 2nd dimension.

    @return             The copied vector.
*/
kmclipm_vector*   kmo_copy_vector_F1I(kmclipm_vector *data,
                                      int x1, int x2)
{
    kmclipm_vector  *vec            = NULL;
    double          *pvecd          = NULL,
                    *pvecm          = NULL,
                    *pdatad         = NULL,
                    *pdatam         = NULL;
    int             i               = 0,
                    offset          = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_vector_get_size(data->data) >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of vector! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_vector_get_size(data->data) >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of vector! x2 = %d", x2);

        KMO_TRY_ASSURE(x1 <= x2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 > x2! x1 = %d, x2 = %d", x1, x2);

        if (x2 - x1 + 1 == cpl_vector_get_size(data->data)) {
            KMO_TRY_EXIT_IF_NULL(
                vec = kmclipm_vector_duplicate(data));
        } else {
            KMO_TRY_EXIT_IF_NULL(
                vec = kmclipm_vector_new(x2 - x1 + 1));

            KMO_TRY_EXIT_IF_NULL(
                pvecd = cpl_vector_get_data(vec->data));
            KMO_TRY_EXIT_IF_NULL(
                pvecm = cpl_vector_get_data(vec->mask));

            KMO_TRY_EXIT_IF_NULL(
                pdatad = cpl_vector_get_data(data->data));
            KMO_TRY_EXIT_IF_NULL(
                pdatam = cpl_vector_get_data(data->mask));

            for (i = x1 - 1; i < x2; i++) {
                pvecd[offset] = pdatad[i];
                pvecm[offset++] = pdatam[i];
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/**
    @brief
        Copies a vector from an image in direction of the 1st dimension.

    @param data         The input image.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 2nd dimension.
    @param y            The position in the 2nd dimension.

    @return             The copied vector.
*/
kmclipm_vector* kmo_copy_vector_F2I_x(cpl_image *data,
                                      int x1, int x2,
                                      int y)
{
    kmclipm_vector  *vec            = NULL;
    double          *pvecd          = NULL,
                    *pvecm          = NULL;
    int             i               = 0,
                    j               = 0,
                    rej             = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_image_get_size_x(data) >= x1) &&
        		       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of image! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_image_get_size_x(data) >= x2) &&
        		       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of image! x2 = %d", x2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y) &&
        		       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of image! y = %d", y);

        KMO_TRY_ASSURE(x1 <= x2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 > x2! x1 = %d, x2 = %d", x1, x2);

        KMO_TRY_EXIT_IF_NULL(
            vec = kmclipm_vector_new(x2 - x1 + 1));
        KMO_TRY_EXIT_IF_NULL(
            pvecd = cpl_vector_get_data(vec->data));
        KMO_TRY_EXIT_IF_NULL(
            pvecm = cpl_vector_get_data(vec->mask));

        for (i = x1 - 1; i < x2; i++) {
            double tmp = cpl_image_get(data, i+1, y, &rej);
            if (rej == 1) {
                // value in image is rejected, reject it in vector
                pvecm[j++] = 0;
            } else {
                pvecd[j++] = tmp;
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/**
    @brief
        Copies a vector from an image in direction of the 2nd dimension.

    @param data         The input image.
    @param x            The position in the 1st dimension.
    @param y1           The 1st position in the 2nd dimension.
    @param y2           The 2nd position in the 2nd dimension.

    @return             The copied vector.
*/
kmclipm_vector*   kmo_copy_vector_F2I_y(cpl_image *data,
                                        int x,
                                        int y1, int y2)
{
    kmclipm_vector  *vec            = NULL;
    double          *pvecd          = NULL,
                    *pvecm          = NULL;
    int             i               = 0,
                    j               = 0,
                    rej             = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y1) &&
        		       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of image! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y2) &&
        		       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of image! y2 = %d", y2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= x) &&
        		       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of image! x = %d", x);

        KMO_TRY_ASSURE(y1 <= y2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 > y2! y1 = %d, y2 = %d", y1, y2);

        KMO_TRY_EXIT_IF_NULL(
            vec = kmclipm_vector_new(y2 - y1 + 1));
        KMO_TRY_EXIT_IF_NULL(
            pvecd = cpl_vector_get_data(vec->data));
        KMO_TRY_EXIT_IF_NULL(
            pvecm = cpl_vector_get_data(vec->mask));

        for (i = y1 - 1; i < y2; i++) {
            double tmp = cpl_image_get(data, x, i+1, &rej);
            if (rej == 1) {
                // value in image is rejected, reject it in vector
                pvecm[j++] = 0;
            } else {
                pvecd[j++] = tmp;
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/**
    @brief
        Copies a vector from a cube in direction of the 1st dimension.

    @param data         The input cube.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 2nd dimension.
    @param y            The position in the 2nd dimension.
    @param z            The position in the 3rd dimension.

    @return             The copied vector.
*/
kmclipm_vector* kmo_copy_vector_F3I_x(cpl_imagelist *data,
                                      int x1, int x2,
                                      int y,
                                      int z)
{
    kmclipm_vector  *vec            = NULL;
    cpl_image       *img            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(x1 <= x2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 > x2! x1 = %d, x2 = %d", x1, x2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z) &&
                       (z > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z < 1 or z > size of cube! z = %d", z);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get(data, z - 1));

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of cube! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of cube! x2 = %d", x2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y) &&
                       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of cube! y = %d", y);

        KMO_TRY_EXIT_IF_NULL(
            vec = kmo_copy_vector_F2I_x(img, x1, x2, y));
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/**
    @brief
        Copies a vector from a cube in direction of the 2nd dimension.

    @param data         The input cube.
    @param x            The position in the 1st dimension.
    @param y1           The 1st position in the 2nd dimension.
    @param y2           The 2nd position in the 2nd dimension.
    @param z            The position in the 3rd dimension.

    @return             The copied vector.
*/
kmclipm_vector* kmo_copy_vector_F3I_y(cpl_imagelist *data,
                                      int x,
                                      int y1, int y2,
                                      int z)
{
    kmclipm_vector  *vec            = NULL;
    cpl_image       *img            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(y1 <= y2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 > y2! y1 = %d, y2 = %d", y1, y2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z) &&
                       (z > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z < 1 or z > size of cube! z = %d", z);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get(data, z - 1));

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y1) &&
                       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of cube! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y2) &&
                       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of cube! y2 = %d", y2);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of cube! x = %d", x);

        KMO_TRY_EXIT_IF_NULL(
            vec = kmo_copy_vector_F2I_y(img, x, y1, y2));
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/**
    @brief
        Copies a vector from a cube in direction of the 3rd dimension.

    @param data         The input cube.
    @param x            The position in the 1st dimension.
    @param y            The position in the 2nd dimension.
    @param z1           The 1st position in the 3rd dimension.
    @param z2           The 2nd position in the 3rd dimension.

    @return             The copied vector.
*/
kmclipm_vector* kmo_copy_vector_F3I_z(cpl_imagelist *data,
                                      int x,
                                      int y,
                                      int z1, int z2)
{
    kmclipm_vector  *vec            = NULL;
    cpl_image       *img            = NULL;
    double          *pvecd          = NULL,
                    *pvecm          = NULL,
                    tmp             = 0;
    int             i               = 0,
                    j               = 0,
                    rej             = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(z1 <= z2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 > z2! z1 = %d, z2 = %d", z1, z2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z1) &&
                       (z1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 < 1 or z1 > size of cube! z1 = %d", z1);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z2) &&
                       (z2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z2 < 1 or z2 > size of cube! z2 = %d", z2);

        KMO_TRY_EXIT_IF_NULL(
            vec = kmclipm_vector_new(z2 - z1 + 1));
        KMO_TRY_EXIT_IF_NULL(
            pvecd = cpl_vector_get_data(vec->data));
        KMO_TRY_EXIT_IF_NULL(
            pvecm = cpl_vector_get_data(vec->mask));

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get(data, 0));

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y) &&
                       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of cube! y = %d", y);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of cube! x = %d", x);

        for (i = z1 - 1; i < z2; i++) {
            KMO_TRY_EXIT_IF_NULL(
                img = cpl_imagelist_get(data, i));

            tmp = cpl_image_get(img, x, y, &rej);
            if (rej == 1) {
                pvecm[j++] = 0;
            } else {
                pvecd[j++] = tmp;
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        kmclipm_vector_delete(vec); vec = NULL;
    }

    return vec;
}

/*-----------------------------------------------------------------------------
 *                              extract image
 *----------------------------------------------------------------------------*/
/**
    @brief
        Copies an image from an image.

    @param data         The input image.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 1st dimension.
    @param x1           The 1st position in the 2nd dimension.
    @param x2           The 2nd position in the 2nd dimension.

    @return             The copied image.
*/
cpl_image*   kmo_copy_image_F2I(cpl_image *data,
                            int x1, int x2,
                            int y1, int y2)
{
    cpl_image       *img            = NULL;
    float           *d_data         = NULL,
                    *d_img          = NULL;
    int             i               = 0,
                    j               = 0,
                    offset          = 0,
                    size_x          = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        size_x = cpl_image_get_size_x(data);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y1) &&
                       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of image! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(data) >= y2) &&
                       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of image! y2 = %d", y2);

        KMO_TRY_ASSURE((size_x >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of image! x1 = %d", x1);

        KMO_TRY_ASSURE((size_x >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of image! x2 = %d", x2);

        KMO_TRY_ASSURE(x1 <= x2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 > x2! x1 = %d, x2 = %d", x1, x2);

        KMO_TRY_ASSURE(y1 <= y2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 > y2! y1 = %d, y2 = %d", y1, y2);

        if ((x2 - x1 + 1 == size_x) &&
            (y2 - y1 + 1 == cpl_image_get_size_y(data))) {
            img = cpl_image_duplicate(data);
        } else {
            KMO_TRY_EXIT_IF_NULL(
                img = cpl_image_new(x2 - x1 + 1, y2 - y1 + 1,
                                    CPL_TYPE_FLOAT));

            KMO_TRY_EXIT_IF_NULL(
                d_data = cpl_image_get_data_float(data));

            KMO_TRY_EXIT_IF_NULL(
                d_img = cpl_image_get_data_float(img));

            for (j = y1 - 1; j < y2; j++) {
                for (i = x1 - 1; i < x2; i++) {
                    d_img[offset++] = d_data[i + j * size_x];
                }
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
    }

    return img;
}

/**
    @brief
        Copies an image from a cube in the 1st dimension.

    @param data         The input cube.
    @param x            The position in the 1st dimension.
    @param y1           The 1st position in the 2nd dimension.
    @param y2           The 2nd position in the 2nd dimension.
    @param z1           The 1st position in the 3rd dimension.
    @param z2           The 2nd position in the 3rd dimension.

    @return             The copied image.
*/
cpl_image* kmo_copy_image_F3I_x(cpl_imagelist *data,
                                int x,
                                int y1, int y2,
                                int z1, int z2)
{
    kmclipm_vector  *vec            = NULL;
    int             i               = 0,
                    j               = 0,
                    g               = 0;
    double          *pvecd          = NULL,
                    *pvecm          = NULL;
    float           *img_data       = NULL;
    cpl_image       *img            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(z1 <= z2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 > z2! z1 = %d, z2 = %d", z1, z2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z1) &&
                       (z1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 < 1 or z1 > size of cube! z1 = %d", z1);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z2) &&
                       (z2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z2 < 1 or z2 > size of cube! z2 = %d", z2);

        img = cpl_imagelist_get(data, 0);
        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y1) &&
                       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of cube! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y2) &&
                       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of cube! y2 = %d", y2);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x) &&
                       (x > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x < 1 or x > size of cube! x = %d", x);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_image_new(z2 - z1 + 1, y2 - y1 + 1, CPL_TYPE_FLOAT));

        KMO_TRY_EXIT_IF_NULL(
            img_data = cpl_image_get_data_float(img));

        for (i = z1; i <= z2; i++) {
            KMO_TRY_EXIT_IF_NULL(
                vec = kmo_copy_vector_F3I_y(data, x, y1, y2, i));
            KMO_TRY_EXIT_IF_NULL(
                pvecd = cpl_vector_get_data(vec->data));
            KMO_TRY_EXIT_IF_NULL(
                pvecm = cpl_vector_get_data(vec->mask));

            g = 0;
            for (j = y1; j <= y2; j++) {
                if (pvecm[g] < 0.5) {
                    cpl_image_reject(img, i-z1+1, j-y1+1);
                    g++;
                } else {
                    img_data[(i - z1) + (j - y1) * (z2 - z1 + 1)] = pvecd[g++];
                }
            }

            kmclipm_vector_delete(vec); vec = NULL;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
    }

    return img;
}

/**
    @brief
        Copies an image from a cube in the 2nd dimension.

    @param data         The input cube.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 1st dimension.
    @param y            The position in the 2nd dimension.
    @param z1           The 1st position in the 3rd dimension.
    @param z2           The 2nd position in the 3rd dimension.

    @return             The copied image.
*/
cpl_image* kmo_copy_image_F3I_y(cpl_imagelist *data,
                                int x1, int x2,
                                int y,
                                int z1, int z2)
{
    kmclipm_vector  *vec            = NULL;
    int             i               = 0,
                    j               = 0,
                    g               = 0;
    double          *pvecd          = NULL,
                    *pvecm          = NULL;
    float           *img_data       = NULL;
    cpl_image       *img            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(z1 <= z2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 > z2! z1 = %d, z2 = %d", z1, z2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z1) &&
                       (z1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 < 1 or z1 > size of cube! z1 = %d", z1);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z2) &&
                       (z2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z2 < 1 or z2 > size of cube! z2 = %d", z2);

        img = cpl_imagelist_get(data, 0);
        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of cube! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of cube! x2 = %d", x2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y) &&
                       (y > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y < 1 or y > size of cube! y = %d", y);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_image_new(z2 - z1 + 1, x2 - x1 + 1, CPL_TYPE_FLOAT));

        KMO_TRY_EXIT_IF_NULL(
            img_data = cpl_image_get_data_float(img));

        for (i = z1; i <= z2; i++) {
            KMO_TRY_EXIT_IF_NULL(
                vec = kmo_copy_vector_F3I_x(data, x1, x2, y, i));
            KMO_TRY_EXIT_IF_NULL(
                pvecd = cpl_vector_get_data(vec->data));
            KMO_TRY_EXIT_IF_NULL(
                pvecm = cpl_vector_get_data(vec->mask));

            g = 0;
            for (j = x1; j <= x2; j++) {
                if (pvecm[g] < 0.5) {
                    cpl_image_reject(img, i-z1+1, j-x1+1);
                    g++;
                } else {
                    img_data[(i - z1) + (j - x1) * (z2 - z1 + 1)] = pvecd[g++];
                }
            }

            kmclipm_vector_delete(vec); vec = NULL;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
    }

    return img;
}

/**
    @brief
        Copies an image from a cube in the 3rd dimension.

    @param data         The input cube.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 1st dimension.
    @param y1           The 1st position in the 2nd dimension.
    @param y2           The 2nd position in the 2nd dimension.
    @param z            The position in the 3rd dimension.

    @return             The copied image.
*/
cpl_image* kmo_copy_image_F3I_z(cpl_imagelist *data,
                                int x1, int x2,
                                int y1, int y2,
                                int z)
{
    cpl_image       *img            = NULL,
                    *img2           = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z) &&
                       (z > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z < 1 or z > size of cube! z = %d", z);

        img = cpl_imagelist_get(data, 0);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of cube! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of cube! x2 = %d", x2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y1) &&
                       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of cube! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y2) &&
                       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of cube! y2 = %d", y2);

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get(data, z - 1));

        KMO_TRY_EXIT_IF_NULL(
            img2 = kmo_copy_image_F2I(img, x1, x2, y1, y2));
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
    }

    return img2;
}

/*-----------------------------------------------------------------------------
 *                              extract cube
 *----------------------------------------------------------------------------*/
/**
    @brief
        Copies a cube from a cube.

    @param data         The input cube.
    @param x1           The 1st position in the 1st dimension.
    @param x2           The 2nd position in the 1st dimension.
    @param y1           The 1st position in the 2nd dimension.
    @param y2           The 2nd position in the 2nd dimension.
    @param z1           The 1st position in the 3rd dimension.
    @param z2           The 2nd position in the 3rd dimension.

    @return             The copied cube.
*/
cpl_imagelist*   kmo_copy_cube_F3I(cpl_imagelist *data,
                            int x1, int x2,
                            int y1, int y2,
                            int z1, int z2)
{
    cpl_imagelist   *imglist        = NULL;
    cpl_image       *img            = NULL;
    int             i               = 0,
                    j               = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(z1 <= z2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 > z2! z1 = %d, z2 = %d", z1, z2);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z1) &&
                       (z1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z1 < 1 or z1 > size of cube! z1 = %d", z1);

        KMO_TRY_ASSURE((cpl_imagelist_get_size(data) >= z2) &&
                       (z2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "z2 < 1 or z2 > size of cube! z2 = %d", z2);

        img = cpl_imagelist_get(data, 0);

        KMO_TRY_ASSURE(x1 <= x2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 > x2! x1 = %d, x2 = %d", x1, x2);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x1) &&
                       (x1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x1 < 1 or x1 > size of cube! x1 = %d", x1);

        KMO_TRY_ASSURE((cpl_image_get_size_x(img) >= x2) &&
                       (x2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "x2 < 1 or x2 > size of cube! x2 = %d", x2);

        KMO_TRY_ASSURE(y1 <= y2,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 > y2! y1 = %d, y2 = %d", y1, y2);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y1) &&
                       (y1 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y1 < 1 or y1 > size of cube! y1 = %d", y1);

        KMO_TRY_ASSURE((cpl_image_get_size_y(img) >= y2) &&
                       (y2 > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y2 < 1 or y2 > size of cube! y2 = %d", y2);

        KMO_TRY_EXIT_IF_NULL(
            imglist = cpl_imagelist_new());

        for (i = z1 - 1; i < z2; i++) {
            cpl_imagelist_set(imglist,
                                kmo_copy_image_F2I(cpl_imagelist_get(data, i),
                                            x1, x2, y1, y2),
                                j++);
            KMO_TRY_CHECK_ERROR_STATE();
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
    }

    return imglist;
}

/** @} */
