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

/*
 * $Author: jtaylor $
 * $Date: 2013-05-13 16:03:43 $
 * $Revision: 1.13 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "visir_recipe.h"
#include <string.h>

/*-----------------------------------------------------------------------------
                                Defines
 -----------------------------------------------------------------------------*/

#define RECIPE_STRING   "visir_util_join"

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

static
cpl_error_code visir_util_join_one(cpl_frameset *,
                                   const irplib_framelist *,
                                   const irplib_framelist *,
                                   const irplib_framelist *,
                                   const irplib_framelist *,
                                   const irplib_framelist *,
                                   const irplib_framelist *,
                                   int, cpl_boolean, cpl_boolean, 
                                   cpl_boolean, cpl_boolean,
                                   cpl_boolean,
                                   const cpl_parameterlist *);

#ifdef VISIR_CHAIN
#define cpl_plugin_get_info visir_util_join_get_info
#endif
VISIR_RECIPE_DEFINE(visir_util_join, 0,
                    "Extend the final product with extensions containing its "
                    "error map, bad pixel map and weight or contribution map",
                    "The files listed in the Set Of Frames (SOF-file) "
                    "must be tagged:\n"
                    "VISIR-data.fits "          VISIR_UTIL_DATA "\n"
                    "\nOptionally, the SOF may also contain one or more of "
                    "the following:\n"
                    "VISIR-bad-pixel-map.fits " VISIR_CALIB_BPM "\n"
                    "VISIR-error.fits "         VISIR_UTIL_WEIGHT2ERROR_PROCATG
                      "\n"
                    "VISIR-contribution-map.fits " VISIR_IMG_CLIPPED_MAP_PROCATG
                      "\n"
                    "VISIR-weight-map.fits "    VISIR_UTIL_WEIGHT2ERROR
                      "\n"
                    "\nThe product(s) will have a FITS card\n"
                    "'HIERARCH " CPL_DFS_PRO_CATG "' with a value of:\n"
                    VISIR_UTIL_JOIN_PROCATG);

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_util_join   Join a product with its meta information
 */
