/* $Id: visir_spc_obs.c,v 1.111 2013-05-13 16:04:49 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2002,2003,2012 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:04:49 $
 * $Revision: 1.111 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "visir_recipe.h"
#include "visir_spectro.h"
#include "visir_spc_distortion.h"
#include "visir_spc_photom.h"


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

#define RECIPE_STRING  "visir_old_spc_obs"
/* still used by chain recipe internally, so keep name for output */
#define RECIPE_SAVE_STRING "visir_spc_obs"

/* FITS keys to be loaded for all raw files */
#define RECIPE_KEYS_REGEXP_ALL            \
        VISIR_PFITS_REGEXP_IMG_RECOMBINE  \
    "|" VISIR_PFITS_REGEXP_SPC_GET_RES_WL

/* FITS keys to be loaded for first raw file */
#define RECIPE_KEYS_REGEXP               \
        RECIPE_KEYS_REGEXP_ALL           \
    "|" VISIR_PFITS_REGEXP_CAPA          \
    "|" VISIR_PFITS_REGEXP_SPC_WCAL_PAF

/* FITS keys to be loaded for first raw file, in case WCS is used */
#define RECIPE_KEYS_REGEXP_WCS \
        RECIPE_KEYS_REGEXP \
    "|" IRPLIB_PFITS_WCS_REGEXP

#define IS_PREPROCESSED 1
#define IS_PHOT 2
#define IS_ECH 4
#define RAW_INPUT 0
#define PREPROCESSED_INPUT IS_PREPROCESSED
#define RAW_PHOT_INPUT IS_PHOT
#define PREPROCESSED_PHOT_INPUT (IS_PHOT | IS_PREPROCESSED)

/*-----------------------------------------------------------------------------
                            Private Functions prototypes
 -----------------------------------------------------------------------------*/
static cpl_error_code visir_spc_obs_save(cpl_frameset *,
                                         const cpl_parameterlist *,
                                         cpl_propertylist *,
                                         const cpl_propertylist *,
                                         const cpl_image *,
                                         const cpl_image *,
                                         const cpl_table *,
                                         const char *);

#ifdef VISIR_CHAIN
#define cpl_plugin_get_info visir_old_spc_obs_get_info
#endif
VISIR_RECIPE_DEFINE(visir_old_spc_obs,
                    VISIR_PARAM_ORDEROFF |
                    VISIR_PARAM_EMIS_TOL |
                    VISIR_PARAM_REFINE | VISIR_PARAM_XCORR |
                    VISIR_PARAM_OFFSETS | VISIR_PARAM_OBJECTS |
                    VISIR_PARAM_NODPOS | VISIR_PARAM_AUTOBPM |
                    VISIR_PARAM_GLITCH | VISIR_PARAM_PURGE |
                    VISIR_PARAM_UNION  | VISIR_PARAM_REJECT |
                    VISIR_PARAM_STRIPITE | VISIR_PARAM_STRIPMOR |
                    VISIR_PARAM_PLOT   | VISIR_PARAM_SLITSKEW |
                    VISIR_PARAM_SPECSKEW | VISIR_PARAM_VERTARC |
                    VISIR_PARAM_REJLEFT | VISIR_PARAM_REJRIGHT |
                    VISIR_PARAM_HORIARC | VISIR_PARAM_FIXCOMBI |
                    VISIR_PARAM_BKG_CORRECT,
                    "Old DRS detector: Spectroscopic Observation recipe",
                    "This recipe estimates the dispersion relation using the "
                    "atmospheric spectrum\n"
                    "in a long-slit spectroscopy half-cycle frame.\n"
                    "It also extracts the spectrum of an observed object using "
                    "a combined frame.\n"
                    "The files listed in the Set Of Frames (sof-file) "
                    "must be tagged:\n"
                    "VISIR-Long-Slit-Spectroscopy-file.fits "
                    VISIR_SPC_OBS_RAW "\n"
                    "VISIR-Quantum-Efficiency-Calibration-file.fits "
                    VISIR_CALIB_QEFF_SPC "\n"
                    "VISIR-Atmospheric-Emission-Lines-Calibration-file.fits "
                    VISIR_CALIB_LINES_SPC
                    "\n"
                    MAN_VISIR_CALIB_BPM_SPC);

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_spc_obs   Spectroscopic Observation
 */
