/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library 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, 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA          *
 */

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


/*----------------------------------------------------------------------------*/
/**
 * @defgroup flames_redchain  Recipe: Reduction Chain
 *
 * This recipe executes a reduction chain. See man-page for details.
 */
/*----------------------------------------------------------------------------*/

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

#include <flames.h>
#include <uves.h>
#include <uves_parameters.h>
#include <uves_utils.h>
#include <uves_utils_wrappers.h>
#include <uves_dfs.h>
#include <uves_recipe.h>
#include <uves_error.h>
#include <uves_msg.h>

/* Library */
#include <cpl.h>
#include <string.h>
/*-----------------------------------------------------------------------------
                            Local constants
 -----------------------------------------------------------------------------*/
static const bool flames = true;
static const bool blue = false;

/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
static cpl_error_code
execute_recipe(int (*get_info)(cpl_pluginlist *), 
               const char *recipe_domain,
               cpl_frameset *frames, const cpl_parameterlist *parameters,
               const char *products[],
               int n_products,
               bool reclassify);

static bool is_missing(const cpl_frameset *frames, const char *frame1, const char *frame2);
static void remove_input_frame(cpl_frameset *frames, const char *tag);

static int flames_obs_redchain_define_parameters(cpl_parameterlist *parameters);

/*-----------------------------------------------------------------------------
                            Recipe standard code
 -----------------------------------------------------------------------------*/
#define cpl_plugin_get_info flames_obs_redchain_get_info
UVES_RECIPE_DEFINE(
                FLAMES_OBS_REDCHAIN_ID, FLAMES_OBS_REDCHAIN_DOM, flames_obs_redchain_define_parameters,
                "Jonas M. Larsen", "cpl@eso.org",
                "Runs the full UVES-FIBRE reduction chain",
                "This recipe does a complete science reduction. It runs all necessary\n"
                "calibration recipes depending on the availability of raw/processed\n"
                "calibration frames.\n"
                "Input frames are all UVES-FIBER raw and reference frames:\n"
                "formatchecks, FIB_ARC_LAMP_FORM_RED,\n"
                "order definition frames, FIB_ORDER_FLAT_RED,\n"
                "biases, BIAS_RED,\n"
                "darks, DARK_RED,\n"
                "flats, SFLAT_RED,\n"
                "arc lamps, FIB_ARC_LAMP_RED,\n"
                "standard stars, FIB_STANDARD_RED\n"
                "a wavelength catalogue table,LINE_REFER_TABLE, \n"
                "and optionally a wavelength table of bright lines,LINE_INTMON_TABLE, \n"
                "used only for computing Quality Control parameters.\n"
                "a reference standard star flux table, FLUX_STD_TABLE, \n"
                "a table describing the atmospheric extintion,EXTCOEFF_TABLE.\n"
                "Optionally, science frames, SCIENCE_xxx, or UVES_SCI_POINT_xxx, \n"
                "or UVES_SCI_EXTND_xxx, or UVES_SCI_SLICER_xxx.\n"
                "For further details on the data reduction and the input frame types\n"
                "refer to the man page of the individual recipes.\n");

/**@{*/