/*----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    framelist   the frames list
  @param    parlist     the parameters list
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static int visir_util_join(cpl_frameset            * framelist,
                           const cpl_parameterlist * parlist)
{
    irplib_framelist * allframes = NULL;
    irplib_framelist * rawframes = NULL;
    irplib_framelist * bpmframes = NULL;
    irplib_framelist * errframes = NULL;
    irplib_framelist * conframes = NULL;
    irplib_framelist * wgtframes = NULL;
    irplib_framelist * qcframes = NULL;
    int                n;
    int                nbad = 0, nerr = 0, ncon = 0, nwgt = 0, nqc = 0;
    

    /* Identify the RAW and TAB frames in the input frameset */
    skip_if (visir_dfs_set_groups(framelist));
    FOR_EACH_FRAMESET(frm, framelist)
        cpl_frame_set_group(frm, CPL_FRAME_GROUP_RAW);

    /* Objects observation */
    allframes = irplib_framelist_cast(framelist);
    skip_if(allframes == NULL);

    rawframes =
        irplib_framelist_extract_regexp(allframes, "^("
                                        VISIR_UTIL_DATA
                                        "|" VISIR_IMG_PHOT_ONEBEAM_PROCATG
                                        "|" VISIR_IMG_PHOT_COMBINED_PROCATG
                                        "|COADDED_IMAGE"
                                        "|" VISIR_IMG_COADDED_IMG
                                        ")$",
                                        CPL_FALSE);

    skip_if(rawframes == NULL);

    n = irplib_framelist_get_size(rawframes);

    if (cpl_frameset_find_const(framelist, VISIR_CALIB_BPM)) {
        bpmframes = irplib_framelist_extract(allframes, VISIR_CALIB_BPM);
        skip_if (bpmframes == NULL);

        nbad = irplib_framelist_get_size(bpmframes);
        error_if(nbad != n && nbad != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "%d raw-frames <=> %d bpm frames", n, nbad);
    }

    if (cpl_frameset_find_const(framelist, VISIR_UTIL_ERROR_MAP_PROCATG)) {
        errframes = irplib_framelist_extract(allframes,
                                             VISIR_UTIL_ERROR_MAP_PROCATG);
        skip_if (errframes == NULL);

        nerr = irplib_framelist_get_size(errframes);
        error_if(nerr != n && nerr != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "%d raw-frames <=> %d error frames", n, nerr);

    }

    if (cpl_frameset_find_const(framelist, VISIR_IMG_CLIPPED_MAP_PROCATG)) {
        conframes = irplib_framelist_extract(allframes,
                                             VISIR_IMG_CLIPPED_MAP_PROCATG);
        skip_if (conframes == NULL);

        ncon = irplib_framelist_get_size(conframes);
        error_if(ncon % n != 0 && ncon != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "%d raw-frames <=> %d contribution frames", n, ncon);
    }

    if (cpl_frameset_find_const(framelist, VISIR_UTIL_WEIGHT_MAP_PROCATG)) {
        wgtframes = irplib_framelist_extract(allframes,
                                             VISIR_UTIL_WEIGHT_MAP_PROCATG);
        skip_if (wgtframes == NULL);

        nwgt = irplib_framelist_get_size(wgtframes);
        error_if(nwgt != n && nwgt != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "%d raw-frames <=> %d weight frames", n, nwgt);
    }

    if (cpl_frameset_find_const(framelist, VISIR_UTIL_QC_PROCATG)) {
        qcframes = irplib_framelist_extract(allframes, VISIR_UTIL_QC_PROCATG);
        skip_if (qcframes == NULL);

        nqc = irplib_framelist_get_size(qcframes);
        error_if(nqc != n && nqc != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "%d raw-frames <=> %d qc frames", n, nqc);
    }

    for (int i = 0; i < n; i++) {
        cpl_msg_info(cpl_func, "Joining frame %d/%d", 1+i, n);

        skip_if (visir_util_join_one(framelist, rawframes, bpmframes, errframes,
                                     conframes, wgtframes, qcframes, i, nbad == 1,
                                     nerr == 1, ncon == 1, nwgt == 1, nqc == 1,
                                     parlist));
    }

    end_skip;

    irplib_framelist_delete(allframes);
    irplib_framelist_delete(rawframes);
    irplib_framelist_delete(bpmframes);
    irplib_framelist_delete(errframes);
    irplib_framelist_delete(conframes);
    irplib_framelist_delete(wgtframes);
    irplib_framelist_delete(qcframes);

    return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Process the i'th frame with its meta information
  @param    framelist   The frameset to append products to
  @param    rawframes   The object frames list
  @param    bpmframes   The bpm frames list
  @param    errframes   The error frames list
  @param    conframes   The contribution frames list
  @param    wgtframes   The weight frames list
  @param    qcframes    The qc parameter frames list
  @param    i           The frame to process (0 for first)
  @param    bshared     CPL_TRUE iff the bpm is shared among all raw frames
  @param    eshared     CPL_TRUE iff the error is shared among all raw frames
  @param    cshared     CPL_TRUE iff the contrib is shared among all raw frames
  @param    wshared     CPL_TRUE iff the weight is shared among all raw frames
  @param    qcshared     CPL_TRUE iff the qc list is shared among all raw frames
  @param    parlist     The parameters list
  @return   CPL_ERROR_NONE iff OK, otherwise the relevant CPL error code.
 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_join_one(cpl_frameset * framelist,
                                   const irplib_framelist * rawframes,
                                   const irplib_framelist * bpmframes,
                                   const irplib_framelist * errframes,
                                   const irplib_framelist * conframes,
                                   const irplib_framelist * wgtframes,
                                   const irplib_framelist * qcframes,
                                   int i,
                                   cpl_boolean bshared, cpl_boolean eshared,
                                   cpl_boolean cshared, cpl_boolean wshared,
                                   cpl_boolean qcshared,
                                   const cpl_parameterlist * parlist)
{

    const int          n = irplib_framelist_get_size(rawframes);
    const cpl_frame  * frame;
    cpl_frameset     * products   = cpl_frameset_new();
    cpl_frameset     * usedframes = cpl_frameset_new();
    const char       * filename;
    cpl_propertylist * img_plist  = NULL;
    char             * proname    = NULL;
    cpl_image        * img        = NULL;
    cpl_image        * csum       = NULL;
    const char       * bunit      = NULL;
    char             * inpcatg    = NULL;

    cpl_image * bpm = NULL;
    cpl_image * err = NULL;
    cpl_image * wgt = NULL;
    cpl_image * con = NULL;

    cpl_propertylist * bpm_plist = NULL;
    cpl_propertylist * err_plist = NULL;
    cpl_propertylist * wgt_plist = NULL;
    cpl_propertylist * con_plist = NULL;
    cpl_propertylist * qc_plist  = NULL;

    const char * procatg = VISIR_UTIL_JOIN_PROCATG;

    bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate
                               (irplib_framelist_get_const(rawframes, i))));

    /* 1) The data frame */
    frame = irplib_framelist_get_const(rawframes, i);
    filename = cpl_frame_get_filename(frame);

    img_plist = cpl_propertylist_load(filename, 0);
    skip_if(img_plist == NULL);

    img = cpl_image_load(filename, CPL_TYPE_UNSPECIFIED, 0, 0);
    skip_if(img == NULL);

    if (qcframes) {
        const cpl_frame * qcframe =
            irplib_framelist_get_const(qcframes, qcshared ? 0 : i);
        filename = cpl_frame_get_filename(qcframe);
        qc_plist = cpl_propertylist_load_regexp(filename, 0,
                                "ESO QC |BUNIT|ESO DRS CATG", CPL_FALSE);
        skip_if (qc_plist == NULL);
        if (cpl_propertylist_has(qc_plist, "BUNIT")) {
            bunit = cpl_propertylist_get_string(qc_plist, "BUNIT");
        }
        if (cpl_propertylist_has(qc_plist, "ESO DRS CATG")) {
            inpcatg =
            cpl_strdup(cpl_propertylist_get_string(qc_plist, "ESO DRS CATG"));
            cpl_propertylist_erase(qc_plist, "ESO DRS CATG");
        }
    }

    if (bunit == NULL && cpl_propertylist_has(img_plist, "BUNIT")) {
        bunit = cpl_propertylist_get_string(img_plist, "BUNIT");
    }
    if (inpcatg == NULL && cpl_propertylist_has(img_plist, "ESO DRS CATG")) {
        inpcatg =
            cpl_strdup(cpl_propertylist_get_string(img_plist, "ESO DRS CATG"));
        cpl_propertylist_erase(img_plist, "ESO DRS CATG");
    }
    cpl_propertylist_append(img_plist, qc_plist);

    if (!strcmp(cpl_frame_get_tag(frame), VISIR_IMG_PHOT_ONEBEAM_PROCATG))
        procatg = VISIR_IMG_PHOT_ONEBEAM_PROCATG;
    else if (!strcmp(cpl_frame_get_tag(frame), VISIR_IMG_PHOT_COMBINED_PROCATG))
        procatg = VISIR_IMG_PHOT_COMBINED_PROCATG;
    else if (!strcmp(cpl_frame_get_tag(frame), VISIR_IMG_COADDED_IMG))
        procatg = VISIR_IMG_OBJ_COMBINED_PROCATG;
    else if (!strcmp(cpl_frame_get_tag(frame), "COADDED_IMAGE"))
        procatg = VISIR_IMG_OBJ_ONEBEAM_PROCATG;
  
    procatg = visir_dfs_output_catg(inpcatg, procatg);

    if (cpl_propertylist_has(img_plist, "ESO QC BEAMID")) {
        const char * b = cpl_propertylist_get_string(img_plist,
                                                     "ESO QC BEAMID");
        proname = cpl_sprintf(RECIPE_STRING "_b%s_%03d"
                              CPL_DFS_FITS, b, 1+i);
    }
    else
        proname = cpl_sprintf(RECIPE_STRING "_%03d"
                              CPL_DFS_FITS, 1+i);

    if (bpmframes != NULL) {
        /* 3) The bpm frame */
        frame = irplib_framelist_get_const(bpmframes, bshared ? 0 : i);

        filename = cpl_frame_get_filename(frame);

        bpm = cpl_image_load(filename, CPL_TYPE_INT, 0, 0);
        skip_if(bpm == NULL);

        bpm_plist = cpl_propertylist_load_regexp(filename, 0, " ESO QC | ESO PRO ",
                                                 0);
        skip_if(bpm_plist == NULL);
        cpl_propertylist_append_string(bpm_plist, "EXTNAME", VISIR_EXTN_BPM);
    }

    if (errframes != NULL) {
        /* 3) The error frame */

        frame = irplib_framelist_get_const(errframes, eshared ? 0 : i);

        filename = cpl_frame_get_filename(frame);

        err = cpl_image_load(filename, CPL_TYPE_UNSPECIFIED, 0, 0);
        skip_if(err == NULL);

        err_plist = cpl_propertylist_load_regexp(filename, 0, " ESO QC | ESO PRO ",
                                             0);
        skip_if(err_plist == NULL);
        cpl_propertylist_append_string(err_plist, "EXTNAME", VISIR_EXTN_ERROR);
        if (bunit) {
            cpl_propertylist_append_string(err_plist, "BUNIT", bunit);
        }

        if (wgtframes == NULL) {
            wgt = cpl_image_new(cpl_image_get_size_x(err),
                                cpl_image_get_size_y(err),
                                CPL_TYPE_FLOAT);
            cpl_image_add_scalar(wgt, 1.0);
            cpl_image_divide(wgt, err);
            cpl_image_power(wgt, 2);
            wgt_plist = cpl_propertylist_new();
            cpl_propertylist_append_string(wgt_plist, "EXTNAME", VISIR_EXTN_WEIGHT);
            skip_if(0);
        }

    }

    if (conframes != NULL) {
        /* 4) The contribution frame(s) */

        if (cshared || irplib_framelist_get_size(conframes) == n) {

            frame = irplib_framelist_get_const(conframes, cshared ? 0 : i);

            filename = cpl_frame_get_filename(frame);

            con = cpl_image_load(filename, CPL_TYPE_INT, 0, 0);
            skip_if(con == NULL);

            con_plist = cpl_propertylist_load_regexp(filename, 0, " ESO QC | ESO PRO ",
                                                     0);
            skip_if(con_plist == NULL);

        } else {

            const int    ncon = irplib_framelist_get_size(conframes);
            const int    nz   = ncon / n;
            int          j;

            for (j = i * nz; j < i * nz + nz; j ++) {
                frame = irplib_framelist_get_const(conframes, j);
                bug_if(cpl_frameset_insert(usedframes,
                                           cpl_frame_duplicate(frame)));

                filename = cpl_frame_get_filename(frame);

                if (j == i * nz) {

                    con = cpl_image_load(filename, CPL_TYPE_INT, 0, 0);
                    skip_if(con == NULL);

                } else {
                    csum = cpl_image_load(filename, CPL_TYPE_INT, 0, 0);
                    skip_if(csum == NULL);
                    skip_if(cpl_image_add(con, csum));
                }
            }

        }

        if (bpm == NULL) {
            bpm = cpl_image_duplicate(con);

            /* FIXME: Find a better way to create a image BPM */
            bug_if(cpl_image_threshold(bpm, -0.5, 1.0, 0.5, 1.0));
            bug_if(cpl_image_multiply_scalar(bpm, -1.0));
            bug_if(cpl_image_add_scalar(bpm, 1.0));

            bpm_plist = cpl_propertylist_new();
            cpl_propertylist_append_string(bpm_plist, "EXTNAME", VISIR_EXTN_BPM);
        }
    }


    if (wgtframes != NULL) {
        /* 5) The weight frame */

        frame = irplib_framelist_get_const(wgtframes, wshared ? 0 : i);

        filename = cpl_frame_get_filename(frame);

        wgt_plist = cpl_propertylist_load_regexp(filename, 0, " ESO QC | ESO PRO ",
                                                 0);
        skip_if(wgt_plist == NULL);

        bug_if(cpl_propertylist_append_string(wgt_plist, "EXTNAME", VISIR_EXTN_WEIGHT));

        wgt = cpl_image_load(filename, CPL_TYPE_FLOAT, 0, 0);
        skip_if(wgt == NULL);

        if (err == NULL) {
            err = cpl_image_new(cpl_image_get_size_x(wgt),
                                cpl_image_get_size_y(wgt), CPL_TYPE_FLOAT);
            cpl_image_add_scalar(err, 1.0);
            cpl_image_divide(err, wgt);
            cpl_image_power(err, 0.5);
            cpl_image_fill_rejected(err, INFINITY);
            err_plist = cpl_propertylist_new();
            cpl_propertylist_append_string(err_plist, "EXTNAME", VISIR_EXTN_ERROR);
            if (bunit) {
                cpl_propertylist_append_string(err_plist, "BUNIT", bunit);
            }
            skip_if(0);
        }

    }

    if (err != NULL && con == NULL && bpm == NULL) {

        /* FIXME: Find a better way to create a image BPM */
        cpl_mask * mbpm = cpl_mask_threshold_image_create(err, -DBL_EPSILON, 1e8);
        cpl_mask_not(mbpm);
        bpm = cpl_image_new_from_mask(mbpm);
        cpl_mask_delete(mbpm);

        bpm_plist = cpl_propertylist_new();
        cpl_propertylist_append_string(bpm_plist, "EXTNAME", VISIR_EXTN_BPM);
    }
    skip_if(0);

    /* try to compute gauss fwhm also for science images, may fail (PIPE-6158) */
    if (!cpl_propertylist_has(img_plist, "ESO QC GAUSSFIT FWHM_MAX")) {
        cpl_errorstate cleanstate = cpl_errorstate_get();
        double fwhm_x = -1., fwhm_y = -1.,
               peak = -1., peak_err = 0.,
               major = -1., major_err = 0.,
               minor = -1., minor_err = 0.,
               angle = -1., angle_err = 0.;
        cpl_size x_pos = cpl_propertylist_get_double(img_plist, "CRPIX1");
        cpl_size y_pos = cpl_propertylist_get_double(img_plist, "CRPIX2");
        cpl_image * lwgt = wgt;

        /* Compute the FWHM */
        cpl_image_get_fwhm(img, (int)x_pos, (int)y_pos, &fwhm_x, &fwhm_y);

        if (lwgt == NULL) {
            lwgt = cpl_image_new(cpl_image_get_size_x(img), cpl_image_get_size_y(img), CPL_TYPE_DOUBLE);
            cpl_image_add_scalar(lwgt, 1);
        }
        if (fit_2d_gauss(img, lwgt, (cpl_size)x_pos, (cpl_size)y_pos,
                         fwhm_x, fwhm_y, &peak, &peak_err,
                         &major, &major_err, &minor, &minor_err,
                         &angle, &angle_err) == CPL_ERROR_NONE) {
            cpl_msg_info(cpl_func, "Peak: %g +- %g, FWHM : %g +- %g major ; %g +- %g minor, "
                         "angle %g +- %g", peak, peak_err,
                         major, major_err, minor, minor_err,
                         angle * CPL_MATH_DEG_RAD,
                         angle_err * CPL_MATH_DEG_RAD);
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT FWHM_MAX",
                                           major);
            cpl_propertylist_set_comment(img_plist, "ESO QC GAUSSFIT FWHM_MAX",
                                         "major axis [pix]");
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT FWHM_MAX_ERR",
                                           major_err);
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT FWHM_MIN",
                                           minor);
            cpl_propertylist_set_comment(img_plist, "ESO QC GAUSSFIT FWHM_MIN",
                                         "minor axis [pix]");
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT FWHM_MIN_ERR",
                                           minor_err);
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT ANGLE",
                                           angle * CPL_MATH_DEG_RAD);
            cpl_propertylist_set_comment(img_plist, "ESO QC GAUSSFIT ANGLE",
                                         "[deg]");
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT ANGLE_ERR",
                                           angle_err * CPL_MATH_DEG_RAD);
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT PEAK",
                                           peak);
            cpl_propertylist_append_double(img_plist, "ESO QC GAUSSFIT PEAK_ERR",
                                           peak_err);
        }
        else {
            cpl_msg_warning(cpl_func, "2D gauss fit failed, approximate FWHM : %g"
                            "in x ; %g in y ", fwhm_x, fwhm_y);
        }

        if (lwgt != wgt) {
            cpl_image_delete(lwgt);
        }
        /* ignore all errors */
        cpl_errorstate_set(cleanstate);
    }

    skip_if(irplib_dfs_save_image(products, parlist, usedframes,
                                  img, CPL_TYPE_UNSPECIFIED,
                                  RECIPE_STRING,
                                  procatg, img_plist,
                                  NULL, visir_pipe_id, proname));

    if (bpm) {
        if (bpmframes) {
            frame = irplib_framelist_get_const(bpmframes, bshared ? 0 : i);
            bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(frame)));
        }
        skip_if(cpl_image_save(bpm, proname, CPL_BPP_8_UNSIGNED,
                               bpm_plist, CPL_IO_EXTEND));
    }
    if (err) {
        if (errframes) {
            frame = irplib_framelist_get_const(errframes, eshared ? 0 : i);
            bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(frame)));
        }
        skip_if(cpl_image_save(err, proname, CPL_TYPE_UNSPECIFIED,
                               err_plist, CPL_IO_EXTEND));
    }
    if (con) {
        if (conframes) {
            frame = irplib_framelist_get_const(conframes, cshared ? 0 : i);
            bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(frame)));
        }
        skip_if(cpl_image_save(con, proname, CPL_TYPE_UNSPECIFIED,
                               con_plist, CPL_IO_EXTEND));
    }
    if (wgt) {
        if (wgtframes) {
            frame = irplib_framelist_get_const(wgtframes, wshared ? 0 : i);
            bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(frame)));
        }
        skip_if(cpl_image_save(wgt, proname, CPL_TYPE_UNSPECIFIED,
                               wgt_plist, CPL_IO_EXTEND));
    }

    FOR_EACH_FRAMESET_C(frm, products) {
        cpl_frame * copy = cpl_frame_duplicate(frm);
        cpl_error_code error = cpl_frameset_insert(framelist, copy);

        if (error) break;
    }

    end_skip;

    cpl_image_delete(img);
    cpl_image_delete(err);
    cpl_image_delete(wgt);
    cpl_image_delete(con);
    cpl_image_delete(bpm);
    cpl_image_delete(csum);
    cpl_free(proname);
    cpl_free(inpcatg);
    cpl_propertylist_delete(img_plist);
    cpl_propertylist_delete(err_plist);
    cpl_propertylist_delete(wgt_plist);
    cpl_propertylist_delete(con_plist);
    cpl_propertylist_delete(bpm_plist);
    cpl_propertylist_delete(qc_plist);
    cpl_frameset_delete(usedframes);
    cpl_frameset_delete(products);

    return cpl_error_get_code();
}