/*----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static int visir_old_spc_obs(cpl_frameset            * framelist,
                         const cpl_parameterlist * parlist)
{
    irplib_framelist * allframes = NULL;
    irplib_framelist * rawframes = NULL;
    irplib_framelist * skyframes = NULL;
    cpl_propertylist * qclist    = cpl_propertylist_new();
    cpl_propertylist * paflist   = cpl_propertylist_new();
    const char      *   badpix;
    const char      *   star_cat;
    const char      *   spc_cal_qeff;
    const char      *   spc_cal_lines;
    const char      *   flat;
    cpl_image       *   combined = NULL;
    cpl_image       *   flipped = NULL;
    cpl_image       *   comnarrow = NULL;
    cpl_imagelist   *   hcycle = NULL;
    cpl_table       *   spc_table = NULL;
    cpl_image       *   weight2d  = NULL;
    cpl_image       *   imhcycle = NULL;
    cpl_image       *   imhcyclenarrow = NULL;
    cpl_image      **   combinedpair;
    double              wlen, slitw, temp, fwhm;
    visir_spc_resol     resol;
    cpl_boolean        drop_wcs;
    int                input_mode;
    cpl_boolean        bkgcorrect;
    visir_spc_config   spc_config;
    const char       * dit_key = VISIR_PFITS_DOUBLE_DIT;
    const char       * rawtag;

    spc_config.recipename = RECIPE_STRING;
    spc_config.parlist = parlist;
    spc_config.orderoffset = 0;
    spc_config.phot_emis_tol = 1.0;

    /* Retrieve input parameters */
    spc_config.plot =
        visir_parameterlist_get_int(parlist, RECIPE_STRING, VISIR_PARAM_PLOT);

    spc_config.phi =
        visir_parameterlist_get_double(parlist,RECIPE_STRING,VISIR_PARAM_SLITSKEW);
    spc_config.ksi =
        visir_parameterlist_get_double(parlist,RECIPE_STRING,VISIR_PARAM_SPECSKEW);
    spc_config.eps =
        visir_parameterlist_get_double(parlist, RECIPE_STRING,VISIR_PARAM_VERTARC);
    spc_config.delta =
        visir_parameterlist_get_double(parlist, RECIPE_STRING,VISIR_PARAM_HORIARC);

    spc_config.orderoffset =
        visir_parameterlist_get_int(parlist, RECIPE_STRING, VISIR_PARAM_ORDEROFF);

    spc_config.do_fixcombi = visir_parameterlist_get_bool(parlist, RECIPE_STRING,
                                                          VISIR_PARAM_FIXCOMBI);
    spc_config.phot_emis_tol =
        visir_parameterlist_get_double(parlist, RECIPE_STRING, VISIR_PARAM_EMIS_TOL);

    bkgcorrect = visir_parameterlist_get_bool(parlist, RECIPE_STRING,
                                              VISIR_PARAM_BKG_CORRECT);
    skip_if (0);

    /* Identify the RAW and CALIB frames in the input frameset */
    skip_if (visir_dfs_set_groups(framelist));

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

    if (irplib_frameset_find_file(framelist, VISIR_SPC_OBS_PP)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_OBS_PP);
        rawtag = VISIR_SPC_OBS_PP;
        cpl_msg_info(cpl_func, "Input: preprocessed observation frame ("
                     VISIR_SPC_OBS_PP ")");
        input_mode = PREPROCESSED_INPUT;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_PHOT_PP)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_PHOT_PP);
        rawtag = VISIR_SPC_PHOT_PP;
        cpl_msg_info(cpl_func, "Input: preprocessed photometric frame ("
                     VISIR_SPC_PHOT_PP ")");
        input_mode = PREPROCESSED_PHOT_INPUT;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_OBS_ECH_PP)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_OBS_ECH_PP);
        rawtag = VISIR_SPC_OBS_ECH_PP;
        cpl_msg_info(cpl_func, "Input: preprocessed observation echelle frame ("
                     VISIR_SPC_OBS_ECH_PP ")");
        input_mode = PREPROCESSED_INPUT | IS_ECH;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_PHOT_ECH_PP)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_PHOT_ECH_PP);
        rawtag = VISIR_SPC_PHOT_ECH_PP;
        cpl_msg_info(cpl_func, "Input: preprocessed photometric echelle frame ("
                     VISIR_SPC_PHOT_ECH_PP ")");
        input_mode = PREPROCESSED_PHOT_INPUT | IS_ECH;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_OBS_RAW)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_OBS_RAW);
        rawtag = VISIR_SPC_OBS_RAW;
        cpl_msg_info(cpl_func, "Input: raw observation frame ("
                     VISIR_SPC_OBS_RAW")");
        input_mode = RAW_INPUT;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_PHOT_RAW)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_PHOT_RAW);
        rawtag = VISIR_SPC_PHOT_RAW;
        cpl_msg_info(cpl_func, "Input: raw photometric frame ("
                     VISIR_SPC_PHOT_RAW")");
        input_mode = RAW_PHOT_INPUT;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_OBS_ECH_RAW)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_OBS_ECH_RAW);
        rawtag = VISIR_SPC_OBS_ECH_RAW;
        cpl_msg_info(cpl_func, "Input: raw observation echelle frame ("
                     VISIR_SPC_OBS_ECH_RAW")");
        input_mode = RAW_INPUT | IS_ECH;

    } else if (irplib_frameset_find_file(framelist, VISIR_SPC_PHOT_ECH_RAW)) {

        rawframes = irplib_framelist_extract(allframes, VISIR_SPC_PHOT_ECH_RAW);
        rawtag = VISIR_SPC_PHOT_ECH_RAW;
        cpl_msg_info(cpl_func, "Input: raw photometric echelle frame ("
                     VISIR_SPC_PHOT_ECH_RAW")");
        input_mode = RAW_PHOT_INPUT | IS_ECH;

    } else {
        error_if(1, CPL_ERROR_ILLEGAL_INPUT, "Input frame set must contain "
               "frame(s) tagged as one of: " VISIR_SPC_OBS_RAW ", "
               VISIR_SPC_PHOT_RAW ", " VISIR_SPC_OBS_PP ","
               VISIR_SPC_PHOT_PP "," VISIR_SPC_OBS_ECH_RAW ","
               VISIR_SPC_OBS_ECH_PP "," VISIR_SPC_PHOT_ECH_RAW ","
               VISIR_SPC_PHOT_ECH_PP);
    }

    bug_if (rawframes == NULL);

    if (input_mode & IS_PREPROCESSED) {
        skyframes = irplib_framelist_extract(allframes, VISIR_SPC_OBS_SKYFRAME);
        error_if(skyframes == NULL, CPL_ERROR_ILLEGAL_INPUT,
                 "Frame set with a preprocessed frame (%s) must include a sky "
               "frame (" VISIR_SPC_OBS_SKYFRAME ")", rawtag);
    }

    irplib_framelist_empty(allframes);

    skip_if(irplib_framelist_load_propertylist_all(rawframes, 0, "^("
                                                   VISIR_PFITS_REGEXP_SPC_SENSIT
                                                   "|" RECIPE_KEYS_REGEXP_ALL
                                                   "|" RECIPE_KEYS_REGEXP_WCS
                                                   "|" VISIR_PFITS_REGEXP_DIT
                                                   "|" VISIR_PFITS_DOUBLE_WLEN
                                                   "|" VISIR_PFITS_DOUBLE_PWLEN
                                                   "|" "ESO DRS .*"
                                                   "|" "ESO QC EXPTIME"
                                                   "|" "ESO QC EXECTIME"
                                                   "|" "ESO QC BACKGD MEAN"
                                                   ")$", CPL_FALSE));

    skip_if(visir_dfs_check_framelist_tag(rawframes));
    
    if (cpl_propertylist_has(irplib_framelist_get_propertylist(rawframes, 0),
                             VISIR_PFITS_DOUBLE_SEQ1_DIT))
        dit_key = VISIR_PFITS_DOUBLE_SEQ1_DIT;
    skip_if(0);

    /* Standard star catalog */
    star_cat = irplib_frameset_find_file(framelist, VISIR_CALIB_STDSTAR_SPC);
    error_if ((input_mode & IS_PHOT) && star_cat == NULL,
              CPL_ERROR_DATA_NOT_FOUND, "Frame set with a photometric frame "
              "(%s) must include a standard star catalogue ("
              VISIR_CALIB_STDSTAR_SPC ")", rawtag);

    visir_data_type data_type;
    const cpl_frame * frm = irplib_framelist_get_const(rawframes, 0);
    skip_if(visir_get_data_type(frm,
         irplib_framelist_get_propertylist(rawframes, 0), &data_type, NULL));

    if (visir_data_is_drs(data_type)) {
        /* if parameters are (aqu) defaults and drs data use old parameters */
        const cpl_parameter * par =
            cpl_parameterlist_find_const(parlist, PACKAGE "." RECIPE_STRING ".ksi");
        if (spc_config.ksi == cpl_parameter_get_default_double(par) * CPL_MATH_RAD_DEG) {
            spc_config.ksi = VISIR_DRS_DIST_KSI;
        }
        par = cpl_parameterlist_find_const(parlist, PACKAGE "." RECIPE_STRING ".eps");
        if (spc_config.eps == cpl_parameter_get_default_double(par)) {
            spc_config.eps = VISIR_DRS_DIST_EPS;
        }
        par = cpl_parameterlist_find_const(parlist, PACKAGE "." RECIPE_STRING ".delta");
        if (spc_config.delta == cpl_parameter_get_default_double(par)) {
            spc_config.delta = VISIR_DRS_DIST_DELTA;
        }
        par = cpl_parameterlist_find_const(parlist, PACKAGE "." RECIPE_STRING ".phi");
        if (spc_config.phi == cpl_parameter_get_default_double(par) * CPL_MATH_RAD_DEG) {
            spc_config.phi = VISIR_DRS_DIST_PHI;
        }
    }

    /* Quantum efficiency file */
    spc_cal_qeff = irplib_frameset_find_file(framelist, VISIR_CALIB_QEFF_SPC);

    /* Spectral lines calibration file */
    spc_cal_lines = irplib_frameset_find_file(framelist, VISIR_CALIB_LINES_SPC);

    /* Bad pixels calibration file */
    badpix = irplib_frameset_find_file(framelist, VISIR_CALIB_BPM);

    /* Flatfield calibration file */
    flat = irplib_frameset_find_file(framelist, VISIR_CALIB_FLAT);

    /* Get Resolution and Central Wavelength */
    resol = visir_spc_get_res_wl(rawframes, &wlen, &slitw, &temp, &fwhm,
                                 visir_data_is_aqu(data_type));

    skip_if (0);

    if (resol == VISIR_SPC_R_GHR) {
        error_if(!(input_mode & IS_ECH) && !(input_mode & IS_PREPROCESSED),
                 CPL_ERROR_TYPE_MISMATCH, "Will not "
                 "reduce echelle data tagged as long slit data");
    } else {
        if (spc_config.orderoffset == 0) {
            cpl_msg_warning(cpl_func,"Reducing non-HR Grism data as main order");
        } else {
            cpl_msg_error(cpl_func, "This recipe cannot reduce non-HR Grism "
                          "data with an order-offset of %d",
                          spc_config.orderoffset);
            visir_error_set(CPL_ERROR_TYPE_MISMATCH);
            skip_if(1);
        }
    }

    if (input_mode & IS_PREPROCESSED) {
        cpl_imagelist * imagelist = cpl_imagelist_new();
        cpl_size nframes = irplib_framelist_get_size(rawframes);
        const cpl_propertylist ** plists =
            cpl_malloc(nframes * sizeof(cpl_propertylist *));
        double bg_sum = 0;

        for (cpl_size i = 0; i < nframes; i++) {
            cpl_frame * frame = irplib_framelist_get(rawframes, i);
            const char * fname = cpl_frame_get_filename(frame);
            const cpl_size next = cpl_fits_count_extensions(fname);
            cpl_imagelist * tmplist = cpl_imagelist_new();
            cpl_errorstate prestate = cpl_errorstate_get();
            cpl_image * tmp;

            plists[i] = irplib_framelist_get_propertylist_const(rawframes, i);
            if (cpl_propertylist_has(plists[i], "ESO QC BACKGD MEAN"))
                bg_sum += cpl_propertylist_get_double(plists[i],
                                                      "ESO QC BACKGD MEAN");
            skip_if(0);

            for (cpl_size j = 0; j < 1 + next; j++) {
                cpl_image * img =
                    cpl_image_load(fname, CPL_TYPE_UNSPECIFIED, 0, j);
                if (img == NULL) {
                    cpl_errorstate_set(prestate);
                    cpl_msg_info(cpl_func, "No image in extension %d", (int)j);
                    continue;
                }
                cpl_image_reject_value(img, CPL_VALUE_NAN);
                cpl_imagelist_set(tmplist, img, cpl_imagelist_get_size(tmplist));
                skip_if(0);
            }
            tmp = cpl_imagelist_collapse_create(tmplist);

            cpl_imagelist_set(imagelist, tmp,
                              cpl_imagelist_get_size(imagelist));
            cpl_imagelist_delete(tmplist);
        }
        combinedpair =
            visir_img_recombine_list(RECIPE_STRING, parlist, imagelist,
                                     plists, CPL_GEOM_FIRST, &drop_wcs);


        cpl_propertylist_append_double(qclist, "ESO QC BACKGD MEAN",
                                       bg_sum / nframes);
        cpl_imagelist_delete(imagelist);
        cpl_free(plists);
        bug_if(0);

        /* FIXME: make use of multiple skyframes */
        {
            cpl_frame * frame = irplib_framelist_get(skyframes, 0);
            const char * fname = cpl_frame_get_filename(frame);
            imhcycle  = cpl_image_load(fname, CPL_TYPE_UNSPECIFIED, 0, 0);//1);
            cpl_image_reject_value(imhcycle, CPL_VALUE_NAN);
            skip_if(imhcycle == NULL);
        }


    } else {
        bug_if (input_mode & IS_PREPROCESSED);

        combinedpair = visir_img_recombine(RECIPE_STRING, parlist, rawframes,
                                           badpix, flat, CPL_GEOM_FIRST,
                                           &drop_wcs, !spc_config.do_fixcombi,
                                           wlen, resol);

        /* Get the hcycle image from the reference file */
        hcycle = visir_load_hcycle(rawframes, 0);
        skip_if (0);

        imhcycle = cpl_imagelist_unset(hcycle, 0);

        skip_if (visir_spc_det_fix(&imhcycle, 1, CPL_FALSE,
                                   wlen, resol,
                                   spc_config.phi,
                                   spc_config.ksi,
                                   spc_config.eps,
                                   spc_config.delta,
                                   spc_config.plot));

        skip_if(visir_qc_append_background(qclist, rawframes, 0, 0));
    }

    error_if (combinedpair == NULL, cpl_error_get_code(),
              "Could not combine the input frames (%s)", rawtag);

    cpl_image_delete(combinedpair[1]);
    combined = cpl_image_cast(combinedpair[0], CPL_TYPE_DOUBLE);
    cpl_image_delete(combinedpair[0]);
    cpl_free(combinedpair);

    bug_if (0);

    if (!(input_mode & IS_PREPROCESSED)) {
        /* Convert the combined image
         * unpreprocessed mode only works with drs data */
        flipped = visir_spc_flip(combined, wlen, resol, VISIR_DATA_CUBE2);
        skip_if (0);
        cpl_image_delete(combined);
        combined = flipped;
        flipped = NULL;

        /* Convert the half cycle image */
        flipped = visir_spc_flip(imhcycle, wlen, resol, VISIR_DATA_CUBE2);
        skip_if (0);
        cpl_image_delete(imhcycle);
        imhcycle = flipped;
        flipped = NULL;
    }

    skip_if (spc_config.do_fixcombi && !(input_mode & IS_PREPROCESSED) &&
             visir_spc_det_fix(&combined, 1, CPL_TRUE, wlen, resol,
                               spc_config.phi, spc_config.ksi,
                               spc_config.eps, spc_config.delta,
                               spc_config.plot));

    cpl_msg_info(cpl_func, "input_mode %d", input_mode);
    skip_if(visir_spc_extract_order(&imhcyclenarrow, &comnarrow,
                                    combined, imhcycle, wlen, &spc_config,
                                    (input_mode & IS_ECH) == IS_ECH,
                                    visir_data_is_aqu(data_type)));

    skip_if (visir_spc_extract_wcal(comnarrow, imhcyclenarrow,
                                    wlen, slitw, temp, fwhm,
                                    resol, spc_config.orderoffset,
                                    spc_cal_lines, spc_cal_qeff,
                                    &spc_table, &weight2d, qclist,
                                    spc_config.plot, bkgcorrect,
                                    visir_data_is_aqu(data_type)));


    bug_if (visir_spectro_qc(qclist, paflist, drop_wcs, rawframes, NULL,
                             "^(" VISIR_PFITS_REGEXP_SPC_WCAL_PAF ")$"));

    if (star_cat) {
        cpl_errorstate prestate = cpl_errorstate_get();
        const cpl_propertylist * plist =
            irplib_framelist_get_propertylist_const(rawframes, 0);
        skip_if(0);

        if (!(input_mode & IS_PHOT))
            cpl_msg_info(cpl_func, "The frame set contains observation frame"
                         "(s) (%s) together with a standard star catalogue ("
                         VISIR_CALIB_STDSTAR_SPC "), attempting to perform a "
                         "photometric calibration", rawtag);

        if (visir_spc_phot_sensit(rawframes, &spc_config, plist, star_cat,
                                  &weight2d, qclist, spc_table, resol,
                                  dit_key)) {
            if (!(input_mode & IS_PHOT)) {
                irplib_error_recover(prestate, "The frame set contains "
                                     "observation frame(s) (%s) together with "
                                     "a standard star catalogue ("
                                     VISIR_CALIB_STDSTAR_SPC ") but the "
                                     "photometric calibration failed", rawtag);
            }
            skip_if(0);
        }
        else
            input_mode |= IS_PHOT;
    } else {
        bug_if(input_mode & IS_PHOT);
        /* This column is not part of the product */
        bug_if (cpl_table_erase_column(spc_table, "SPC_EMISSIVITY"));
    }
   
    irplib_framelist_empty(rawframes);

    /* Save the spectrum */
    cpl_msg_info(cpl_func, "Saving the produced spectrum");

    /* PRO.CATG */
    bug_if (cpl_propertylist_append_string(paflist, CPL_DFS_PRO_CATG,
                                           VISIR_SPC_OBS_COMBINED_PROCATG));

    if (input_mode & IS_PHOT) {
        if (input_mode & IS_ECH)
            skip_if (visir_spc_obs_save(framelist, parlist, qclist, paflist,
                                        combined, weight2d, spc_table,
                                        VISIR_SPC_PHOT_ECH_TAB_PROCATG));
        else
            skip_if (visir_spc_obs_save(framelist, parlist, qclist, paflist,
                                        combined, weight2d, spc_table,
                                        VISIR_SPC_PHOT_TAB_PROCATG));
    }
    else {
        if (input_mode & IS_ECH)
            skip_if (visir_spc_obs_save(framelist, parlist, qclist, paflist,
                                        combined, weight2d, spc_table,
                                        VISIR_SPC_OBS_ECH_TAB_PROCATG));
        else
            skip_if (visir_spc_obs_save(framelist, parlist, qclist, paflist,
                                        combined, weight2d, spc_table,
                                        VISIR_SPC_OBS_TAB_PROCATG));
    }

    end_skip;

    cpl_propertylist_delete(qclist);
    cpl_propertylist_delete(paflist);
    irplib_framelist_delete(allframes);
    irplib_framelist_delete(rawframes);
    irplib_framelist_delete(skyframes);
    cpl_image_delete(flipped);
    cpl_image_delete(combined);
    cpl_image_delete(comnarrow);
    cpl_image_delete(imhcycle);
    cpl_image_delete(imhcyclenarrow);
    cpl_table_delete(spc_table);
    cpl_image_delete(weight2d);
    cpl_imagelist_delete(hcycle);

    return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Save the visir_spc_obs recipe products on disk
  @param    set         The input frame set
  @param    parlist     The input list of parameters
  @param    qclist      List of QC parameters
  @param    paflist     List of PAF parameters
  @param    combined    The combined image produced
  @param    weight2d    2D weights image
  @param    table       The spectral fields
  @param    tab_procatg Table procatg
  @param    com_procatg Combined procatg
  @param    wgt_procatg Weight procatg
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code visir_spc_obs_save(cpl_frameset            * set,
                                         const cpl_parameterlist * parlist,
                                         cpl_propertylist  * qclist,
                                         const cpl_propertylist  * paflist,
                                         const cpl_image         * combined,
                                         const cpl_image         * weight2d,
                                         const cpl_table         * table,
                                         const char              * tab_procatg)
{
    bug_if (0);

    /* THE TABLE */
    cpl_propertylist * plist = cpl_propertylist_new();
    cpl_propertylist_update_string(plist, "EXTNAME", "TAB_SPECTRUM");
    skip_if (irplib_dfs_save_table(set, parlist, set, table, plist,
                                   RECIPE_SAVE_STRING, tab_procatg,
                                   qclist, NULL, visir_pipe_id,
                                   RECIPE_SAVE_STRING "_tab" CPL_DFS_FITS));
    /* save in single file with extensions for consistency with imaging */
    cpl_propertylist_update_string(plist, "EXTNAME", "IMG_COMBINED");
    skip_if(cpl_image_save(combined, RECIPE_SAVE_STRING "_tab" CPL_DFS_FITS,
                           CPL_TYPE_FLOAT, plist, CPL_IO_EXTEND));
    cpl_propertylist_update_string(plist, "EXTNAME", "IMG_WEIGHT");
    skip_if(cpl_image_save(weight2d, RECIPE_SAVE_STRING "_tab" CPL_DFS_FITS,
                           CPL_TYPE_FLOAT, plist, CPL_IO_EXTEND));
    cpl_propertylist_delete(plist);

#ifdef VISIR_SAVE_PAF
    /* THE PAF FILE FOR QC PARAMETERS */

    skip_if (cpl_dfs_save_paf("VISIR", RECIPE_SAVE_STRING, paflist,
                             RECIPE_SAVE_STRING CPL_DFS_PAF));
#else
    bug_if(paflist == NULL);
#endif

    end_skip;

    return cpl_error_get_code();

}