/*-----------------------------------------------------------------------------
                              Functions code
 -----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    parameters        the parameterlist to fill
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int
flames_obs_redchain_define_parameters(cpl_parameterlist *parameters)
{
    const char *recipe_id = make_str(FLAMES_OBS_REDCHAIN_ID);
    const char *subcontext = NULL;

    uves_par_new_value("scired",
                       CPL_TYPE_BOOL,
                       "Whether or not to do science reduction. "
                       "If false, only master calibration frames "
                       "are created. If false, either zero or all "
                       "necessary calibration frames must be provided "
                       "for each arm",
                       true);

    /*****************
     *    General    *
     *****************/
    if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
    {
        return -1;
    }

    /******************
     *   Master bias  *
     ******************/
    if (uves_propagate_parameters(
                    make_str(UVES_MBIAS_ID), parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     *   Master dark  *
     ******************/
    if (uves_propagate_parameters(
                    make_str(UVES_MDARK_ID), parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     * Physical model *
     ******************/
    if (uves_prop_par(
                    &flames_cal_predict_get_info, parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     * Order position *
     ******************/
    if (uves_prop_par(
                    &flames_cal_orderpos_get_info, parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     *   Master flat  *
     ******************/
    if (uves_propagate_parameters(
                    make_str(UVES_MFLAT_ID), parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     *   Wave.cal.    *
     ******************/
    if (uves_prop_par(
                    &flames_cal_wavecal_get_info, parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     *     Prep       *
     ******************/
    if (uves_prop_par(
                    &flames_cal_prep_sff_ofpos_get_info, parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    /******************
     *    Scired      *
     ******************/
    if (uves_prop_par(
                    &flames_obs_scired_get_info, parameters, make_str(FLAMES_OBS_REDCHAIN_ID), NULL) != 0)
    {
        return -1;
    }

    return (cpl_error_get_code() != CPL_ERROR_NONE);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    parameters  the parameters list
  @param    frames      the frames list
  @return   CPL_ERROR_NONE if everything is ok


 */
/*----------------------------------------------------------------------------*/
static void
UVES_CONCAT2X(FLAMES_OBS_REDCHAIN_ID,exe)(cpl_frameset *frames,
                const cpl_parameterlist *parameters,
                const char *starttime)
                {
    const enum uves_chip chip1 = UVES_CHIP_REDL;
    const enum uves_chip chip2 = UVES_CHIP_REDU;

    bool do_science;

    bool run_mbias;
    bool run_mdark;
    bool run_physmod;
    bool run_orderpos;
    bool run_mflat;
    bool run_prep;
    bool run_wavecal;
    bool run_scired;

    cpl_frameset *fms = NULL;

    /* Exceptionally, this parameter is not used because this
       recipe does not create any products on its own. Suppress
       warning about unused variable */
    starttime = starttime;

    check( uves_get_parameter(parameters, NULL, make_str(FLAMES_OBS_REDCHAIN_ID), 
                    "scired",
                    CPL_TYPE_BOOL, &do_science), "Could not read parameter");

    /* Check for at least one science frame */
    assure(!do_science ||
           cpl_frameset_find(frames, FLAMES_SCI_RED)    != NULL ||
           cpl_frameset_find(frames, FLAMES_SCI_SIM_RED) != NULL ||
           cpl_frameset_find(frames, FLAMES_SCI_COM_RED) != NULL,
           CPL_ERROR_DATA_NOT_FOUND, "No %s, %s or %s in frame set",
           FLAMES_SCI_RED,
           FLAMES_SCI_SIM_RED,
           FLAMES_SCI_COM_RED);

    fms = cpl_frameset_duplicate(frames);
    assure_mem( fms );

    /* Algorithm:
       (with purpose of failing early if we have to fail.)

       1) Find out which recipes to run
       2) Check for necessary input frames
       3) Execute
     */

    run_scired = do_science &&
                    !(is_missing(fms, FLAMES_SCI_RED, NULL) &&
                                    is_missing(fms, FLAMES_SCI_SIM_RED, NULL) &&
                                    is_missing(fms, FLAMES_SCI_COM_RED, NULL));

    /* Require master bias */
    run_mbias = is_missing(fms, 
                    UVES_MASTER_BIAS(chip1),
                    UVES_MASTER_BIAS(chip2));

    /* Run master dark, only if master is missing and
       raw frames are available */ 
    run_mdark = 
                    is_missing(fms,
                                    UVES_MASTER_DARK(chip1),
                                    UVES_MASTER_DARK(chip2))
                                    &&
                                    is_missing(fms,
                                                    UVES_MASTER_PDARK(chip1),
                                                    UVES_MASTER_PDARK(chip2))
                                                    &&(!is_missing(fms, UVES_DARK(blue), NULL) ||
                                                                    !is_missing(fms, UVES_PDARK(blue), NULL));

    /* Run prep if no (final) order table */
    run_prep = is_missing(fms,
                    UVES_ORDER_TABLE(flames, chip1),
                    UVES_ORDER_TABLE(flames, chip2));

    /* Run orderpos if order table and guess order table is missing */
    run_orderpos = run_prep && (
                    is_missing(fms,
                                    UVES_GUESS_ORDER_TABLE(flames, chip1),
                                    UVES_GUESS_ORDER_TABLE(flames, chip2))
                                    ||
                                    is_missing(fms,
                                                    FLAMES_ORDEF(flames, chip1),
                                                    FLAMES_ORDEF(flames, chip2))
    );

    /* Run master flat recipe if master flat frame is missing */
    run_mflat = run_prep &&    
                    is_missing(fms,
                               UVES_MASTER_SFLAT(chip1),
                               UVES_MASTER_SFLAT(chip2));

    /* Run wavecal if no line table */
    run_wavecal = is_missing(fms,
                    UVES_LINE_TABLE(flames, chip1),
                    UVES_LINE_TABLE(flames, chip2));

    /* Run physical model if there's no guess line table */
    run_physmod = 
                    (run_wavecal && is_missing(fms,
                                               UVES_GUESS_LINE_TABLE(flames, chip1),
                                               UVES_GUESS_LINE_TABLE(flames, chip2)))
                                               ||
                                               (run_orderpos && is_missing(fms,
                                                                           UVES_GUESS_ORDER_TABLE(flames, chip1),
                                                                           UVES_GUESS_ORDER_TABLE(flames, chip2)));



    uves_msg("Reduction strategy for %s arm:", (blue) ? "BLUE" : "RED");
    uves_msg("Run %-13s: %s", make_str(UVES_MBIAS_ID)   , (run_mbias   ) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(UVES_MDARK_ID)   , (run_mdark   ) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(FLAMES_CAL_PREDICT_ID) , (run_physmod ) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(FLAMES_CAL_ORDERPOS_ID), (run_orderpos) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(UVES_MFLAT_ID)   , (run_mflat   ) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID) , (run_prep) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(FLAMES_CAL_WAVECAL_ID) , (run_wavecal ) ? "Yes" : "No");
    uves_msg("Run %-13s: %s", make_str(FLAMES_OBS_SCIRED_ID)  , (run_scired  ) ? "Yes" : "No");

    /* As a service to the user, assure that required
       raw frames and catalogue calibration frames
       exist *before* doing the reduction */

    assure( !run_mbias || !is_missing(fms, UVES_BIAS(blue), NULL), 
            CPL_ERROR_DATA_NOT_FOUND,
            "One or more '%s' frames needed for recipe '%s'",
            UVES_BIAS(blue), make_str(UVES_MBIAS_ID));

    assure( !run_mdark || 
            !is_missing(fms, UVES_DARK(blue), NULL) ||
            !is_missing(fms, UVES_PDARK(blue), NULL),
            CPL_ERROR_DATA_NOT_FOUND, 
            "One or more '%s' or '%s' frames needed for recipe '%s'",
            UVES_DARK(blue), UVES_PDARK(blue), make_str(UVES_MDARK_ID));

    assure( !run_physmod || !is_missing(fms, UVES_FORMATCHECK(flames, blue), NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            UVES_FORMATCHECK(flames, blue), make_str(FLAMES_CAL_PREDICT_ID));

    assure( !run_orderpos || !is_missing(fms, UVES_ORDER_FLAT(flames, blue), NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            UVES_ORDER_FLAT(flames, blue), make_str(FLAMES_CAL_ORDERPOS_ID));

    assure( !run_mflat || !is_missing(fms, UVES_SFLAT(blue), NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            UVES_SFLAT(blue),
            make_str(UVES_MFLAT_ID));

    assure( !run_wavecal || 
            !is_missing(fms, UVES_ARC_LAMP(flames, blue), NULL) ||
            !is_missing(fms, FLAMES_FIB_SCI_SIM, NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' or '%s' needed for recipe '%s'",
            UVES_ARC_LAMP(flames, blue), FLAMES_FIB_SCI_SIM,
            make_str(FLAMES_CAL_WAVECAL_ID));

    assure( !run_wavecal || !is_missing(fms, UVES_LINE_REFER_TABLE, NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            UVES_LINE_REFER_TABLE, make_str(FLAMES_CAL_WAVECAL_ID));

    assure( !run_prep || !is_missing(fms, FLAMES_FIB_FF_ODD, NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            FLAMES_FIB_FF_ODD,
            make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID));

    assure( !run_prep || !is_missing(fms, FLAMES_FIB_FF_EVEN, NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            FLAMES_FIB_FF_EVEN,
            make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID));

    assure( !run_prep || !is_missing(fms, FLAMES_FIB_FF_ALL, NULL),
            CPL_ERROR_DATA_NOT_FOUND, "Frame '%s' needed for recipe '%s'",
            FLAMES_FIB_FF_ALL,
            make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID));

    /* We now know which recipes to run and
     * that required input frames exist. Execute
     * chain; re-classify PRODUCT->CALIB under way
     */

    if (run_mbias)
    {
        const char *products[2];

        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_MASTER_BIAS(chip1);
        products[1] = UVES_MASTER_BIAS(chip2);

        check( execute_recipe(&uves_mbias_get_info,
                        UVES_MBIAS_DOM, fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_BIAS(blue)), "Error removing input frames");

    if (run_mdark)
    {
        const char *products[4];

        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_MASTER_DARK(chip1);
        products[1] = UVES_MASTER_PDARK(chip1);
        products[2] = UVES_MASTER_DARK(chip2);
        products[3] = UVES_MASTER_PDARK(chip2);

        check( execute_recipe(&uves_mdark_get_info,
                        UVES_MDARK_DOM, fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_DARK(blue)), "Error removing input frames");
    check( remove_input_frame(fms, UVES_PDARK(blue)), "Error removing input frames");

    if (run_physmod)
    {
        const char *products[4];
        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_GUESS_LINE_TABLE (flames, chip1);
        products[1] = UVES_GUESS_ORDER_TABLE(flames, chip1);
        products[2] = UVES_GUESS_LINE_TABLE (flames, chip2);
        products[3] = UVES_GUESS_ORDER_TABLE(flames, chip2);

        check( execute_recipe(&flames_cal_predict_get_info,
                        FLAMES_CAL_PREDICT_DOM,
                        fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_FORMATCHECK(flames, blue)), 
           "Error removing input frames");

    if (run_orderpos)
    {
        const char *products[4];
        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_ORD_TAB(flames, chip1);
        products[1] = UVES_ORD_TAB(flames, chip2);
        products[2] = FLAMES_ORDEF(flames, chip1);
        products[3] = FLAMES_ORDEF(flames, chip2);

        check( execute_recipe(&flames_cal_orderpos_get_info,
                        FLAMES_CAL_ORDERPOS_DOM,
                        fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_ORDER_FLAT(flames, blue)),
           "Error removing input frames");

    if (run_mflat)
    {
        const char *products[2];

        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_MASTER_SFLAT(chip1);
        products[1] = UVES_MASTER_SFLAT(chip2);

        check( execute_recipe(&uves_mflat_get_info,
                        UVES_MFLAT_DOM,
                        fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_SFLAT(blue)), "Error removing input frames");

    if (run_prep)
    {
        const char *products[29];
        int nprod = sizeof(products) / sizeof (char *);

        products[0] = FIB_FF_ODD_INFO_TAB;
        products[1] = FIB_FF_EVEN_INFO_TAB;
        products[2] = FIB_FF_ALL_INFO_TAB;
        products[3] = UVES_ORDER_TABLE(flames, chip1);
        products[4] = UVES_ORDER_TABLE(flames, chip2);
        products[5] = FLAMES_SLIT_FF_COM(chip1);
        products[6] = FLAMES_SLIT_FF_COM(chip2);
        products[7] = FLAMES_SLIT_FF_NOR(chip1);
        products[8] = FLAMES_SLIT_FF_NOR(chip2);
        products[9] = FLAMES_SLIT_FF_DTC(chip1);
        products[10] = FLAMES_SLIT_FF_DTC(chip2);
        products[11] = FLAMES_SLIT_FF_BPC(chip1);
        products[12] = FLAMES_SLIT_FF_BPC(chip2);
        products[13] = FLAMES_SLIT_FF_SGC(chip1);
        products[14] = FLAMES_SLIT_FF_SGC(chip2);
        products[15] = FLAMES_SLIT_FF_BNC(chip1);
        products[16] = FLAMES_SLIT_FF_BNC(chip2);
        products[17] = FLAMES_FIB_FF_COM(chip1);
        products[18] = FLAMES_FIB_FF_COM(chip2);
        products[19] = FLAMES_FIB_FF_NOR(chip1);
        products[20] = FLAMES_FIB_FF_NOR(chip2);
        products[21] = FLAMES_FIB_FF_NSG(chip1);
        products[22] = FLAMES_FIB_FF_NSG(chip2);
        products[23] = FLAMES_FIB_FF_DTC(chip1);
        products[24] = FLAMES_FIB_FF_DTC(chip2);
        products[25] = FLAMES_FIB_FF_BPC(chip1);
        products[26] = FLAMES_FIB_FF_BPC(chip2);
        products[27] = FLAMES_FIB_FF_SGC(chip1);
        products[28] = FLAMES_FIB_FF_SGC(chip2);
        /* And many others too numerous to mention */

        check( execute_recipe(&flames_cal_prep_sff_ofpos_get_info,
                        FLAMES_CAL_PREP_SFF_OFPOS_DOM,
                        fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, FLAMES_FIB_FF_ODD), "Error removing input frames");
    check( remove_input_frame(fms, FLAMES_FIB_FF_EVEN), "Error removing input frames");
    check( remove_input_frame(fms, FLAMES_FIB_FF_ALL), "Error removing input frames");


    if (run_wavecal)
    {
        const char *products[2];
        int nprod = sizeof(products) / sizeof (char *);

        products[0] = UVES_LINE_TABLE(flames, chip1);
        products[1] = UVES_LINE_TABLE(flames, chip2);

        check( execute_recipe(&flames_cal_wavecal_get_info,
                        FLAMES_CAL_WAVECAL_DOM,
                        fms, parameters, products, nprod, true),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, UVES_ARC_LAMP(flames, blue)), 
           "Error removing input frames");
    check( remove_input_frame(fms, FLAMES_FIB_SCI_SIM),
           "Error removing input frames");
    check( remove_input_frame(fms, UVES_LINE_REFER_TABLE), 
           "Error removing input frames");

    if (run_scired)
    {
        const char *products[2];
        int nprod = sizeof(products) / sizeof (char *);

        /* not used */
        products[0] = "DUMMY";
        products[1] = "DUMMY";

        check( execute_recipe(&flames_obs_scired_get_info,
                        FLAMES_OBS_SCIRED_DOM,
                        fms, parameters, products, nprod, false),
               "Recipe execution failed");
    }

    check( remove_input_frame(fms, FLAMES_SCI_RED), "Error removing input frames");
    check( remove_input_frame(fms, FLAMES_SCI_COM_RED), "Error removing input frames");

    /* Insert all product frames into recipe frame set */
    {
        cpl_frame *f = NULL;
        int sz=cpl_frameset_get_size(fms);
        int i=0;
        for (i = 0;i < sz;i++ )
        {
            f=cpl_frameset_get_frame(fms,i);
            if (cpl_frame_get_group(f) == CPL_FRAME_GROUP_PRODUCT)
            {
                check( cpl_frameset_insert(frames, cpl_frame_duplicate(f)),
                                "Error inserting product '%s' into frame set",
                                cpl_frame_get_tag(f));
            }
        }
    }

    cleanup:
    uves_free_frameset(&fms);
    return;
                }

/* Execute a recipe and re-classify its products as calibration frames */
static cpl_error_code
execute_recipe(int (*get_info)(cpl_pluginlist *), 
               const char *recipe_domain,
               cpl_frameset *frames, const cpl_parameterlist *parameters,
               const char *products[],
               int n_products,
               bool reclassify)              /* Re-classify products? */
{
    int i;
    cpl_frame *f = NULL;

    /* Remove (from frame set) any product
     frames already present */
    for (i = 0; i < n_products; i++)
    {
        if ((f = cpl_frameset_find(frames, products[i])) != NULL)
        {
            if (cpl_frame_get_group(f) == CPL_FRAME_GROUP_PRODUCT)
            {
                cpl_msg_warning(__func__, "Ignoring %s frame in '%s'. "
                                "A new %s frame will now be calculated",
                                products[i], cpl_frame_get_filename(f),
                                products[i]);

                cpl_frameset_erase_frame(frames, f);
            }
        }
    }

    /* Execute */
    check_nomsg( uves_exec_recipe(get_info, recipe_domain, parameters, frames, make_str(FLAMES_OBS_REDCHAIN_ID), NULL));
    if (reclassify)
    {
        /* Now re-classify PRODUCT->CALIB to be used in the remaining
             reduction chain. Before doing that, we have to remove any
             calibration frame with same tag as a product (such as line tables),
             in order not to confuse the re-classified products with the
             previous calibration frames */

        for (i = 0; i < n_products; i++)
        {
            if ((f = cpl_frameset_find(frames, products[i])) != NULL &&
                            cpl_frame_get_group(f) != CPL_FRAME_GROUP_PRODUCT)
            {
                uves_msg("Removing %s frame in '%s' from frameset. "
                                "It is not tagged as a product",
                                products[i], cpl_frame_get_filename(f));

                cpl_frameset_erase_frame(frames, f);
            }
        }

        /*
         * Re-classify products
         */
        int sz=cpl_frameset_get_size(frames);
        int j=0;
        for (i = 0; i < n_products; i++) {
            for (j = 0;j < sz;j++) {
                f=cpl_frameset_get_frame(frames,j);
                if (cpl_frame_get_group(f) == CPL_FRAME_GROUP_PRODUCT) {
                    if (strcmp(cpl_frame_get_tag(f), products[i]) == 0)
                    {
                        /* Re-classify the product as calibration frames */
                        uves_msg("Re-classifying %s product in '%s' PRODUCT->CALIB",
                                        products[i], cpl_frame_get_filename(f));

                        cpl_frame_set_group(f, CPL_FRAME_GROUP_CALIB);
                    }
                }
            }
        }

        /*
         * Remove other products that
         * are not used later  (e.g. BKG_FLAT_xxxx)
         */
        for (j=0;j<sz;j++)
        {
            f=cpl_frameset_get_frame(frames,j);
            if (cpl_frame_get_group(f) == CPL_FRAME_GROUP_PRODUCT)
            {
                /* Remove this product */
                uves_msg("Removing product %s in '%s' from frameset. "
                                "Not needed later",
                                cpl_frame_get_tag(f), cpl_frame_get_filename(f));

                cpl_frameset_erase_frame(frames, f);

            }
        }
    } /* if re-classify... */

    cleanup:
    return cpl_error_get_code();
}


/* Retruns true if either frame 1 or frame 2 is not in the
   provided frame set 

   fixme: reverse the logic of this function, i.e. rename to 'contains'
 */
static bool
is_missing(const cpl_frameset *frames, const char *frame1, const char *frame2)
{
    bool result = false;
    if (cpl_frameset_find_const(frames, frame1) == NULL)
    {
        uves_msg("checking for %s... no", frame1);
        result = true;
    }
    else
    {
        uves_msg("checking for %s... yes", frame1);
    }

    if (frame2 != NULL && strcmp(frame1, frame2) != 0)
    {
        if (cpl_frameset_find_const(frames, frame2) == NULL)
        {
            uves_msg("checking for %s... no", frame2);
            result = true;
        }
        else
        {
            uves_msg("checking for %s... yes", frame2);
        }
    }

    return result;
}


/* Remove input frames (e.g. bias frames) along the way */
static void
remove_input_frame(cpl_frameset *frames, const char *tag)
{
    int removed = cpl_frameset_erase(frames, tag);

    if (removed > 0)
    {
        uves_msg("Removing %d %s frame%s from frame set", removed, tag, removed > 1 ? "s" : "");
    }

    return;
}
/**@}*/
