/*                                                                            *
 *   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 Frsanklin St, Fifth Floor, Boston, MA  02111-1307  USA     *
 *                                                                           */
/*
 * $Author: amodigli $
 * $Date: 2013-07-22 07:28:25 $
 * $Revision: 1.107 $
 * $Name: not supported by cvs2svn $
 *
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*---------------------------------------------------------------------------*/
/**
 * @defgroup flames_cal_prep_sff_ofpos  Recipe: Order Position
 *
 * This recipe determines the echelle order locations.
 * See man-page for details.
 */
/*---------------------------------------------------------------------------*/

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

#include <flames_cal_prep_sff_ofpos_impl.h>
#include <uves_corrbadpix.h>
#include <flames_ordpos.h>
#include <flames_crea_bp_ima.h>
#include <flames_fastprepfibreff.h>
#include <flames_prepfibreff.h>
#include <flames_mainoptFF.h>
#include <flames_mainstandFF.h>
#include <flames_mainoptquickFF.h>
#include <flames_mainstandquickFF.h>
#include <flames_add_extra_des.h>
#include <flames_create_full_ordertable.h>
#include <flames_create_backtable.h>
#include <flames_preppa_impl.h>
#include <flames_prepslitff.h>
#include <flames_midas_def.h>
#include <flames_dfs.h>
#include <flames_utils.h>
#include <flames_utils_science.h>
#include <flames_def_drs_par.h>
#include <flames.h>

#include <uves_recipe.h>
#include <uves_utils.h>
#include <uves_utils_wrappers.h>
#include <uves_parameters.h>
#include <uves_qclog.h>
#include <uves_dump.h>
#include <uves_pfits.h>
#include <uves_dfs.h>
#include <uves_chip.h>
#include <uves_error.h>

#include <cpl.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
/*-----------------------------------------------------------------------------
 Functions prototypes
 ----------------------------------------------------------------------------*/
static const char *
det_fibre_order_pos(const cpl_frame *parOdd, const cpl_table* ext_odd,
    const cpl_frame *parEven, const cpl_table* ext_even,
    const cpl_frame *parAll, const cpl_table* ext_all, const cpl_frame *parBias,
    const cpl_frame *parOrdTra, const cpl_frame *ord_gue_tab,
    enum uves_chip chip, int binx, int biny, double *t_ordpos,
    double *t_preppa1, char bias_method, int bias_value, bool DRS_USE_ORDEF,
    int sat_thr, const char *filt_sw, char prefid, int DEFPOL[2],
    cpl_frameset **infibre, cpl_frame **bkg_frame, cpl_frame **rof_frame);

static void
prefibre(const cpl_frameset *infibre, const cpl_frameset *outslitff,
    const cpl_frame *parOdd, const cpl_table* ext_odd, const cpl_frame *parEven,
    const cpl_table* ext_even, const cpl_frame *parAll,
    const cpl_table* ext_all, const cpl_frame *parBias,
    const cpl_frame *bkg_frm, const cpl_frame *rof_frm, double *FRM_YSHIFT,
    const char *method, char bias_method, int bias_value, char prefid,
    int sat_thr, enum uves_chip chip, int binx, int biny, const char *filt_sw,
    double *t_prep_fibre, double *t_prepnorm);

static void
prepslit(const cpl_frame *all, const cpl_frame *master_bias,
    const cpl_frameset *msflat, char bias_method, int bias_value,
    const int save_flat_size, char prefid, int sat_thr, enum uves_chip chip,
    int binx, int biny, const char *filt_sw, double *t_preppa2,
    cpl_frameset **outslitff);

static void
qc(const cpl_frame *parAll, const cpl_frame *ord_gue_tab, char prefid,
    enum uves_chip chip, double FRM_YSHIFT[], double Y_SELF_SHF[],
    cpl_table *qclog);

static void
flames_prep_fibre(const cpl_frameset *INFIBREFFCAT,
    const cpl_frameset *SLITFFCAT, const char *BACKTABLE, const char *MYORDER,
    double SIGMA, cpl_frameset **OUTFIBREFFCAT, const char *BASENAME,
    const char *method);

static int
flames_cal_prep_sff_ofpos_define_parameters(cpl_parameterlist *parameters);

/**@{*/
/*-----------------------------------------------------------------------------
 Recipe standard code
 ----------------------------------------------------------------------------*/
#define cpl_plugin_get_info flames_cal_prep_sff_ofpos_get_info
UVES_RECIPE_DEFINE(
    flames_cal_prep_sff_ofpos,
    FLAMES_CAL_PREP_SFF_OFPOS_DOM,
    flames_cal_prep_sff_ofpos_define_parameters,
    "Jonas M. Larsen",
    "cpl@eso.org",
    "Determines order and fibre postions",
    "Fibre & order position determination procedure:\n"
    "You should feed the recipe with:\n"
    "- even, odd, all fibre images (" FLAMES_FIB_FF_EVEN ", " FLAMES_FIB_FF_ODD ", " FLAMES_FIB_FF_ALL ")\n"
    "- single fibre images, FIB_ORDEF_(REDL|REDU)\n"
    "- master bias frames, MASTER_BIAS_(REDL|REDU)\n"
    "- master slit flatfield frames, MASTER_SFLAT_(REDL|REDU)\n"
    "- order guess tables, FIB_ORD_GUE_(REDL|REDU).\n"
    "\n"
    "Products are:\n"
    "- Fibre order table (FIB_ORDEF_TABLE_chip)\n"
    "- All fibre info table (FIB_FF_ALL_INFO_TAB)\n"
    "- Odd fibre info table (FIB_FF_ODD_INFO_TAB)\n"
    "- Even fibre info table (FIB_FF_EVEN_INFO_TAB)\n"
    "- Slit flatfield common (SLIT_FF_COM_chip)\n"
    "- Slit flatfield norm (SLIT_FF_NOR_chip)\n"
    "- Slit flatfield data (SLIT_FF_DTC_chip)\n"
    "- Slit flatfield sigma (SLIT_FF_SGC_chip)\n"
    "- Slit flatfield bad pixel (SLIT_FF_BPC_chip)\n"
    "- Slit flatfield boundary (SLIT_FF_BNC_chip)\n"
    "- Fibre flatfield common (FIB_FF_COM_chip)\n"
    "- Fibre flatfield norm (FIB_FF_NOR_chip)\n"
    "- Fibre flatfield norm sigma (FIB_FF_NSG_chip)\n"
    "chip=REDL and REDU\n");
/*-----------------------------------------------------------------------------
 Functions code
 ----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
 @brief    Setup the recipe options
 @param    parameters   the parameterlist to fill
 @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
int flames_cal_prep_sff_ofpos_define_parameters(cpl_parameterlist *parameters) {
  const char *subcontext = NULL;
  const char *recipe_id = make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID);

  /*****************
   *    General    *
   *****************/

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

  uves_par_new_enum("ext_method", CPL_TYPE_STRING, "Extraction method", "opt",
      4,
      //"std", "opt","fst","fop","qst","qop");
      "std", "opt", "fst", "fop");

  /* More ext. methods?
   5,
   "opt", "sta", "fop", "fst", "qopt");
   */

  uves_par_new_enum("bias_method", CPL_TYPE_STRING,
      "Bias subtraction method, M for master bias frame, "
      "N for no bias subtraction, V to subtract a constant bias "
      "level defined by the parameter bias_value", "M", 3, "M", "V", "N");

  uves_par_new_value("bias_value", CPL_TYPE_INT,
      "Bias value (only if bias_method = V)", 200);

  uves_par_new_enum("filter_switch", CPL_TYPE_STRING, "Filter switch", "none",
      2, "none", "median");

  uves_par_new_value("sat_thr", CPL_TYPE_INT, "Saturation threshold", 55000);

  uves_par_new_value("fileprep", CPL_TYPE_BOOL,
      "Slitff* and Fibreff* file preparation. "
      "If fast extraction method is used it should be set to FALSE", true);

  uves_par_new_value("cubify", CPL_TYPE_BOOL, "Cubify switch", true);

  uves_par_new_value("save_flat_size", CPL_TYPE_INT,
      "To be sure to use the flat part of a slit flats"
      "one may need to subtract this bit. The default value -1, "
      "is used for automatic setting: if WCEN=520 save_flat_size=0, "
      "else save_flat_size=2. Values explicitly set by user overwrite this rule.",
      -1);

  uves_par_new_value("clean_tmp_products",
             CPL_TYPE_BOOL,
             "Input data format",
             FALSE);
  return (cpl_error_get_code() != CPL_ERROR_NONE);
}

/*---------------------------------------------------------------------------*/
/**
 @brief reduce data
 @param  odd          odd fibres image
 @param  even         even fibres image
 @param  all          all fibres image
 @param  master_bias  master bias image
 @param  msflat       master sflat frames
 @param  ordef        order def frame (calibration fibre)
 @param  ord_gue_tab  order guess table
 @param  FRM_YSHIFT   Frame shift along Y (Why?)
 @param  chip         CCD chip
 @param  binx         detector X binning
 @param  biny         detector Y binning
 @param  fast_sw      switch to activate fast data reduction
 @param  method       extraction method (opt/std/fst)
 @param  t_start      recipe start time
 @param  bias_method  bias subtraction method
 @param  bias_value   bias value to be subtracted if method is set to "value"
 @param  save_flat_size parameter to control extension of slit flat size declared as illuminated
 @param  DRS_USE_ORDEF parameter to use order definition frame
 @param  sat_thr      parameter to control flagging of saturated pixels
 @param  filt_sw      parameter to activate (median) frame filtering
 @param  prefid       prefix id


 @doc  -read order guess table: from it read guess order solution, COEFFI (containing polynomial X,Y degree)
 and number of orders
 -define fibres-order table
 -prepare slit flats (determine error and bad pixels)
 -prepare fibre flats (determine error and bad pixels, interpolate bad pixels, normalizes fibres)


 @return order table
 **/
/*---------------------------------------------------------------------------*/
static const char *
reduce(const cpl_frame *odd, const cpl_table* ext_odd, const cpl_frame *even,
    const cpl_table* ext_even, const cpl_frame *all, const cpl_table* ext_all,
    const cpl_frame *master_bias, const cpl_frameset *msflat,
    const cpl_frame *ordef, const cpl_frame *ord_gue_tab, double *FRM_YSHIFT,
    enum uves_chip chip, int binx, int biny, bool fast_sw, const char *method,
    time_t t_start, char bias_method, int bias_value, const int save_flat_size,
    bool DRS_USE_ORDEF, int sat_thr, const char *filt_sw, char prefid) {

  double t_ordpos = 0.;
  double t_all = 0.;
  double t_preppa1 = 0.;
  double t_prep_fibre, t_prepnorm, t_res_tuto, t_drs, t_preppa2;
  double t_res_ordpos = 0; /* Probably a bug that this is
   not used after initialization */
  int DEFPOL[2];
  int *coeffi = NULL;
  const char *order_table_filename = NULL;

  cpl_frameset *infibre = NULL;
  cpl_frameset *outslitff = NULL;
  cpl_frame *bkg_frm = NULL;
  cpl_frame *rof_frm = NULL;
  uves_propertylist *ord_gue_header = NULL;

  passure( cpl_frame_get_type(all) == CPL_FRAME_TYPE_IMAGE,
      "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(all)));

  {
    cpl_type coeffi_type;
    int coeffi_length;

    check(
        ord_gue_header = uves_propertylist_load( cpl_frame_get_filename(ord_gue_tab), 0),
        "Could not load %s header", cpl_frame_get_filename(ord_gue_tab));

    check(
        coeffi = uves_read_midas_array( ord_gue_header, "COEFFI", &coeffi_length, &coeffi_type, NULL),
        "Error reading COEFFI from %s", cpl_frame_get_filename(ord_gue_tab));

    assure(
        coeffi_type == CPL_TYPE_INT,
        CPL_ERROR_TYPE_MISMATCH,
        "Type of COEFFI is %s, int expected", uves_tostring_cpl_type(coeffi_type));
  }

  DEFPOL[0] = coeffi[5];
  DEFPOL[1] = coeffi[6];

  uves_msg_debug("DEFPOL1 = %d, DEFPOL2 = %d", DEFPOL[0], DEFPOL[1]);

  uves_msg(" --------------------------------------");
  uves_msg("From ord guess tab we get:");
  uves_msg("guess sol, polyn deg, no of orders.....");
  uves_msg(" --------------------------------------");

  /* Determines order-fibre traces table */
  uves_msg_warning("DRS_USE_ORDEF=%d parOrdTra=%p", DRS_USE_ORDEF, ordef);

  check_nomsg(
      order_table_filename = det_fibre_order_pos(odd,ext_odd, even,ext_even, all,ext_all, master_bias, ordef, ord_gue_tab, chip, binx, biny, &t_ordpos, &t_preppa1, bias_method, bias_value, DRS_USE_ORDEF, sat_thr, filt_sw, prefid, DEFPOL, &infibre, &bkg_frm, &rof_frm));

  passure( cpl_frame_get_type(all) == CPL_FRAME_TYPE_IMAGE,
      "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(all)));

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_msg_debug(
      "fast_sw = %s, method = %s", fast_sw ? "true" : "false", method);

  uves_msg(
      "odd, even, all = %s, %s, %s", cpl_frame_get_filename(odd), cpl_frame_get_filename(even), cpl_frame_get_filename(all));

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  if (fast_sw) {
    if (method[0] != 'q') /* Bug in MIDAS, method is lowercase */
    {

#if FLAMES_DEBUG
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      passure( cpl_frame_get_type(all) == CPL_FRAME_TYPE_IMAGE,
          "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(all)));

      /* Prepare slitff* data */
      check_nomsg(
          prepslit(all, master_bias, msflat, bias_method, bias_value, save_flat_size, prefid, sat_thr, chip, binx, biny, filt_sw, &t_preppa2, &outslitff));
    }

#if FLAMES_DEBUG
    system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    uves_msg(
        "odd, even, all = %s, %s, %s", cpl_frame_get_filename(odd), cpl_frame_get_filename(even), cpl_frame_get_filename(all));

    /* Prepares fibreff* data */
    check_nomsg(
        prefibre(infibre, outslitff, odd, ext_odd, even, ext_even, all, ext_all, master_bias, bkg_frm, rof_frm, FRM_YSHIFT, method, bias_method, bias_value, prefid, sat_thr, chip, binx, biny, filt_sw, &t_prep_fibre, &t_prepnorm));

    uves_free_string_const(&order_table_filename);
    order_table_filename = uves_sprintf("orf%c.fits", prefid);

#if FLAMES_DEBUG
    system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  } /* end if fast_sw */
  else {
    t_preppa2 = 0;
  }

  /* do extraction */
  t_all = difftime(time(NULL), t_start);
  t_res_tuto = t_all - t_ordpos + t_res_ordpos - t_preppa1;
  t_res_tuto = t_res_tuto - t_preppa2 - t_prep_fibre - t_prepnorm;
  t_drs = t_all - t_res_tuto;

  cleanup:
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  uves_free_frameset(&infibre);
  uves_free_frameset(&outslitff);
  uves_free_frame(&bkg_frm);
  uves_free_frame(&rof_frm);
  uves_free_int(&coeffi);
  uves_free_propertylist(&ord_gue_header);

  return order_table_filename;
}

/*----------------------------------------------------------------------------*/
/**
 @brief  start data reduction
 @param  raw_odd      odd fibres image
 @param  raw_even     even fibres image
 @param  raw_all      all fibres image
 @param  master_bias  master bias image
 @param  msflat       master sflat frames
 @param  ordef        order def frame (calibration fibre)
 @param  ord_gue_tab  order guess table
 @param  chip        CCD chip
 @param  binx         detector X binning
 @param  biny         detector Y binning
 @param  fast_sw      switch to activate fast data reduction
 @param  method       extraction method (opt/std/fst)
 @param  t_start      recipe start time
 @param  bias_method  bias subtraction method
 @param  bias_value   bias value to be subtracted if method is set to "value"
 @param  save_flat_size parameter to control extension of slit flat size declared as illuminated
 @param  DRS_USE_ORDEF parameter to use order definition frame
 @param  sat_thr      parameter to control flagging of saturated pixels
 @param  filt_sw      parameter to activate (median) frame filtering
 @param  FRM_YSHIFT   Y shift on fibre frames
 @param  Y_SELF_SHF   Y self-shift on fibre frames
 @param  qclog        table to hold quality control results

 @return order table filename
 **/
/*----------------------------------------------------------------------------*/
static const char *
start(const cpl_frame *raw_odd, cpl_table* ext_odd, const cpl_frame *raw_even,
    cpl_table* ext_even, const cpl_frame *raw_all, cpl_table* ext_all,
    const cpl_frame *master_bias, const cpl_frameset *msflat,
    const cpl_frame *ordef, const cpl_frame *ord_gue_tab, enum uves_chip chip,
    int binx, int biny, bool fast_sw, const char *method, time_t t_start,
    char bias_method, int bias_value, const int save_flat_size,
    bool DRS_USE_ORDEF, int sat_thr, const char *filt_sw, double FRM_YSHIFT[],
    double Y_SELF_SHF[], cpl_table *qclog) {

  const char *order_table_filename = NULL; /* Result */
  const char *fid = NULL;
  char prefid;
  int wc;

  check( fid = flames_get_frmid(raw_all, chip, &wc), "Could not get frame ID");

  prefid = uves_chip_tochar(chip);

  passure( cpl_frame_get_type(raw_all) == CPL_FRAME_TYPE_IMAGE,
      "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(raw_all)));

  uves_msg_warning("DRS_USE_ORDEF=%d parOrdTra=%p", DRS_USE_ORDEF, ordef);

  /* reduce data */
  check_nomsg(
      order_table_filename = reduce(raw_odd,ext_odd, raw_even,ext_even, raw_all,ext_all, master_bias, msflat, ordef, ord_gue_tab, FRM_YSHIFT, chip, binx, biny, fast_sw, method, t_start, bias_method, bias_value,save_flat_size, DRS_USE_ORDEF, sat_thr, filt_sw, prefid));

  if (fast_sw) {

    if (method[0] != 'q') {
      /* performs qc checks */
      check(
          qc(raw_all, ord_gue_tab, prefid, chip, FRM_YSHIFT, Y_SELF_SHF, qclog),
          "Could not compute QC parameters");
    }
  }

  cleanup: uves_free_string_const(&fid);
  return order_table_filename;
}

/*----------------------------------------------------------------------------*/
/**
 @brief  write order def frame
 @param  ordef_table_filename   order definition file name
 @param  frames       input frames list
 @param  parameters   input parameter list
 @param  recipe_id    recipe id
 @param  qclog        table to hold quality control results
 @param  starttime    recipe start time
 @param  raw_header   raw frame header
 @param  chip         detector chip

 @return void
 **/
/*----------------------------------------------------------------------------*/
static void write_odef(const char *ordef_table_filename, cpl_frameset *frames,
    const cpl_parameterlist *parameters, const char *recipe_id,
    cpl_table **qclog, const char *starttime,
    const uves_propertylist *raw_header, enum uves_chip chip) {
  const bool flames = true;

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  check(
      flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_TABLE, CPL_FRAME_LEVEL_INTERMEDIATE, ordef_table_filename, UVES_ORDER_TABLE(flames, chip), raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, qclog, starttime, true, 0),
      "Could not add order table %s (%s) to frameset", ordef_table_filename, UVES_ORDER_TABLE(flames, chip));

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_msg(
      "Fibre order table %s (%s) added to frameset", ordef_table_filename, UVES_ORDER_TABLE(flames, chip));

  cleanup: return;
}

/*----------------------------------------------------------------------------*/
/**
 @brief  classify slit flat field frames
 @param  base_name    base name (prefix of slit flat fields names)
 @param  qclog        table to hold quality control results
 @param  chip         detector chip
 @param  frames       input frames list
 @param  raw_header   raw frame FITS header
 @param  parameters   input parameter list
 @param  recipe_id    recipe id
 @param  starttime    recipe start time
 @param  cubify       generate products in bube format?
 @param  ordef_table_filename   order definition file name

 @return void
 **/
/*----------------------------------------------------------------------------*/

static void flames_clas_sff_fibff(const char *base_name, cpl_table **qclog,
    enum uves_chip chip, cpl_frameset *frames,
    const uves_propertylist *raw_header, const cpl_parameterlist *parameters,
    const char *recipe_id, const char *starttime, bool cubify,
    const char *ordef_table_filename)

{
  const char *product_filename = NULL;
  uves_propertylist *parSlitFFC_header = NULL;
  uves_propertylist *primary_header = NULL;
  cpl_image *image = NULL;

  bool isfib = (strcmp(base_name, "fibreff") == 0);
  const char *tag;

  char prefid = uves_chip_tochar(chip);
  int it;
  int nflats;

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  uves_free_string_const(&product_filename);
  product_filename = uves_sprintf("%s_%c_common.fits", base_name, prefid);

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_free_propertylist(&parSlitFFC_header);
  check( parSlitFFC_header = uves_propertylist_load(product_filename, 0),
      "Error reading %s", product_filename);
  check( nflats = uves_flames_pfits_get_nflats(parSlitFFC_header),
      "Error reading %s", product_filename);

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  if (isfib) {
#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
    check_nomsg(
        write_odef(ordef_table_filename, frames, parameters, recipe_id, qclog, starttime, raw_header, chip));
  }

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  tag = isfib ? FLAMES_FIB_FF_COM(chip) : FLAMES_SLIT_FF_COM(chip);

  check(
      flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, tag, raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
      "Could not add slit flatfield common %s (%s) to frameset", product_filename, tag);

  uves_msg(
      "Slit flatfield common %s (%s) added to frameset", product_filename, tag);

  uves_free_string_const(&product_filename);
  product_filename = uves_sprintf("%s_%c_norm.fits", base_name, prefid);

  tag = isfib ? FLAMES_FIB_FF_NOR(chip) : FLAMES_SLIT_FF_NOR(chip);

  check(
      flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, tag, raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
      "Could not add slit flatfield norm %s (%s) to frameset", product_filename, tag);

  uves_msg(
      "Slit flatfield norm %s (%s) added to frameset", product_filename, tag);

  if (isfib) {
    uves_free_string_const(&product_filename);
    product_filename = uves_sprintf("%s_%c_nsigma.fits", base_name, prefid);

    check(
        flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, FLAMES_FIB_FF_NSG(chip), raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
        "Could not add slit flatfield norm sigma %s (%s) to frameset", product_filename, FLAMES_FIB_FF_NSG(chip));

    uves_msg(
        "Slit flatfield norm sigma %s (%s) added to frameset", product_filename, FLAMES_FIB_FF_NSG(chip));
  }

  if (!cubify) {

    for (it = 1; it <= nflats; it++) {

      uves_free_string_const(&product_filename);
      product_filename =
          uves_sprintf("%s_%c_data0%d.fits", base_name, prefid, it);

      tag = isfib ? FLAMES_FIB_FF_DT(it, chip) : FLAMES_SLIT_FF_DT(it, chip);

      check(
          flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, tag, raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
          "Could not add %s flatfield %d data %s (%s) to frameset", isfib ? "fibre" : "slit", it, product_filename, tag);

      uves_msg(
          "%s flatfield %d data %s (%s) added to frameset", isfib ? "Fibre" : "Slit", it, product_filename, tag);

      uves_free_string_const(&product_filename);
      product_filename =
          uves_sprintf("%s_%c_sigma0%d.fits", base_name, prefid, it);

      tag = isfib ? FLAMES_FIB_FF_SG(it, chip) : FLAMES_SLIT_FF_SG(it, chip);

      check(
          flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, tag, raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
          "Could not add slit flatfield %d sigma %s (%s) to frameset", it, product_filename, tag);

      uves_msg(
          "Slit flatfield %d sigma %s (%s) added to frameset", it, product_filename, tag);

      uves_free_string_const(&product_filename);
      product_filename =
          uves_sprintf("%s_%c_badpixel0%d.fits", base_name, prefid, it);

      tag = isfib ? FLAMES_FIB_FF_BP(it, chip) : FLAMES_SLIT_FF_BP(it, chip);

      check(
          flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, tag, raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
          "Could not add slit flatfield %d badpixel %s (%s) to frameset", it, product_filename, tag);

      uves_msg(
          "Slit flatfield %d badpixel %s (%s) added to frameset", it, product_filename, tag);

      if (!isfib) {

        uves_free_string_const(&product_filename);
        product_filename =
            uves_sprintf("%s_%c_bound0%d.fits", base_name, prefid, it);

        check(
            flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_IMAGE, CPL_FRAME_LEVEL_INTERMEDIATE, product_filename, FLAMES_SLIT_FF_BN(it, chip), raw_header, parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, UVES_ALL_STATS),
            "Could not add slit flatfield %d bounds %s (%s) to frameset", it, product_filename, FLAMES_SLIT_FF_BN(it, chip));

        uves_msg(
            "Slit flatfield %d bounds %s (%s) added to frameset", it, product_filename, FLAMES_SLIT_FF_BN(it, chip));

      }
    }/* for each slit/fib flat */
  } /* if (!cubify) */
  else {
    if (isfib) {

      cpl_frameset* set_fibreff = cpl_frameset_new();
      ck0_nomsg(
          flames_my_cubify2(chip,2,&set_fibreff, frames,raw_header,parameters, recipe_id,starttime));

      //ck0_nomsg(flames_my_cubify(chip,2,&set_fibreff));
      uves_frameset_merge(frames, set_fibreff);
      uves_free_frameset(&set_fibreff);
    } else {
      cpl_frameset* set_slitff = cpl_frameset_new();
      ck0_nomsg(
          flames_my_cubify2(chip,1,&set_slitff, frames,raw_header,parameters, recipe_id,starttime));
      //ck0_nomsg(flames_my_cubify(chip,1,&set_slitff));
      uves_frameset_merge(frames, set_slitff);
      uves_free_frameset(&set_slitff);

    }

  }

  cleanup: uves_free_propertylist(&primary_header);
  uves_free_string_const(&product_filename);
  uves_free_propertylist(&parSlitFFC_header);
  uves_free_image(&image);

  return;
}

static cpl_error_code
flames_set_save_flat_size_param(const cpl_parameterlist* parameters,
                                const char *context, const char *recipe_id,
                                const char *name, const double wcen,
                                int* save_flat_size)
{

    char *fullname = NULL;
    const cpl_parameter *p = NULL;

    passure( parameters != NULL, " ");
    /* 'context' may be NULL */
    passure( recipe_id != NULL, " ");
    passure( name != NULL, " ");

    if (context != NULL ) {
        check( fullname = uves_sprintf("%s.%s.%s", context, recipe_id, name),
                        "Error getting full parameter name");
    }
    else {
        check( fullname = uves_sprintf("%s.%s", recipe_id, name),
                        "Error getting full parameter name");
    }

    p = cpl_parameterlist_find_const(parameters, fullname);
    if (cpl_parameter_get_int(p) <= 0) {
        if (fabs(wcen - 520) <= 0.1) {
            *save_flat_size = 0;
        }
        else {
            *save_flat_size = 2;
        }
    }
    cleanup:
    cpl_free(fullname);
    fullname = NULL;
    return cpl_error_get_code();
}
/*----------------------------------------------------------------------------*/
/**
 @brief    Top level function to trace all orders and fibres
 @param    parameters  recipe parameters
 @param    frames      recipe input frames
 @param  starttime    recipe start time

 MIDAS documentation:

 ORFSLFF/FLAMES in_cat out_cat ref_cat method bias_val filter_sw
 saturation_threshold sff_fibff,cubify

 in_cat:   input catalogue with odd-even-all fibre flat field data
 out_cat:  output catalogue with slitff*, fibreff*, orf table
 products

 ref_cat:  input catalogue with reference data
 method   = extraction method:
 opt: optimal
 sta: standard
 fop: fast-optimal (no shift correction+optimal)
 fst: fast-standard (no shift
 correction+standard)
 qop: quick-optimal (no use of slitff, no
 shift correction,optimal)
 qst: quick-standard (no use of slitff, no
 shift correction,standard)

 Default: DRS_EXT_MTD (opt)

 bias_val = bias value :
 M(aster): given master bias is subtracted
 <num>,  : given number is subtracted
 No      : no bias subtraction
 Default: DRS_BIAS_MTD (M)
 filter_sw  = [none]/median. Filter applied for PREPPA/FLAMES

 Default: DRS_FILT_SW (none).
 saturation_threshold = saturation threshold considered in searching for
 saturation pixels in the frame. Pixels above sat_thr
 are marked as bad. Value updated according to given
 bias value (number of median of given frame).
 Default: DRS_PTHRE_MAX (55000)

 sff_fibff      = slit-flat-field and fibre-flat-field frameset
 determination. Default: DRS_SFF_FIBFF (Y).
 PSO might decide to have it N using
 calibration data base solutions, to improve
 performance. User must keep it to Y to have
 necessary input for science reduction.

 cubify         = cubify switch. Y or N. Default is DRS_CUBIFY (N).
 Data Flow Operations finds useful to have solutions
 for slitff* frames collected in cubes. In such
 a case, this parameter have to be set to Y and

 output slitff* cubes will be cubified after
 all Flat Field extraction. Normal user is suggested to
 use option N, as otherwise would slow the data
 reduction.


 Note:
 MIDAS Keyword DRS_EXT_MTD set default extraction method
 MIDAS Keyword DRS_BIAS_MTD set default bias subtraction method
 MIDAS Keyword DRS_FILT_SW set default filter method
 MIDAS Keyword DRS_PTHRE_MTD set default saturation threshold
 MIDAS Keyword DRS_SFF_FIBFF set defauld value for SlitFF and fibreFF frames
 production
 MIDAS Keyword DRS_CUBIFY set format of data products.


 Examples:
 ORFSLFF/FLAMES in_cat out_cat ref_cat opt 10 none 60000

 ORFSLFF/FLAMES in_cat out_cat ref_cat std M median 65000


 */

/*----------------------------------------------------------------------------*/

static void flames_cal_prep_sff_ofpos_exe(cpl_frameset *frames,
    const cpl_parameterlist *parameters, const char *starttime) {
  /* Input images */
  cpl_image *raw_image_odd[] = { NULL, NULL };
  cpl_image *raw_image_all[] = { NULL, NULL };
  cpl_image *raw_image_even[] = { NULL, NULL };
  uves_propertylist *raw_header_odd[] = { NULL, NULL };
  uves_propertylist *raw_header_all[] = { NULL, NULL };
  uves_propertylist *raw_header_even[] = { NULL, NULL };
  uves_propertylist *rot_header_odd[] = { NULL, NULL };
  uves_propertylist *rot_header_all[] = { NULL, NULL };
  uves_propertylist *rot_header_even[] = { NULL, NULL };
  cpl_frame *odd = NULL;
  cpl_frame *all = NULL;
  cpl_frame *even = NULL;
  cpl_table *ext_odd[] = { NULL, NULL };
  cpl_table *ext_all[] = { NULL, NULL };
  cpl_table *ext_even[] = { NULL, NULL };
  const char *odd_x0 = NULL; /* Local filenames */
  const char *even_x0 = NULL;
  const char *all_x0 = NULL;
  const char *master_fname = NULL;

  /* Calibration */
  cpl_image* master_bias_image = NULL;
  uves_propertylist* master_bias_header = NULL;
  cpl_frame *master_bias = NULL;

  cpl_frameset *msflat = NULL;

  cpl_table* ord_gue_tab = NULL;
  uves_propertylist* ord_gue_tab_pheader = NULL; /* Primary */
  uves_propertylist* ord_gue_tab_theader = NULL; /* 1st extension */
  cpl_frame *ord_gue = NULL;

  cpl_image *ordef_image = NULL;
  uves_propertylist *ordef_header = NULL;
  cpl_frame *ordef = NULL;

  /* Output */
  cpl_table *ordef_table = NULL;
  const char *ordef_table_filename = NULL;
  uves_propertylist *primary_header = NULL;
  uves_propertylist *table_header = NULL;

  //cpl_table *xt_odd = NULL;
  //cpl_table *xt_even = NULL;
  //cpl_table *xt_all = NULL;

  const char *xt_odd_filename = NULL;
  const char *xt_even_filename = NULL;
  const char *xt_all_filename = NULL;

  /* QC */
  cpl_table *qclog[] = { NULL, NULL };

  uves_propertylist *parSlitFFC_header = NULL;
  cpl_image *image = NULL;
  const char *raw_filename_odd = "";
  const char *raw_filename_all = "";
  const char *raw_filename_even = "";

  const bool flames = true; /* FLAMES only */
  const bool blue = false; /* only RED arm data */
  enum uves_chip chip;

  const char *pid = make_str(FLAMES_CAL_PREP_SFF_OFPOS_ID);
  const char *recipe_id = pid;
  const char *method;
  const char *filter_switch, *filt_sw;
  int sat_thr;

  //  const char *badpxframe = " ";

  const char *mid2fits = DRS_MIDAS2FITS;

  bool fast_sw;
  //const char *cubify_sw = " ";
  int save_flat_size = 0;

  const char *bias_method_string;
  char bias_method;
  int bias_value; /* used iff method=V */
  bool cubify;

  double Y_SELF_SHF[MAXFIBRES];
  double FRM_YSHIFT[MAXFIBRES];
  //double FIB_YSHIFT[MAXFIBRES];
  /*
   * Use (Y) or not (N) of the ORDERDEF as part of the odd/even fibre FF
   * Keep it at Y to support SimCal mode
   *
   */
  char DRS_USE_ORDEF;
  time_t t_start;
  int binx, biny;
  const char* PROCESS_CHIP = NULL;

  const char *chip_name = "";
  const char *master_bias_filename = "";
  const char *ord_gue_tab_filename = "";
  const char *ordef_filename = "";

  char prefid = ' ';
  int raw_index = 0;
  int clean_tmp_products=0;

  check(
      uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
      "Could not read parameter");
  uves_string_toupper((char*) PROCESS_CHIP);

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "ext_method", CPL_TYPE_STRING, &method),
      "Could not read parameter");

  assure(
      strlen(method) == 3 && method[0] == tolower(method[0]) && method[1] == tolower(method[1]) && method[2] == tolower(method[2]),
      CPL_ERROR_ILLEGAL_INPUT,
      "Extraction method must be 3 letters, lowercase");

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "filter_switch", CPL_TYPE_STRING, &filter_switch),
      "Could not read parameter");

  if (strcmp(filter_switch, "none") == 0)
    filt_sw = "NONE";
  else if (strcmp(filter_switch, "median") == 0)
    filt_sw = "MEDIAN";
  else {
    assure( false, CPL_ERROR_ILLEGAL_INPUT,
        "Unrecognized filter switch: %s", filter_switch);
  }

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "sat_thr", CPL_TYPE_INT, &sat_thr),
      "Could not read parameter");

  assure( strlen(mid2fits) >= 1 && mid2fits[0] == toupper(mid2fits[0]),
      CPL_ERROR_ILLEGAL_INPUT, "Illegal string: %s", mid2fits);

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "fileprep", CPL_TYPE_BOOL, &fast_sw),
      "Could not read parameter");

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "cubify", CPL_TYPE_BOOL, &cubify),
      "Could not read parameter");

  //cubify_sw = cubify ? "y" : "n";

  {
    int i;
    for (i = 0; i < MAXFIBRES; i++)
      Y_SELF_SHF[i] = 0;
    for (i = 0; i < MAXFIBRES; i++)
      FRM_YSHIFT[i] = 0;
    /*
    for (i = 0; i < MAXFIBRES; i++)
      FIB_YSHIFT[i] = 0;
    */
  }

  t_start = time(NULL);

  /* not portable: uves_msg("start = %.2f s", t_start); */
  check(
      uves_get_parameter(parameters, NULL, recipe_id, "bias_method", CPL_TYPE_STRING, &bias_method_string),
      "Could not read parameter");

  assure(
      strlen(bias_method_string) == 1 && bias_method_string[0] == toupper(bias_method_string[0]),
      CPL_ERROR_ILLEGAL_INPUT,
      "Bias method is '%s', should be 1 uppercase character", bias_method_string);

  bias_method = bias_method_string[0];

  if (bias_method == 'V') {
    check(
        uves_get_parameter(parameters, NULL, recipe_id, "bias_value", CPL_TYPE_INT, &bias_value),
        "Could not read parameter");
  }

  check(
      uves_get_parameter(parameters, NULL, recipe_id, "save_flat_size", CPL_TYPE_INT, &save_flat_size),
      "Could not read parameter");

  check(
      flames_load_frame(frames, &raw_filename_odd, raw_image_odd, raw_header_odd, rot_header_odd, ext_odd, FLAMES_FIB_FF_ODD),
      "Error loading raw frame");

  check(
      flames_load_frame(frames, &raw_filename_even, raw_image_even, raw_header_even, rot_header_even, ext_even, FLAMES_FIB_FF_EVEN),
      "Error loading raw frame");

  check(
      flames_load_frame(frames, &raw_filename_all, raw_image_all, raw_header_all, rot_header_all, ext_all, FLAMES_FIB_FF_ALL),
      "Error loading raw frame");

  check( uves_get_parameter(parameters, NULL, recipe_id,"clean_tmp_products",
                  CPL_TYPE_BOOL, &clean_tmp_products),
                              "Could not read parameter");

  /* set flat_save_size param properly depending on WLEN */
  double wcen=0;
  wcen=(int)uves_pfits_get_gratwlen(raw_header_all[0],UVES_CHIP_REDL);
  flames_set_save_flat_size_param(parameters,NULL,recipe_id,"save_flat_size",
                                  wcen,&save_flat_size);
  //uves_msg("flat size=%d",save_flat_size);
  //exit(0);
  check_nomsg(
      uves_table_save(ext_odd[0], raw_header_odd[0], NULL, "odd.fits", CPL_IO_DEFAULT));
  check_nomsg(
      uves_table_save(ext_odd[1], raw_header_odd[0], NULL, "odda.fits", CPL_IO_DEFAULT));
  check_nomsg(
      uves_table_save(ext_even[0], raw_header_even[0], NULL, "even.fits", CPL_IO_DEFAULT));
  check_nomsg(
      uves_table_save(ext_even[1], raw_header_even[0], NULL, "evena.fits", CPL_IO_DEFAULT));
  check_nomsg(
      uves_table_save(ext_all[0], raw_header_all[0], NULL, "all.fits", CPL_IO_DEFAULT));
  check_nomsg(
      uves_table_save(ext_all[1], raw_header_all[0], NULL, "alla.fits", CPL_IO_DEFAULT));

  check( binx = uves_pfits_get_binx(raw_header_all[0]),
      "Could not read x binning factor from input header");
  check( biny = uves_pfits_get_biny(raw_header_all[0]),
      "Could not read y binning factor from input header");

  /* Loop over two chips */
  for (chip = uves_chip_get_first(blue); chip != UVES_CHIP_INVALID; chip =
      uves_chip_get_next(chip)) {

    if (strcmp(PROCESS_CHIP, "REDU") == 0) {
      chip = uves_chip_get_next(chip);
    }

    prefid = uves_chip_tochar(chip);

    raw_index = uves_chip_get_index(chip);
    uves_msg(
        "Processing %s chip in '%s'", uves_chip_tostring_upper(chip), raw_filename_odd);

    check_nomsg(
        chip_name = uves_pfits_get_chipid(raw_header_all[raw_index], chip));

    /* load calib input frames
     */

    /* Master Bias */
    if (bias_method == 'M') {
      uves_free_image(&master_bias_image);
      uves_free_propertylist(&master_bias_header);
      check(
          uves_load_mbias(frames, chip_name, &master_bias_filename, &master_bias_image, &master_bias_header, chip),
          "Error loading master bias");

      uves_msg_low("Using master bias %s", master_bias_filename);
    } else {
      master_bias = NULL;
    }

    /* MASTER_SFLAT_x */
    {
      cpl_frame *f;
      int i = 0;

      msflat = cpl_frameset_new();
      int sz=cpl_frameset_get_size(frames);
      for (i=0; i< sz; i++) {
          f=cpl_frameset_get_frame(frames,i);
        if (strcmp(cpl_frame_get_tag(f), UVES_MASTER_SFLAT(chip)) == 0) {
          cpl_frame *msflat_local;

          /* Here it is important that the local filename is the same as the
           original filename. That is because the filename is also contained
           in HISTORY BADPXFRAME descriptor, which will break otherwise */

          /* Move to end of string, move back to first /
           or to beginning of string */
          const char *master_sflat_filename_local = cpl_frame_get_filename(f)
              + strlen(cpl_frame_get_filename(f)) - 1;

          static const bool reset_crval = true;

          while (master_sflat_filename_local > cpl_frame_get_filename(f)
              && *(master_sflat_filename_local - 1) != '/') {
            /* UNIX style directory separator here */
            master_sflat_filename_local -= 1;
          }

          cpl_frame_set_type(f, CPL_FRAME_TYPE_IMAGE);

          check_nomsg(
              msflat_local = flames_image_duplicate(master_sflat_filename_local, f, false, reset_crval));

          cpl_frameset_insert(msflat, msflat_local);
        }
      }

      assure( cpl_frameset_find(msflat, UVES_MASTER_SFLAT(chip)) != NULL,
          CPL_ERROR_DATA_NOT_FOUND, "Missing %s", UVES_MASTER_SFLAT(chip));

      uves_msg_low(
          "Found %" CPL_SIZE_FORMAT " master sflat frames", cpl_frameset_get_size(msflat));
    }

    /* FIB_ORD_GUE_x */
    uves_free_table(&ord_gue_tab);
    uves_free_propertylist(&ord_gue_tab_pheader);
    uves_free_propertylist(&ord_gue_tab_theader);
    if (cpl_frameset_find(frames, UVES_ORD_TAB(flames,chip)) != NULL) {
      check(
          flames_load_table(frames, &ord_gue_tab_filename, &ord_gue_tab, &ord_gue_tab_pheader, &ord_gue_tab_theader, UVES_GUESS_ORDER_TABLE_EXTENSION(flames,chip), UVES_ORD_TAB(flames,chip)),
          "Error loading %s", UVES_ORD_TAB(flames,chip));
    } else {
      uves_frameset_dump(frames);
      check(
          flames_load_table(frames, &ord_gue_tab_filename, &ord_gue_tab, &ord_gue_tab_pheader, &ord_gue_tab_theader, UVES_GUESS_ORDER_TABLE_EXTENSION(flames,chip), UVES_GUESS_ORDER_TABLE(flames,chip)),
          "Error loading %s", UVES_GUESS_ORDER_TABLE(flames,chip));

    }

    if (cpl_table_has_column(ord_gue_tab, "Order")) {
      check( cpl_table_name_column(ord_gue_tab, "Order", "ORDER"),
          "Error renaming column 'Order'");
    }
    if (cpl_table_has_column(ord_gue_tab, "Yfit")) {
      check( cpl_table_name_column(ord_gue_tab, "Yfit", "YFIT"),
          "Error renaming column 'Yfit'");
    }

    uves_msg_low("Using order guess table %s", ord_gue_tab_filename);

    {
      const char *dprtype;
      check( dprtype = uves_pfits_get_dpr_type(raw_header_all[raw_index]),
          "Error reading DPR TYPE");

      if (strstr(dprtype, "SimCal") != NULL) {
        DRS_USE_ORDEF = 'Y';
      } else {
        DRS_USE_ORDEF = 'N';
      }
    }

    /* FIB_ORDEF_x */
    uves_free_image(&ordef_image);
    uves_free_propertylist(&ordef_header);
    if (DRS_USE_ORDEF == 'Y') {
      check(
          flames_load_ordef(frames, chip_name, &ordef_filename, &ordef_image, &ordef_header, chip),
          "Error loading order definition image");

      uves_msg_low("Using order definition image %s", ordef_filename);
    }

    /* Save to local directory */
    uves_free_frame(&odd);
    uves_free_frame(&even);
    uves_free_frame(&all);
    uves_free_frame(&master_bias);
    uves_free_frame(&ordef);
    uves_free_frame(&ord_gue);

    uves_free_string_const(&odd_x0);
    uves_free_string_const(&even_x0);
    uves_free_string_const(&all_x0);

    odd_x0 = uves_sprintf("odd_%c.fits", uves_chip_tochar(chip));
    even_x0 = uves_sprintf("even_%c.fits", uves_chip_tochar(chip));
    all_x0 = uves_sprintf("all_%c.fits", uves_chip_tochar(chip));

    check_nomsg(
        odd = flames_new_frame(odd_x0, raw_image_odd[raw_index], raw_header_odd[raw_index]));

    check_nomsg(
        even = flames_new_frame(even_x0, raw_image_even[raw_index], raw_header_even[raw_index]));
    check_nomsg(
        all = flames_new_frame(all_x0, raw_image_all[raw_index], raw_header_all[raw_index]));
    master_fname=uves_sprintf("mbias_%s.fits", uves_chip_tostring_lower(chip));
    check_nomsg(
        master_bias = flames_new_frame(master_fname, master_bias_image, master_bias_header));
    uves_free_string_const(&master_fname);
    uves_free_image(&master_bias_image);
    uves_free_propertylist(&master_bias_header);
    uves_free_image(&(raw_image_odd[raw_index]));
    uves_free_image(&(raw_image_even[raw_index]));
    uves_free_image(&(raw_image_all[raw_index]));
    uves_free_propertylist(&(raw_header_odd[raw_index]));
    uves_free_propertylist(&(raw_header_even[raw_index]));

    passure( cpl_frame_get_type(all) == CPL_FRAME_TYPE_IMAGE,
        "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(all)));

    if (DRS_USE_ORDEF == 'Y') {
      check_nomsg(
          ordef = flames_new_frame(uves_sprintf("ordef_%s.fits", uves_chip_tostring_lower(chip)), ordef_image, ordef_header));
      cpl_frame_set_tag(ordef, uves_pfits_get_dpr_catg(ordef_header));
    }check_nomsg(
        ord_gue = flames_new_frame_table(uves_sprintf("ord_gue_%s.fits", uves_chip_tostring_lower(chip)), ord_gue_tab, ord_gue_tab_pheader, ord_gue_tab_theader));

    uves_free_table(&ord_gue_tab);
    uves_free_propertylist(&ord_gue_tab_pheader);
    uves_free_propertylist(&ord_gue_tab_theader);

    uves_qclog_delete(&qclog[0]);
    check_nomsg( qclog[0] = uves_qclog_init(raw_header_all[raw_index], chip));

    uves_msg_warning("DRS_USE_ORDEF=%c parOrdTra=%p", DRS_USE_ORDEF, ordef);

    check_nomsg(
        ordef_table_filename = start(odd,ext_odd[0], even,ext_even[0], all,ext_all[0], master_bias, msflat, ordef, ord_gue, chip, binx, biny, fast_sw, method, t_start, bias_method, bias_value,save_flat_size, DRS_USE_ORDEF, sat_thr, filt_sw, FRM_YSHIFT, Y_SELF_SHF, qclog[0]));

    if (!fast_sw) {
      write_odef(ordef_table_filename, frames, parameters, recipe_id, qclog,
          starttime, raw_header_all[raw_index], chip);
    }

    else {
      /* Not  fast_sw  */
      if (chip == UVES_CHIP_REDL) {

        xt_odd_filename = uves_sprintf("xt_odd_%c.fits", prefid);
        xt_even_filename = uves_sprintf("xt_even_%c.fits", prefid);
        xt_all_filename = uves_sprintf("xt_all_%c.fits", prefid);

        check(
            flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_TABLE, CPL_FRAME_LEVEL_INTERMEDIATE, xt_odd_filename, FIB_FF_ODD_INFO_TAB, raw_header_all[raw_index], parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, 0),
            "Could not add info table odd %s (%s) to frameset", xt_odd_filename, FIB_FF_ODD_INFO_TAB);

        uves_msg(
            "Odd fibre info table %s (%s) added to frameset", xt_odd_filename, FIB_FF_ODD_INFO_TAB);

        check(
            flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_TABLE, CPL_FRAME_LEVEL_INTERMEDIATE, xt_even_filename, FIB_FF_EVEN_INFO_TAB, raw_header_all[raw_index], parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, 0),
            "Could not add info table even %s (%s) to frameset", xt_even_filename, FIB_FF_EVEN_INFO_TAB);

        uves_msg(
            "Even fibre info table %s (%s) added to frameset", xt_even_filename, FIB_FF_EVEN_INFO_TAB);

        check(
            flames_frameset_insert( frames, CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_TYPE_TABLE, CPL_FRAME_LEVEL_INTERMEDIATE, xt_all_filename, FIB_FF_ALL_INFO_TAB, raw_header_all[raw_index], parameters, recipe_id, PACKAGE "/" PACKAGE_VERSION, NULL, starttime, false, 0),
            "Could not add info table all %s (%s) to frameset", xt_all_filename, FIB_FF_ALL_INFO_TAB);

        uves_msg(
            "All fibre info table %s (%s) added to frameset", xt_all_filename, FIB_FF_ALL_INFO_TAB);
#if FLAMES_DEBUG
        fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
      } /* if REDL */

      if (method[0] != 'q') {
#if FLAMES_DEBUG
        fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

        flames_clas_sff_fibff("slitff", qclog, chip, frames,
            raw_header_all[raw_index], parameters, recipe_id, starttime, cubify,
            ordef_table_filename);

      }

      /* Repeat, this time with fibff */
      flames_clas_sff_fibff("fibreff", qclog, chip, frames,
          raw_header_all[raw_index], parameters, recipe_id, starttime, cubify,
          ordef_table_filename);

    } /* end if not fast_sw */

    if (strcmp(PROCESS_CHIP, "REDL") == 0) {
      chip = uves_chip_get_next(chip);
    }


  } /* for each chip */

  /* clean-up temporary products */
  if(clean_tmp_products){
    for (chip = uves_chip_get_first(blue); chip != UVES_CHIP_INVALID; chip =
        uves_chip_get_next(chip)) {

      if (strcmp(PROCESS_CHIP, "REDU") == 0) {
        chip = uves_chip_get_next(chip);
      }

      flames_clean_tmp_products_ofpos(chip,cubify);
      if (strcmp(PROCESS_CHIP, "REDL") == 0) {
        chip = uves_chip_get_next(chip);
      }

    }
  }

  cleanup:
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  /* Raw */
  uves_free_image(&(raw_image_odd[0]));
  uves_free_image(&(raw_image_odd[1]));
  uves_free_image(&(raw_image_even[0]));
  uves_free_image(&(raw_image_even[1]));
  uves_free_image(&(raw_image_all[0]));
  uves_free_image(&(raw_image_all[1]));

  uves_free_string_const(&odd_x0);
  uves_free_string_const(&even_x0);
  uves_free_string_const(&all_x0);

  uves_free_propertylist(&(raw_header_odd[0]));
  uves_free_propertylist(&(raw_header_odd[1]));
  uves_free_propertylist(&(raw_header_even[0]));
  uves_free_propertylist(&(raw_header_even[1]));
  uves_free_propertylist(&(raw_header_all[0]));
  uves_free_propertylist(&(raw_header_all[1]));

  uves_free_propertylist(&(rot_header_odd[0]));
  uves_free_propertylist(&(rot_header_odd[1]));
  uves_free_propertylist(&(rot_header_even[0]));
  uves_free_propertylist(&(rot_header_even[1]));
  uves_free_propertylist(&(rot_header_all[0]));
  uves_free_propertylist(&(rot_header_all[1]));

  uves_free_table(&ext_odd[0]);
  uves_free_table(&ext_odd[1]);
  uves_free_table(&ext_even[0]);
  uves_free_table(&ext_even[1]);
  uves_free_table(&ext_all[0]);
  uves_free_table(&ext_all[1]);

  uves_free_string_const(&xt_odd_filename);
  uves_free_string_const(&xt_even_filename);
  uves_free_string_const(&xt_all_filename);

  uves_free_frame(&odd);
  uves_free_frame(&even);
  uves_free_frame(&all);

  /* Calibration */
  uves_free_image(&master_bias_image);
  uves_free_propertylist(&master_bias_header);
  uves_free_frame(&master_bias);

  uves_free_frameset(&msflat);

  uves_free_table(&ord_gue_tab);
  uves_free_propertylist(&ord_gue_tab_pheader);
  uves_free_propertylist(&ord_gue_tab_theader);
  uves_free_frame(&ord_gue);

  uves_free_image(&ordef_image);
  uves_free_propertylist(&ordef_header);
  uves_free_frame(&ordef);

  /* Products */
  uves_free_string_const(&ordef_table_filename);
  uves_free_table(&ordef_table);
  uves_free_propertylist(&primary_header);
  uves_free_propertylist(&table_header);

  uves_free_table(&qclog[0]);

  uves_free_propertylist(&parSlitFFC_header);
  uves_free_image(&image);
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  return;
}

/**
 @brief check if a frame is bias subtracted or not

 @param in      input frame

 @return true/false according to test result
 */
static bool flames_is_bias_subtracted(const cpl_frame* in) {

  cpl_propertylist* plist = NULL;
  const char* name = NULL;
  int i = 0;
  char pcatg[40];
  const char* value = NULL;
  bool result = false;

  check_nomsg(name=cpl_frame_get_filename(in));
  check_nomsg(plist=cpl_propertylist_load(name,0));

  for (i = 0; i < 10; i++) {
    sprintf(pcatg, "%s%d%s", "ESO PRO REC1 CAL", i + 1, " CATG");
    if (cpl_propertylist_has(plist, pcatg)) {
      check_nomsg(value=cpl_propertylist_get_string(plist,pcatg));
      if (strncmp(value, "MASTER_BIAS_RED", 15) == 0) {
        result = true;
      }
    }
  }

  cleanup:

  cpl_propertylist_delete(plist);

  return result;

}

/**
 @brief      !prepares data for order fibre definition

 @param parOdd        odd frame image
 @param parEven       even frame image
 @param parAll        all frame image
 @param parBias       mbias frame image
 @param parOrdTra     order tracing frame
 @param  ord_gue_tab  order guess table
 @param  chip        CCD chip
 @param  binx         detector X binning
 @param  biny         detector Y binning
 @param  t_ordpos     start time order position definition recipe
 @param  t_preppa1    start time 1 frame preparation recipe
 @param  bias_method  bias subtraction method
 @param  bias_value   bias value to be subtracted if method is set to "value"
 @param  DRS_USE_ORDEF parameter to use order definition frame
 @param  sat_thr      parameter to control flagging of saturated pixels
 @param  filt_sw      parameter to activate (median) frame filtering
 @param  prefid       chip ID as char
 @param  DEFPOL       parameter holding polynomial (X,Y) degrees
 @param  infibre      input fibre flats frameset
 @param  bkg_frame    inter-order background frame
 @param  rof_frame    raw-order-fibre table frame

 @return ooutput filename
 */

static const char *
det_fibre_order_pos(const cpl_frame *parOdd, const cpl_table* ext_odd,
    const cpl_frame *parEven, const cpl_table* ext_even,
    const cpl_frame *parAll, const cpl_table* ext_all, const cpl_frame *parBias,
    const cpl_frame *parOrdTra, const cpl_frame *ord_gue_tab,
    enum uves_chip chip, int binx, int biny, double *t_ordpos,
    double *t_preppa1, char bias_method, int bias_value, bool DRS_USE_ORDEF,
    int sat_thr, const char *filt_sw, char prefid, int DEFPOL[2],
    cpl_frameset **infibre, cpl_frame **bkg_frame, cpl_frame **rof_frame) {
  cpl_frame* b_parOdd = NULL;
  cpl_frame* b_parEven = NULL;
  cpl_frame* b_parOrdTra = NULL;
  cpl_table *rof = NULL;
  cpl_table *rof_e = NULL;
  uves_propertylist *rof_header = NULL;

  const char *bp_parOdd = NULL;
  const char *bp_parEven = NULL;
  const char *bp_parOrdTra = NULL;

  uves_propertylist *header = NULL;
  int *fibre_mask = NULL;
  const char *out_filename = NULL;
  const char *bkg_tbl = NULL;

  time_t t_preppa1_time;

  int indx;
  int nmaxfib;

  uves_msg_warning("DRS_USE_ORDEF=%d parOrdTra=%p", DRS_USE_ORDEF, parOrdTra);

  int niter = 0, omin_nf = 0, omax_nf = 0, ntraces = 0, ord_min = 0,
      ord_max = 0, wlen = 0;

  double DRS_P8_OFPOS[5];

  assure_nomsg( t_preppa1 != NULL, CPL_ERROR_NULL_INPUT);
  assure_nomsg( t_ordpos != NULL, CPL_ERROR_NULL_INPUT);

  uves_msg(" -----------------------------------------");
  uves_msg("Orders and Fibres position definition.....");
  uves_msg(" -----------------------------------------");

  if (bias_method == 'N') {
    b_parOdd = flames_image_duplicate("b_", parOdd, true, false);
    b_parEven = flames_image_duplicate("b_", parEven, true, false);
    assure_mem( b_parOdd);
    assure_mem( b_parEven);

    if (DRS_USE_ORDEF && parOrdTra != NULL) {
      b_parOrdTra = flames_image_duplicate("b_", parOrdTra, true, false);
      assure_mem( b_parOrdTra);
    }
  }

  else if (bias_method == 'V') {
    b_parOdd = flames_image_subtract_scalar_create("b_", parOdd, bias_value);
    b_parEven = flames_image_subtract_scalar_create("b_", parEven, bias_value);
    if (DRS_USE_ORDEF && parOrdTra != NULL) {
      b_parOrdTra = flames_image_subtract_scalar_create("b_", parOrdTra,
          bias_value);
    }
  }

  else if (bias_method == 'M') {
    if (parBias == NULL) {
      b_parOdd = flames_image_duplicate("b_", parOdd, true, false);
      b_parEven = flames_image_duplicate("b_", parEven, true, false);
      assure_mem( b_parOdd);
      assure_mem( b_parEven);
      if (DRS_USE_ORDEF && parOrdTra != NULL) {
        b_parOrdTra = flames_image_duplicate("b_", parOrdTra, true, false);
        assure_mem( b_parOrdTra);
      }
    } else {
      b_parOdd = flames_image_subtract_create("b_", parOdd, parBias);
      b_parEven = flames_image_subtract_create("b_", parEven, parBias);
      assure_mem( b_parOdd);
      assure_mem( b_parEven);
      if (DRS_USE_ORDEF && parOrdTra != NULL) {
        //To prevent robustness problems with masking as bad pixels
        //points below a min intensity (that can be due to double bias
        // subtraction) we do not want to make bias subtraction twice
        if (flames_is_bias_subtracted(parOrdTra)) {

          check_nomsg(
              b_parOrdTra = flames_image_duplicate("b_", parOrdTra, true, false));
          check_nomsg(
              cpl_frame_set_tag(b_parOrdTra, cpl_frame_get_tag(parOrdTra)));
        } else {

          b_parOrdTra = flames_image_subtract_create("b_", parOrdTra, parBias);

        }
        assure_mem( b_parOrdTra);

      }
      /* In MIDAS: inexact median */
      sat_thr -= flames_image_get_median(parBias);
    }
  }

  else {
    uves_msg_error("Bias subtraction method %c not supported", bias_method);
  }

  *infibre = cpl_frameset_new();
  cpl_frame_set_tag(b_parOdd, "ANY"); /* need to tag before inserting in frameset */
  cpl_frame_set_tag(b_parEven, "ANY");
  cpl_frameset_insert(*infibre, cpl_frame_duplicate(b_parOdd));
  cpl_frameset_insert(*infibre, cpl_frame_duplicate(b_parEven));

  uves_msg_warning("DRS_USE_ORDEF=%d parOrdTra=%p", DRS_USE_ORDEF, parOrdTra);

  if (DRS_USE_ORDEF && parOrdTra != NULL) {
    uves_msg_warning("Insering PARORDTRACE in framest");
    cpl_frame_set_tag(b_parOrdTra, "ANY");
    cpl_frameset_insert(*infibre, cpl_frame_duplicate(b_parOrdTra));
    bp_parOrdTra =
        uves_sprintf("%s%s", "bp_", cpl_frame_get_filename(parOrdTra));
  }

  uves_msg(" ------------------------------------------------------");
  uves_msg("Preparing fibre FF frames. Chip = %c", prefid);
  uves_msg("For each frame an associated sigma and fake badpixel mask");
  uves_msg("are created");
  uves_msg(" ------------------------------------------------------");

  /*
   * the following commented out as we use a frm specific bad pix mask
   * PREPPA/FLAMES infibre_{prefid}.cat P3={filt_sw} P8={DRS_PTHRE_MIN},{DRS_PTHRE_MAX}
   *
   */
  t_preppa1_time = time(NULL);

  bp_parOdd = uves_sprintf("%s%s", "bp_", cpl_frame_get_filename(parOdd));
  bp_parEven = uves_sprintf("%s%s", "bp_", cpl_frame_get_filename(parEven));

  check(
      flames_crea_bp_ima(b_parOdd, bp_parOdd, sat_thr, chip, binx, biny,ext_odd),
      "Error creating odd frame bad pixel map");

  check(
      flames_preppa_process(b_parOdd,bp_parOdd, filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
      "Error preparing bias subtracted odd frame");

  uves_free_frame(&b_parOdd);

  check(
      flames_crea_bp_ima(b_parEven, bp_parEven, sat_thr, chip, binx, biny,ext_even),
      "Error creating even frame bad pixel map");

  check(
      flames_preppa_process(b_parEven,bp_parEven,filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
      "Error preparing bias subtracted even frame");
  uves_free_frame(&b_parEven);

  *t_preppa1 = difftime(time(NULL), t_preppa1_time);

  if (DRS_USE_ORDEF && parOrdTra != NULL) {
    check(
        flames_crea_bp_ima(b_parOrdTra, bp_parOrdTra, sat_thr, chip, binx, biny,ext_all),
        "Error creating single trace frame bad pixel map");

    check(
        flames_preppa_process(b_parOrdTra,bp_parOrdTra, filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
        "Error preparing bias subtracted single trace frame");
  }

  *t_ordpos = time(NULL);

  /* Check that 1st order has found all fibre traces:*/
  {
    int fibre_mask_length;
    int i;
    cpl_type fibre_mask_type;

    header = uves_propertylist_load(cpl_frame_get_filename(parAll), 0);

    check(
        fibre_mask = uves_read_midas_array( header, "FIBREMASK", &fibre_mask_length, &fibre_mask_type, NULL),
        "Error reading FIBREMASK");

    assure(
        fibre_mask_type == CPL_TYPE_INT,
        CPL_ERROR_TYPE_MISMATCH,
        "Type of FIBREMASK is %s, int expected", uves_tostring_cpl_type(fibre_mask_type));

    assure(
        MAXFIBRES == fibre_mask_length,
        CPL_ERROR_INCOMPATIBLE_INPUT,
        "FIBREMASK length is %d but MAXFIBRES is %d", fibre_mask_length, MAXFIBRES);

    nmaxfib = 0;
    for (i = 0; i < MAXFIBRES; i++)
      if (fibre_mask[i] == 1)
        nmaxfib += 1;

    uves_msg_debug("Max fibres: %d", nmaxfib);
  }

  {
    uves_free_propertylist(&header);
    header = uves_propertylist_load(cpl_frame_get_filename(ord_gue_tab), 0);
    wlen = uves_round_double(uves_pfits_get_gratwlen(header, chip));
  }

  uves_msg_debug("WLEN = %d", wlen);

  DRS_P8_OFPOS[1] = -.1;
  DRS_P8_OFPOS[2] = .1;
  DRS_P8_OFPOS[3] = .005;
  DRS_P8_OFPOS[4] = 1.;
  switch (wlen) {
  case 520:
    DRS_P8_OFPOS[0] = DRS_P8_OFPOS_S1_1;
    break;
  case 580:
    DRS_P8_OFPOS[0] = DRS_P8_OFPOS_S1_2;
    break;
  case 860:
    DRS_P8_OFPOS[0] = DRS_P8_OFPOS_S1_3;
    break;
  default:
    uves_msg_warning("Unrecognized wavelength = %d, "
    "using default value for DRS_P8_OFPOS", wlen);
    DRS_P8_OFPOS[0] = 0.1;
    break;
  }

  uves_msg_debug(
      "DRS_P8_OFPOS = %f, %f, %f, %f, %f", DRS_P8_OFPOS[0], DRS_P8_OFPOS[1], DRS_P8_OFPOS[2], DRS_P8_OFPOS[3], DRS_P8_OFPOS[4]);

  out_filename = uves_sprintf("rof%c.fits", prefid);

  check(
      flames_ordpos(ord_gue_tab, out_filename, *infibre, ntraces, DRS_P8_OFPOS, prefid, wlen, DEFPOL),
      "Fibre order definition failed");

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  niter = niter + 1;

  check( rof = cpl_table_load(out_filename, 1, 1),
      "Failed to load table %s", out_filename);

  ord_min = uves_round_double(cpl_table_get_column_min(rof, "ORDER"));
  ord_max = uves_round_double(cpl_table_get_column_max(rof, "ORDER"));

  assure( cpl_table_has_column(rof, "ORDER"), CPL_ERROR_DATA_NOT_FOUND,
      "Missing column ORDER");
  assure( cpl_table_has_column(rof, "FIBRE"), CPL_ERROR_DATA_NOT_FOUND,
      "Missing column FIBRE");

  omin_nf = 0;
  for (indx = 1; indx <= MAXFIBRES; indx++) {
    uves_free_table(&rof_e);
    /* Note! Columns are float but contain integer values.
     Avoid == comparison of floating point values */
    //check_nomsg( rof_e = uves_extract_table_rows(rof, "ORDER", CPL_EQUAL_TO, ord_min));
    //check_nomsg( uves_extract_table_rows_local(rof_e, "FIBRE", CPL_EQUAL_TO, indx));
    check_nomsg(
        rof_e = uves_extract_table_rows(rof, "ORDER", CPL_GREATER_THAN, ord_min-0.1));
    check_nomsg(
        uves_extract_table_rows_local (rof_e, "ORDER", CPL_LESS_THAN, ord_min+0.1));

    check_nomsg(
        uves_extract_table_rows_local(rof_e, "FIBRE", CPL_GREATER_THAN, indx-0.1));
    check_nomsg(
        uves_extract_table_rows_local(rof_e, "FIBRE", CPL_LESS_THAN, indx+0.1));

    if (cpl_table_get_nrow(rof_e) > 0) {
      omin_nf += 1;
    }
  }

  omax_nf = 0;
  for (indx = 1; indx <= MAXFIBRES; indx++) {
    uves_free_table(&rof_e);
    check_nomsg(
        rof_e = uves_extract_table_rows(rof, "ORDER", CPL_GREATER_THAN, ord_max-0.1));
    check_nomsg(
        uves_extract_table_rows_local (rof_e, "ORDER", CPL_LESS_THAN, ord_max+0.1));

    check_nomsg(
        uves_extract_table_rows_local(rof_e, "FIBRE", CPL_GREATER_THAN, indx-0.1));
    check_nomsg(
        uves_extract_table_rows_local(rof_e, "FIBRE", CPL_LESS_THAN, indx+0.1));

    if (cpl_table_get_nrow(rof_e) > 0) {
      omax_nf += 1;
    }
  }

  check_nomsg( flames_select_all(out_filename));

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_msg("No of fibre determined on ord min: %d", omin_nf);
  uves_msg("No of fibre determined on ord max: %d", omax_nf);
  uves_msg("No of fibres expected: %d", nmaxfib);

  if (omin_nf < nmaxfib || omax_nf < nmaxfib) {

    if (niter < 2) {
      const char *INORDTAB = out_filename;
      const char *OUTORDTAB = out_filename;
      /* Avoid calling the low level function with the
       same in/out file */
      const char *temp = "flames_create_full_ot.fits";
      double STEPX[1] = { 10.0 };

      check( flames_create_full_ordertable(INORDTAB, temp, STEPX),
          "Failed to create full ordertable %s", INORDTAB);

#if FLAMES_DEBUG
      system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      check_nomsg( flames_rename_table(temp, OUTORDTAB));

      /* It is not clear where flames_create_full_ordertable() gets the
       Y = 0.000 values from when the input does not contain such */
      /* As a workaround, change Y = 0.000 to Y = NULL */
      {
        int row;

        uves_free_table(&rof);
        uves_free_propertylist(&rof_header);
        check( rof = cpl_table_load(out_filename, 1, 1),
            "Failed to load table %s", out_filename);
        check_nomsg( rof_header = uves_propertylist_load(out_filename, 0));

        for (row = 0; row < cpl_table_get_nrow(rof); row++) {
          int null;
          float y = cpl_table_get_float(rof, "Y", row, &null);

          if (!null && -0.5 < y && y < 0.5) {
            cpl_table_set_invalid(rof, "Y", row);
          }
        }

        check_nomsg(
            uves_table_save(rof, rof_header, NULL, out_filename, CPL_IO_DEFAULT));

#if FLAMES_DEBUG
        system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
        fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      }

      check_nomsg( flames_sort_table_2(OUTORDTAB, "ORDER", "FIBRE"));

      uves_free_table(&rof);
      check( rof = cpl_table_load(out_filename, 1, 1),
          "Failed to load table %s", out_filename);

      ord_min = uves_round_double(cpl_table_get_column_min(rof, "ORDER"));
      ord_max = uves_round_double(cpl_table_get_column_max(rof, "ORDER"));

      omin_nf = 0;
      for (indx = 1; indx <= MAXFIBRES; indx++) {
        uves_free_table(&rof_e);
        check_nomsg(
            rof_e = uves_extract_table_rows(rof, "ORDER", CPL_GREATER_THAN, ord_min-0.1));
        check_nomsg(
            uves_extract_table_rows_local (rof_e, "ORDER", CPL_LESS_THAN, ord_min+0.1));

        check_nomsg(
            uves_extract_table_rows_local(rof_e, "FIBRE", CPL_GREATER_THAN, indx-0.1));
        check_nomsg(
            uves_extract_table_rows_local(rof_e, "FIBRE", CPL_LESS_THAN, indx+0.1));

        if (cpl_table_get_nrow(rof_e) > 0) {
          omin_nf += 1;
        }
      }

      omax_nf = 0;
      for (indx = 1; indx <= MAXFIBRES; indx++) {
        uves_free_table(&rof_e);
        check_nomsg(
            rof_e = uves_extract_table_rows(rof, "ORDER", CPL_GREATER_THAN, ord_max-0.1));
        check_nomsg(
            uves_extract_table_rows_local (rof_e, "ORDER", CPL_LESS_THAN, ord_max+0.1));

        check_nomsg(
            uves_extract_table_rows_local(rof_e, "FIBRE", CPL_GREATER_THAN, indx-0.1));
        check_nomsg(
            uves_extract_table_rows_local(rof_e, "FIBRE", CPL_LESS_THAN, indx+0.1));

        if (cpl_table_get_nrow(rof_e) > 0) {
          omax_nf += 1;
        }
      }

      check_nomsg( flames_select_all(out_filename));

#if FLAMES_DEBUG
      system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      uves_msg("After correction: No of fibre on ord min: %d", omin_nf);
      uves_msg("After correction: No of fibre on ord max: %d", omax_nf);
      uves_msg("After correction: No of fibres expected: %d", nmaxfib);

    } /* if niter < 2 */
    else if (ntraces == 1) {

      uves_extract_table_rows_local(rof, "ORDER", CPL_GREATER_THAN,
          ord_min + 0.1);
      cpl_table_subtract_scalar(rof, "ORDER", 1);

      check_nomsg( rof_header = uves_propertylist_load(out_filename, 0));
      check_nomsg(
          uves_table_save(rof, rof_header, NULL, out_filename, CPL_IO_DEFAULT));

#if FLAMES_DEBUG
      system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      {
        int tid;
        double coeffr4[1];
        int actvals, unit, null;

        assure( TCTOPN(out_filename, F_IO_MODE, &tid) == 0,
            CPL_ERROR_ILLEGAL_OUTPUT, "Failed to open table %s", out_filename);

        assure(
            SCDRDD(tid, "COEFFR", 4, 1, &actvals, coeffr4, &unit, &null) == 0,
            CPL_ERROR_ILLEGAL_OUTPUT,
            "Failed to read COEFFR[4] from %s", out_filename);

        coeffr4[0] -= 1;
        assure( SCDWRD(tid, "COEFFR", coeffr4, 4, 1, &unit),
            CPL_ERROR_ILLEGAL_OUTPUT,
            "Failed to write COEFFR[4] to %s", out_filename);

        assure( TCTCLO(tid) == 0, CPL_ERROR_ILLEGAL_OUTPUT,
            "Failed to close table %s", out_filename);

#if FLAMES_DEBUG
        system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
        fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      }
    }
  }

  *t_ordpos = difftime(time(NULL), *t_ordpos);

  {
    bkg_tbl = uves_sprintf("bkg_%c.fits", prefid);

    check(
        flames_create_backtable(out_filename, bkg_tbl, &BKG_MAX_IO_WIN, BKG_XY_WIN_SZ),
        "Failed to create background table");

#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    *rof_frame = cpl_frame_new();
    cpl_frame_set_filename(*rof_frame, out_filename);
    cpl_frame_set_type(*rof_frame, CPL_FRAME_TYPE_TABLE);

    *bkg_frame = cpl_frame_new();
    cpl_frame_set_filename(*bkg_frame, bkg_tbl);
    cpl_frame_set_type(*bkg_frame, CPL_FRAME_TYPE_TABLE);
#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    check_nomsg( flames_add_extra_des(parAll, *rof_frame));
#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
    check_nomsg( flames_add_extra_des(parAll, *bkg_frame));
#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  }

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  cleanup:
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_free_string_const(&bp_parOdd);
  uves_free_string_const(&bp_parEven);
  uves_free_string_const(&bp_parOrdTra);
  uves_free_string_const(&bkg_tbl);
  uves_free_table(&rof);
  uves_free_table(&rof_e);
  uves_free_propertylist(&rof_header);
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_free_propertylist(&header);
  cpl_free(fibre_mask);
  fibre_mask = NULL;
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  return out_filename;
}

/**
 @brief prepares fibre flat field data
 @param INFIBREFFCAT   input fibre flat field set of frames
 @param SLITFFCAT      input fibre flat field set of frames
 @param BACKTABLE      input inter-order background table
 @param MYORDER        input order table (filename)
 @param SIGMA          sigma
 @param OUTFIBREFFCAT  output fibre flat field set of frames
 @param BASENAME       base name (prefix) of fibre flats

 @return void
 */
static void flames_prep_fibre(const cpl_frameset *INFIBREFFCAT,
    const cpl_frameset *SLITFFCAT, const char *BACKTABLE, const char *MYORDER,
    double SIGMA, cpl_frameset **OUTFIBREFFCAT, const char *BASENAME,
    const char *method) {
  uves_msg("Input frames for prepfibreff");
  uves_print_cpl_frameset(INFIBREFFCAT);
  uves_msg("Background table = %s", BACKTABLE);

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  if (method[0] == 'q') {
    int OUTPUTI[80];
    assure_nomsg(
        0 == flames_fastprepfibreff( INFIBREFFCAT, OUTFIBREFFCAT, MYORDER, BASENAME, BACKTABLE, &MAXDISCARDFRACT, &MAXBACKITERS, &FRACSLICESTHRES, BKGPOL, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, &MINFIBREFRAC, OUTPUTI),
        CPL_ERROR_ILLEGAL_OUTPUT);
  } else {
    int OUTPUTI[80];

    assure_nomsg(
        0 == flames_prepfibreff( INFIBREFFCAT, OUTFIBREFFCAT, MYORDER, SLITFFCAT, BACKTABLE, &MAXSINGLEPXFRC, &MAXCLEANITERS, &FRACSLICESTHRES, &GAUSSCORRELSCL, &GAUSSCORRELWND, &GAUSSFIBRESIGMA, &GAUSSHALFWIDTH, &MINFIBREFRAC, BASENAME, &DECENTSNR, &MAXDISCARDFRACT, &MAXBACKITERS, &MAXCORRITERS, BKGPOL, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, &MAXYSHIFT, &CORRELTOL, &CORRELXSTEP, OUTPUTI),
        CPL_ERROR_ILLEGAL_OUTPUT);
  }
#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  cleanup: return;
}

/**
 @brief prepares and normalise fibre flat field data

 @param in_frm    input fibre frame
 @param in_cats1  1st input set of frames
 @param in_cats2  2nd input set of frames
 @param in_tabs1  1st input table
 @param in_tabs2  2nd input table
 @param out_tab   output table
 @param method    extraction method
 @param OUTPUTI   output recipe status (0 ok, else failure)

 @doc -read input parameters (extraction method)  and
            input data  in_frm: all fibre flat frame, bias subtracted
                        in_cat1: fibreff_chip_dataNN, fibreff_chip_norm, fibreff_chip_nsigma, fibreff_chip_common frames,
                        in_cat2: slitff_chip_dataNN, slitff_chip_norm, slitff_chip_common frames,
                        in_tab1: inter-order background table file name,
                        in_tab2: raw fibre order table file name,
                        out_tab: final fibre order table file name)
            extraction method
            perform fibre flats (opt/std/fst) extraction

 @return void
 */
static void flames_prep_norm(const cpl_frame *in_frm,
    const cpl_frameset *in_cats1, const cpl_frameset *in_cats2,
    const char *in_tabs1, const char *in_tabs2, const char *out_tab,
    const char *method, int *OUTPUTI) {
  cpl_table *IN_E_table = NULL;
  uves_propertylist *IN_E_header = NULL;

  const char *IN_A = cpl_frame_get_filename(in_frm);
  const cpl_frameset *IN_B = NULL;
  const cpl_frameset *IN_C = NULL;

  if (in_cats2 != NULL && method[0] != 'q') {
    IN_B = in_cats1;
    IN_C = in_cats2;
  } else if (in_cats2 == NULL && method[0] == 'q') {
    IN_B = in_cats1;
  } else {
    if (method[0] != 'q') {
      uves_msg("methods std/opt: 2, fibre FF catalog,slit FF catalogue");
    } else {
      uves_msg("methods quick: 1, fibre FF catalog");
    }
    assure_nomsg( false, CPL_ERROR_ILLEGAL_INPUT);
  }
  {
    const char *IN_D = in_tabs1;
    const char *IN_E = in_tabs2;
    const char *IN_F = out_tab;
    double SIGMA = DRS_K_S_THRE;

    const char mess1[80] = "I'm starting with the optimal extraction...";
    const char mess2[80] = "The sigma which will be used is %f";

    check( IN_E_table = cpl_table_load(IN_E, 1, 1),
        "Failed to load table %s", IN_E);

    check( IN_E_header = uves_propertylist_load(IN_E, 0),
        "Failed to load %s header", IN_E);

#if FLAMES_DEBUG
    system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    check( uves_table_save(IN_E_table, IN_E_header, NULL, IN_F, CPL_IO_DEFAULT),
        "Failed to save table %s", IN_F);

#if FLAMES_DEBUG
    system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    if (strcmp(method, "opt") == 0 || strcmp(method, "fop") == 0) {
      double OUTPUTD[80];

      uves_msg(mess1);
      uves_msg(mess2, SIGMA);

#if FLAMES_DEBUG
      system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
      assure_nomsg(
          0 == flames_mainoptFF( IN_A, IN_B, IN_C, IN_D, IN_E, IN_F, &DECENTSNR, &MAXDISCARDFRACT, &MAXBACKITERS, &MAXCORRITERS, &MINOPTITERSINT, &MAXOPTITERSINT, &XKILLSIZE, &YKILLSIZE, BKGPOL, BKGFITINLINE, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, &MAXYSHIFT, &CORRELTOL, &CORRELXSTEP, OUTPUTD, OUTPUTI),
          CPL_ERROR_ILLEGAL_OUTPUT);
    } else if (strcmp(method, "qop") == 0) {
      /* quicknormopti -> mainoptquickFF */
      double OUTPUTD[80];
      assure_nomsg(
          0 == flames_mainoptquickFF( IN_A, IN_B, IN_D, IN_E, IN_F, &DECENTSNR, &MAXDISCARDFRACT, &MAXBACKITERS, &MINOPTITERSINT, &MAXOPTITERSINT, &XKILLSIZE, &YKILLSIZE, BKGPOL, BKGFITINLINE, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, OUTPUTD, OUTPUTI),
          CPL_ERROR_ILLEGAL_OUTPUT);
    } else if (strcmp(method, "qst") == 0) {
      double WINDOW = DRS_EXT_W_SIZ;
      double OUTPUTD[80];
      uves_msg(mess1);
      uves_msg(mess2, SIGMA);
      assure_nomsg(
          0 == flames_mainstandquickFF( IN_A, IN_B, IN_D, IN_E, IN_F, &DECENTSNR, &MAXDISCARDFRACT, &MAXBACKITERS, BKGPOL, BKGFITINLINE, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, &WINDOW, OUTPUTD, OUTPUTI),
          CPL_ERROR_ILLEGAL_OUTPUT);
    } else if (strcmp(method, "std") == 0 || strcmp(method, "fst") == 0) {
      double WINDOW = DRS_EXT_W_SIZ;

      double OUTPUTD[80];
      uves_msg(mess1);
      uves_msg(mess2, SIGMA);

      assure_nomsg(
          0 == flames_mainstandFF( IN_A, IN_B, IN_C, IN_D, IN_E, IN_F, &DECENTSNR, &MAXDISCARDFRACT, &MAXBACKITERS, &MAXCORRITERS, BKGPOL, BKGFITINLINE, BKGFITMETHOD, BKGBADSCAN, BKGBADWIN, &BKGBADMAXFRAC, &BKGBADMAXTOT, &SIGMA, &MAXYSHIFT, &CORRELTOL, &CORRELXSTEP, &WINDOW, OUTPUTD, OUTPUTI),
          CPL_ERROR_ILLEGAL_OUTPUT);
    }

    else {
      assure( false, CPL_ERROR_ILLEGAL_INPUT,
          "Method %s not supported. Exit", method);
    }

  }

  cleanup: uves_free_table(&IN_E_table);
  uves_free_propertylist(&IN_E_header);

  return;
}

/*----------------------------------------------------------------------------*/
/**
 @brief Associate to image a table with crucial info on the fibres in proper order
 @param   in_ima      input fibre mode frame
 @param   in_tab      fibre-order table
 @param   out_tab      extracted table
 @return void
 */
/*----------------------------------------------------------------------------*/
void flames_ex_bin_tab(const cpl_frame *in_ima_frm, const cpl_frame *in_tab_frm,
    const char *out_tab) {
  uves_propertylist *header = NULL;
  const char *in_ima = cpl_frame_get_filename(in_ima_frm);
  const char *in_tab = cpl_frame_get_filename(in_tab_frm);

  const char *in_ima_x1 = NULL;
  const char *in_ima_x2 = NULL;

  char *in_ima_base = NULL;

  uves_propertylist *in_ima_header = NULL;
  uves_propertylist *in_tab_header = NULL;

  cpl_table *t = NULL;
  cpl_table *out = NULL;
  cpl_table *in_ima_x1_tab = NULL;
  cpl_table *in_ima_x2_tab = NULL;

  const int tot_fib = 9;
  double *fib_pos = NULL;
  int *fib_msk = NULL;

  int it = 0;

#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  uves_msg_debug("===================================================");
  uves_msg_debug("ima=%s tab=%s", in_ima, in_tab);
  assure(
      strlen(in_ima) >= 8 && strcmp(in_ima + strlen(in_ima) - 5, ".fits") == 0,
      CPL_ERROR_ILLEGAL_INPUT, "Input image filename %s must be > 8 chars, and "
      "extension must be .fits", in_ima);

  in_ima_base = cpl_strdup(in_ima);
  in_ima_base[strlen(in_ima_base) - (2 + strlen(".fits"))] = '\0';

  in_ima_x1 = uves_sprintf("%s.fits" , in_ima_base);
  in_ima_x2 = uves_sprintf("%sa.fits", in_ima_base);

  {
    int fib_pos_length, fib_msk_length;
    cpl_type fib_pos_type, fib_msk_type;

    check( in_tab_header = uves_propertylist_load(in_tab, 0),
        "Error loading %s header", in_tab);

    check( in_ima_header = uves_propertylist_load(in_ima, 0),
        "Error loading %s header", in_ima);

    check(
        fib_pos = uves_read_midas_array( in_tab_header, "FIBREPOS", &fib_pos_length, &fib_pos_type, NULL),
        "Error reading FIBREPOS from %s", in_tab);

    assure(
        fib_pos_type == CPL_TYPE_DOUBLE,
        CPL_ERROR_TYPE_MISMATCH,
        "%s: Type of FIBREPOS is %s, double expected", in_tab, uves_tostring_cpl_type(fib_pos_type));

    assure(
        fib_pos_length == tot_fib,
        CPL_ERROR_TYPE_MISMATCH,
        "%s FIBREPOS length is %d, %d expected", in_tab, fib_pos_length, tot_fib);

    check(
        fib_msk = uves_read_midas_array( in_ima_header, "FIBREMASK", &fib_msk_length, &fib_msk_type, NULL),
        "Error reading FIBREMASK");

    assure(
        fib_msk_type == CPL_TYPE_INT,
        CPL_ERROR_TYPE_MISMATCH,
        "%s: Type of FIBREMASK is %s, int expected", in_ima, uves_tostring_cpl_type(fib_msk_type));

    assure(
        fib_msk_length == tot_fib,
        CPL_ERROR_TYPE_MISMATCH,
        "%s FIBREMASK length is %d, %d expected", in_ima, fib_msk_length, tot_fib);
  }

  out = cpl_table_new(tot_fib);
  cpl_table_new_column(out, "FIBREPOS", CPL_TYPE_DOUBLE);
  cpl_table_new_column(out, "FIBREMASK", CPL_TYPE_INT);
  cpl_table_fill_column_window_int(out, "FIBREMASK", 0, tot_fib, 0);
  cpl_table_fill_column_window_double(out, "FIBREPOS", 0, tot_fib, 0);
  /* not yet: cpl_table_new_column(out, "FIBREORD", CPL_TYPE_INT);*/

  for (it = 1; it <= tot_fib; it++) {
    if (fib_msk[it - 1] == 1) {
      if (fabs(fib_pos[it - 1]) < 1e-5 && it <= 2) {
        cpl_table_set_double(out, "FIBREPOS", it - 1, -200);
        cpl_table_set_int(out, "FIBREMASK", it - 1, fib_msk[it - 1]);
      } else if (fabs(fib_pos[it - 1]) < 1e-5 && it >= 7) {
        cpl_table_set_double(out, "FIBREPOS", it - 1, 200);
        cpl_table_set_int(out, "FIBREMASK", it - 1, fib_msk[it - 1]);
      } else {
        cpl_table_set_double(out, "FIBREPOS", it - 1, fib_pos[it - 1]);
        cpl_table_set_int(out, "FIBREMASK", it - 1, fib_msk[it - 1]);
      }
      uves_msg_debug("FIBREMASK[%d]=%d", it-1, fib_msk[it-1]);
    }
  }

  {
    int ref_button[9] = { 0, 3, 135, 37, 169, 69, 201, 103, 235 };

    int mag_col = 1;
    int com_col = 1;
    int sel_no = 0;
    int sel_raw = 0;

    cpl_table_new_column(out, "OBJECT", CPL_TYPE_STRING);
    cpl_table_new_column(out, "RA", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "DEC", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "R", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "R_ERROR", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "THETA", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "THETA_ERROR", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "TYPE", CPL_TYPE_STRING);
    cpl_table_new_column(out, "BUTTON", CPL_TYPE_INT);
    cpl_table_new_column(out, "PRIORITY", CPL_TYPE_INT);
    cpl_table_new_column(out, "ORIENT", CPL_TYPE_DOUBLE);
    cpl_table_new_column(out, "IN_TOL", CPL_TYPE_STRING);

    cpl_table_fill_column_window_double(out, "RA", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "DEC", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "R", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "R_ERROR", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "THETA", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "THETA_ERROR", 0, tot_fib, 0);
    cpl_table_fill_column_window_int(out, "BUTTON", 0, tot_fib, 0);
    cpl_table_fill_column_window_int(out, "PRIORITY", 0, tot_fib, 0);
    cpl_table_fill_column_window_double(out, "ORIENT", 0, tot_fib, 0);

    check( header = uves_propertylist_load(in_ima_x1, 0),
        "Could not load %s header", in_ima_x1);

    if (uves_propertylist_contains(header, "MAGNITUDE")) {
      cpl_table_new_column(out, "MAGNITUDE", CPL_TYPE_DOUBLE);
      mag_col = 0;
    }
    if (uves_propertylist_contains(header, "COMMENTS")) {
      cpl_table_new_column(out, "COMMENTS", CPL_TYPE_STRING);
      com_col = 0;
    }

    check( in_ima_x1_tab = cpl_table_load(in_ima_x1, 1, 1),
        "Could not load table %s", in_ima_x1);

    for (it = 1; it <= tot_fib; it++) {

      if (fib_msk[it - 1] == 1) {

        uves_select_table_rows(in_ima_x1_tab, "BUTTON", CPL_EQUAL_TO,
            ref_button[it - 1]);

        sel_raw = 0;
        while (sel_raw < cpl_table_get_nrow(in_ima_x1_tab)
            && !cpl_table_is_selected(in_ima_x1_tab, sel_raw)) {
          sel_raw++;
        }

        /* and convert to MIDAS convention (count rows from 1) */
        sel_raw += 1;

        sel_no = cpl_table_count_selected(in_ima_x1_tab);

        if (sel_no == 1) {

          cpl_table_set_string(out, "OBJECT", it - 1,
              cpl_table_get_string(in_ima_x1_tab, "OBJECT", sel_raw - 1));
          cpl_table_set_double(out, "RA", it - 1,
              cpl_table_get_double(in_ima_x1_tab, "RA", sel_raw - 1, NULL));
          cpl_table_set_double(out, "DEC", it - 1,
              cpl_table_get_double(in_ima_x1_tab, "DEC", sel_raw - 1, NULL));
          cpl_table_set_double(out, "R", it - 1,
              cpl_table_get_double(in_ima_x1_tab, "R", sel_raw - 1, NULL));
          cpl_table_set_double(
              out,
              "R_ERROR",
              it - 1,
              cpl_table_get_double(in_ima_x1_tab, "R_ERROR", sel_raw - 1,
                  NULL));
          cpl_table_set_double(out, "THETA", it - 1,
              cpl_table_get_double(in_ima_x1_tab, "THETA", sel_raw - 1, NULL));
          cpl_table_set_double(
              out,
              "THETA_ERROR",
              it - 1,
              cpl_table_get_double(in_ima_x1_tab, "THETA_ERROR", sel_raw - 1,
                  NULL));
          cpl_table_set_string(out, "TYPE", it - 1,
              cpl_table_get_string(in_ima_x1_tab, "TYPE", sel_raw - 1));
          cpl_table_set_int(out, "BUTTON", it - 1,
              cpl_table_get_int(in_ima_x1_tab, "BUTTON", sel_raw - 1, NULL));
          cpl_table_set_int(out, "PRIORITY", it - 1,
              cpl_table_get_int(in_ima_x1_tab, "PRIORITY", sel_raw - 1, NULL));
          cpl_table_set_double(out, "ORIENT", it - 1,
              cpl_table_get_double(in_ima_x1_tab, "ORIENT", sel_raw - 1, NULL));
          cpl_table_set_string(out, "IN_TOL", it - 1,
              cpl_table_get_string(in_ima_x1_tab, "IN_TOL", sel_raw - 1));

          if (mag_col == 0) {
            cpl_table_set_double(
                out,
                "MAGNITUDE",
                it - 1,
                cpl_table_get_double(in_ima_x1_tab, "MAGNITUDE", sel_raw - 1,
                    NULL));
          }
          if (com_col == 0) {
            cpl_table_set_string(out, "COMMENTS", it - 1,
                cpl_table_get_string(in_ima_x1_tab, "COMMENTS", sel_raw - 1));
          }

        }

      }
    }

    cpl_table_new_column(out, "Slit_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "Slit_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "FPS_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "FPS_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "Retractor_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "Retractor_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "BN_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "BN_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "FBN_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "FBN_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "RP_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "RP_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "400_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "400_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "420_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "420_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "500_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "500_pt2", CPL_TYPE_STRING);
    cpl_table_new_column(out, "700_pt1", CPL_TYPE_STRING);
    cpl_table_new_column(out, "700_pt2", CPL_TYPE_STRING);

    const char *ref_rp[9] = { "   ", "3  ", "135", "37 ", "169", "69 ", "201",
        "103", "235" };

    int raw = 0; /* table row */

    check( in_ima_x2_tab = cpl_table_load(in_ima_x2, 1, 1),
        "Could not load table %s", in_ima_x2);

    if (cpl_table_has_column(in_ima_x2_tab, "_370")) {
      cpl_table_name_column(in_ima_x2_tab, "_370", "370");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_400")) {
      cpl_table_name_column(in_ima_x2_tab, "_400", "400");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_420")) {
      cpl_table_name_column(in_ima_x2_tab, "_420", "420");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_450")) {
      cpl_table_name_column(in_ima_x2_tab, "_450", "450");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_500")) {
      cpl_table_name_column(in_ima_x2_tab, "_500", "500");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_600")) {
      cpl_table_name_column(in_ima_x2_tab, "_600", "600");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_650")) {
      cpl_table_name_column(in_ima_x2_tab, "_650", "650");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_700")) {
      cpl_table_name_column(in_ima_x2_tab, "_700", "700");
    }
    if (cpl_table_has_column(in_ima_x2_tab, "_870")) {
      cpl_table_name_column(in_ima_x2_tab, "_870", "870");
    }

    assure( cpl_table_has_column(in_ima_x2_tab, "Slit"),
        CPL_ERROR_DATA_NOT_FOUND,
        "Missing column %s in table %s", "Slit", in_ima_x2);

    cpl_table_new_column(in_ima_x2_tab, "Select", CPL_TYPE_INT);

    for (raw = 0; raw < cpl_table_get_nrow(in_ima_x2_tab); raw++) {

      if (cpl_table_get_string(in_ima_x2_tab, "Slit", raw) != NULL
          && strstr(cpl_table_get_string(in_ima_x2_tab, "Slit", raw), "Uv")
              != NULL) {
        cpl_table_set_int(in_ima_x2_tab, "Select", raw, 1);
      } else {
        cpl_table_set_int(in_ima_x2_tab, "Select", raw, 0);
      }
    }

    check_nomsg(
        t = uves_extract_table_rows(in_ima_x2_tab, "Select", CPL_NOT_EQUAL_TO, 0));

    check_nomsg( cpl_table_duplicate_column(t, "RPN", t, "RP"));

    assure( cpl_table_has_column(t, "RPN"), CPL_ERROR_DATA_NOT_FOUND,
        "Missing column RPN in %s", in_ima_x2);

    assure(
        cpl_table_get_column_type(t, "RPN") == CPL_TYPE_STRING,
        CPL_ERROR_TYPE_MISMATCH,
        "%s: Column RPN must have type string. Found %s", in_ima_x2, uves_tostring_cpl_type(cpl_table_get_column_type(t, "RPN")));

    for (it = 1; it <= tot_fib; it++) {
      int i;
      sel_no = 0;

      if (fib_msk[it - 1] == 1) {

        for (i = 0; i < cpl_table_get_nrow(t); i++) {
          if (cpl_table_get_string(t, "RPN", i) != NULL
              && strcmp(cpl_table_get_string(t, "RPN", i), ref_rp[it - 1]) == 0) {
            sel_no += 1;
          }
        }

        if (sel_no == 2) {

          int raw1 = (1 - 1) * tot_fib + it;
          int raw2 = (2 - 1) * tot_fib + it;

          cpl_table_set_string(out, "Slit_pt1", it - 1,
              cpl_table_get_string(t, "Slit", raw1 - 1));
          cpl_table_set_string(out, "Slit_pt2", it - 1,
              cpl_table_get_string(t, "Slit", raw2 - 1));
          cpl_table_set_string(out, "FPS_pt1", it - 1,
              cpl_table_get_string(t, "FPS", raw1 - 1));
          cpl_table_set_string(out, "FPS_pt2", it - 1,
              cpl_table_get_string(t, "FPS", raw2 - 1));
          cpl_table_set_string(out, "Retractor_pt1", it - 1,
              cpl_table_get_string(t, "Retractor", raw1 - 1));
          cpl_table_set_string(out, "Retractor_pt2", it - 1,
              cpl_table_get_string(t, "Retractor", raw2 - 1));
          cpl_table_set_string(out, "BN_pt1", it - 1,
              cpl_table_get_string(t, "BN", raw1 - 1));
          cpl_table_set_string(out, "BN_pt2", it - 1,
              cpl_table_get_string(t, "BN", raw2 - 1));
          cpl_table_set_string(out, "FBN_pt1", it - 1,
              cpl_table_get_string(t, "FBN", raw1 - 1));
          cpl_table_set_string(out, "FBN_pt2", it - 1,
              cpl_table_get_string(t, "FBN", raw2 - 1));
          cpl_table_set_string(out, "RP_pt1", it - 1,
              cpl_table_get_string(t, "RP", raw1 - 1));
          cpl_table_set_string(out, "RP_pt2", it - 1,
              cpl_table_get_string(t, "RP", raw2 - 1));
          check_nomsg(
              cpl_table_set_string(out, "400_pt1", it-1, cpl_table_get_string(t, "400", raw1-1)));
          cpl_table_set_string(out, "400_pt2", it - 1,
              cpl_table_get_string(t, "400", raw2 - 1));
          cpl_table_set_string(out, "420_pt1", it - 1,
              cpl_table_get_string(t, "420", raw1 - 1));
          cpl_table_set_string(out, "420_pt2", it - 1,
              cpl_table_get_string(t, "420", raw2 - 1));
          cpl_table_set_string(out, "500_pt1", it - 1,
              cpl_table_get_string(t, "500", raw1 - 1));
          cpl_table_set_string(out, "500_pt2", it - 1,
              cpl_table_get_string(t, "500", raw2 - 1));
          cpl_table_set_string(out, "700_pt1", it - 1,
              cpl_table_get_string(t, "700", raw1 - 1));
          cpl_table_set_string(out, "700_pt2", it - 1,
              cpl_table_get_string(t, "700", raw2 - 1));

        }
      }
    }

    /* This MIDAS code overwrites the information of {out_tab} row 1.
     This is perhaps not what we want.
     But until it becomes clear, what we *do* want to do here,
     simply implement the same functionality.
     */

    assure( cpl_table_has_column(t, "Retractor"), CPL_ERROR_DATA_NOT_FOUND,
        "Missing column %s", "Rectractor");
    assure( cpl_table_has_column(t, "Slit"), CPL_ERROR_DATA_NOT_FOUND,
        "Missing column %s", "Slit");
    assure( cpl_table_has_column(t, "FPS"), CPL_ERROR_DATA_NOT_FOUND,
        "Missing column %s", "FPS");
    {
      int i;
      for (i = 0; i < cpl_table_get_nrow(t); i++) {

        assure( cpl_table_has_column(t, "Retractor"), CPL_ERROR_DATA_NOT_FOUND,
            "Missing column %s", "Rectractor");
        assure( cpl_table_has_column(t, "Slit"), CPL_ERROR_DATA_NOT_FOUND,
            "Missing column %s", "Slit");
        assure( cpl_table_has_column(t, "FPS"), CPL_ERROR_DATA_NOT_FOUND,
            "Missing column %s", "FPS");

        if (cpl_table_get_string(t, "Retractor", i) != NULL
            && strstr(cpl_table_get_string(t, "Retractor", i), "Calibration")
                != NULL && cpl_table_get_string(t, "Slit", i) != NULL
            && strstr(cpl_table_get_string(t, "Slit", i), "Uves1") != NULL) {

          cpl_table_set_string(out, "Slit_pt1", 0,
              cpl_table_get_string(t, "Slit", i));
          cpl_table_set_string(out, "Retractor_pt1", 0,
              cpl_table_get_string(t, "Retractor", i));
          cpl_table_set_string(out, "FPS_pt1", 0,
              cpl_table_get_string(t, "FPS", i));
        }
        if (cpl_table_get_string(t, "Retractor", i) != NULL
            && strstr(cpl_table_get_string(t, "Retractor", i), "Calibration")
                != NULL && cpl_table_get_string(t, "Slit", i) != NULL
            && strstr(cpl_table_get_string(t, "Slit", i), "Uves2") != NULL) {

          cpl_table_set_string(out, "Slit_pt2", 0,
              cpl_table_get_string(t, "Slit", i));
          cpl_table_set_string(out, "Retractor_pt2", 0,
              cpl_table_get_string(t, "Retractor", i));
          cpl_table_set_string(out, "FPS_pt2", 0,
              cpl_table_get_string(t, "FPS", i));
        }
      }
    }

    check_nomsg( uves_sort_table_1(out, "FIBREPOS", false));

    cpl_table_new_column(out, "FIBREORD", CPL_TYPE_INT);
    cpl_table_fill_column_window_int(out, "FIBREORD", 0, tot_fib, 0);

    {
      int i;
      for (i = 0; i < cpl_table_get_nrow(out); i++) {
        if (fib_msk[i] == 1) {
          cpl_table_set_int(out, "FIBREORD", i, i + 1);
        }
      }
    }

    check(uves_table_save(out, NULL, NULL, out_tab, CPL_IO_DEFAULT),
        "Error saving table to %s", out_tab);

  }

  cleanup: uves_free_propertylist(&header);
  uves_free_propertylist(&in_ima_header);
  uves_free_propertylist(&in_tab_header);
  uves_free_string(&in_ima_base);
  uves_free_string_const(&in_ima_x1);
  uves_free_string_const(&in_ima_x2);
  uves_free_table(&in_ima_x1_tab);
  uves_free_table(&in_ima_x2_tab);
  uves_free_double(&fib_pos);
  uves_free_int(&fib_msk);
  uves_free_table(&out);
  uves_free_table(&t);
}

/**
 @brief prepare fibre flat field frames
 @param  infibre      input fibre flat field frame set
 @param  outslitff    output slit fibre flat fields frame set
 @param  parOdd       odd fibres image
 @param  parEven      even fibres image
 @param  parAll       all fibres image
 @param  parBias      master bias image
 @param  bkg_frm      inter-order background frame
 @param  rof_frm      raw order fibre frame
 @param  FRM_YSHIFT   Y shift on fibre frames
 @param  method       extraction method (opt/std/fst)
 @param  bias_method  bias subtraction method
 @param  bias_value   bias value to be subtracted if method is set to "value"
 @param  prefid       prefix id
 @param  sat_thr      parameter to control flagging of saturated pixels
 @param  chip         CCD chip
 @param  binx         detector X binning
 @param  biny         detector Y binning
 @param  filt_sw      parameter to activate (median) frame filtering
 @param  t_prep_fibre recipe start time fibre preparation
 @param  t_prep_norm  recipe start time fibre normalisation

 */

static void prefibre(const cpl_frameset *infibre, const cpl_frameset *outslitff,
    const cpl_frame *parOdd, const cpl_table* ext_odd, const cpl_frame *parEven,
    const cpl_table* ext_even, const cpl_frame *parAll,
    const cpl_table* ext_all, const cpl_frame *parBias,
    const cpl_frame *bkg_frm, const cpl_frame *rof_frm, double *FRM_YSHIFT,
    const char *method, char bias_method, int bias_value, char prefid,
    int sat_thr, enum uves_chip chip, int binx, int biny, const char *filt_sw,
    double *t_prep_fibre, double *t_prepnorm) {

  //!This entry is to prepare fibreff data
  const char *drs_y_self_shfit = NULL;
  uves_propertylist *middumma_header = NULL;
  const char *bkg_tbl = cpl_frame_get_filename(bkg_frm);
  const char *rof_tbl = cpl_frame_get_filename(rof_frm);
  cpl_frameset *outfibreff = NULL;
  cpl_frame *fibreff_prefid;
  const char *fibreff_filename = NULL;
  double Y_SELF_SHF;
  cpl_frame *b_parAll = NULL;
  const char *bp_parAll = NULL;
  cpl_frame *orf = NULL; /* Output order table */
  const char *orf_filename = NULL;
  int OUTPUTI[80];
  uves_propertylist *parFibFFC_header = NULL;
  const char *parFibFFC = NULL;

  const char *xt_odd_filename = NULL;
  const char *xt_even_filename = NULL;
  const char *xt_all_filename = NULL;

  int it = 0;
  int nflats = 0;

  passure( cpl_frame_get_type(parAll) == CPL_FRAME_TYPE_IMAGE,
      "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(parAll)));

  uves_msg(" ------------------------------------------------------------");
  uves_msg("Prepare FF fibre frames");
  uves_msg("any bad pixels are filled with interpolated values,");
  uves_msg("each fibre is normalised to a 1 at each x and");
  uves_msg("the resulting frames are added to the new outcleanff.cat");
  uves_msg(" --------------------------------------------------------------");

  *t_prep_fibre = time(NULL);

  fibreff_prefid = cpl_frame_new();
  fibreff_filename = uves_sprintf("fibreff_%c.fits", prefid);
  cpl_frame_set_filename(fibreff_prefid, fibreff_filename);

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif


  if (method[0] != 'q') {

    check_nomsg(
        flames_prep_fibre( infibre, outslitff, bkg_tbl, rof_tbl, DRS_K_S_THRE, &outfibreff, fibreff_filename, method));

      printf("about to exit\n");
      //exit(0);
  } else {

    check_nomsg(
        flames_prep_fibre( infibre, NULL, bkg_tbl, rof_tbl, DRS_K_S_THRE, &outfibreff, fibreff_filename, method));
  }

#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  *t_prep_fibre = difftime(time(NULL), *t_prep_fibre);

  nflats = cpl_frameset_get_size(infibre);

  /* This Y_SELF_SHF seems unsed? */
  check( middumma_header = uves_propertylist_load("middumma.fits", 0),
      "Failed to load middumma.fits header");
  for (it = 1; it <= nflats; it++) {
    uves_free_string_const(&drs_y_self_shfit);
    drs_y_self_shfit = uves_sprintf("DRS_Y_SELF_SHF%d", it);
    if (uves_propertylist_contains(middumma_header, drs_y_self_shfit)) {
      Y_SELF_SHF = uves_propertylist_get_double(middumma_header,
          drs_y_self_shfit);
    }
  }

  if (bias_method == 'N') {
    b_parAll = flames_image_duplicate("b_", parAll, true, false);
  } else if (bias_method == 'V') {
    b_parAll = flames_image_subtract_scalar_create("b_", parAll, bias_value);
    flames_add_extra_des(parAll, b_parAll);
  } else if (bias_method == 'M') {
    b_parAll = flames_image_subtract_create("b_", parAll, parBias);
    flames_add_extra_des(parAll, b_parAll);
  } else {
    uves_msg("Bias subtraction method %c not supported", bias_method);
  }

  bp_parAll = uves_sprintf("%s%s", "bp_", cpl_frame_get_filename(parAll));

  check(
      flames_crea_bp_ima(b_parAll, bp_parAll, sat_thr, chip, binx, biny,ext_all),
      "Error creating bad pixel map");

  check(
      flames_preppa_process(b_parAll, bp_parAll, filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
      "Error preparing frame");

  *t_prepnorm = time(NULL);

  orf = cpl_frame_new();
  orf_filename = uves_sprintf("orf%c.fits", prefid);
  cpl_frame_set_filename(orf, orf_filename);
  cpl_frame_set_type(orf, CPL_FRAME_TYPE_TABLE);


  if (method[0] != 'q') {

    check_nomsg(
        flames_prep_norm(b_parAll, outfibreff, outslitff, bkg_tbl, rof_tbl, orf_filename, method, OUTPUTI));
  } else {
    check_nomsg(
        flames_prep_norm(b_parAll, outfibreff, NULL, bkg_tbl, rof_tbl, orf_filename, method, OUTPUTI));
  }


#if FLAMES_DEBUG
  system("dfits -x 0 rofl.fits | egrep -A9 \"(FIBREPOS|FIBREMASK)\"");
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  *t_prepnorm = difftime(time(NULL), *t_prepnorm);

  if (OUTPUTI[1] != 0) {
    DRS_CHI2_RED = OUTPUTI[0] * 1.0 / OUTPUTI[1];
  }

  DRS_BKG_PIX = OUTPUTI[2];

  parFibFFC = uves_sprintf("fibreff_%c_common.fits", prefid);
  check( parFibFFC_header = uves_propertylist_load(parFibFFC, 0),
      "Error reading %s", parFibFFC);
  check( nflats = uves_flames_pfits_get_nflats(parFibFFC_header),
      "Error reading %s", parFibFFC);

  uves_free_propertylist(&middumma_header);
  check( middumma_header = uves_propertylist_load("middumma.fits", 0),
      "Failed to load middumma.fits header");

  for (it = 1; it <= nflats; it++) {
    uves_free_string_const(&drs_y_self_shfit);
    drs_y_self_shfit = uves_sprintf("DRS_Y_SHIFT%d", it);
    if (uves_propertylist_contains(middumma_header, drs_y_self_shfit)) {
      FRM_YSHIFT[it - 1] = uves_propertylist_get_double(middumma_header,
          drs_y_self_shfit);
      uves_msg_debug(
          "Found %s=%f in %s", drs_y_self_shfit, FRM_YSHIFT[it-1], "middumma.fits");

    }
  }

  check_nomsg( flames_add_extra_des(parAll, orf));

  xt_odd_filename = uves_sprintf("xt_odd_%c.fits", prefid);
  xt_even_filename = uves_sprintf("xt_even_%c.fits", prefid);
  xt_all_filename = uves_sprintf("xt_all_%c.fits", prefid);

  check_nomsg( flames_ex_bin_tab(parOdd, orf, xt_odd_filename));
  check_nomsg( flames_ex_bin_tab(parEven, orf, xt_even_filename));
  check_nomsg( flames_ex_bin_tab(parAll, orf, xt_all_filename));

  cleanup: uves_free_propertylist(&middumma_header);
  uves_free_string_const(&parFibFFC);
  uves_free_propertylist(&parFibFFC_header);
  uves_free_frameset(&outfibreff);
  uves_free_frame(&fibreff_prefid);
  uves_free_frame(&b_parAll);
  uves_free_frame(&orf);
  uves_free_string_const(&fibreff_filename);
  uves_free_string_const(&drs_y_self_shfit);
  uves_free_string_const(&bp_parAll);
  uves_free_string_const(&orf_filename);
  uves_free_string_const(&xt_odd_filename);
  uves_free_string_const(&xt_even_filename);
  uves_free_string_const(&xt_all_filename);
  return;

}

/**
 @brief compute qc log
 @param parAll        input frame
 @param ord_gue_tab   order guess table
 @param prefid        CCD chip
 @param chip          CCD chip
 @param qclog         qc log table
 */

static void qc(const cpl_frame *parAll, const cpl_frame *ord_gue_tab,
    char prefid, enum uves_chip chip, double FRM_YSHIFT[], double Y_SELF_SHF[],
    cpl_table *qclog) {
  const char *qc_frm_yshift = NULL;
  const char *qc_fib_yshift = NULL;
  const char *qc_fib_drsno = NULL;
  const char *qc_fib_seq = NULL;
  const char *qc_fib_pos = NULL;
  const char *qc_fib_msk = NULL;
  const char *qc_fib_reltrans = NULL;
  const char *qc_fib_abstrans = NULL;
  const char *qc_fib_ord_residmin = NULL;
  const char *qc_fib_ord_residmax = NULL;
  const char *qc_fib_ord_residavg = NULL;
  const char *qc_fib_ord_residrms = NULL;
  const char *qc_fib_ord_npred = NULL;
  const char *qc_fib_ord_ndet = NULL;
  const char *qc_fib_ord_nposall = NULL;
  const char *qc_fib_ord_npossel = NULL;
  const char *qc_fib_ord_ordmin = NULL;
  const char *qc_fib_ord_ordmax = NULL;

  uves_propertylist *header = NULL;
  cpl_table *sel = NULL;
  cpl_imagelist *ilist = NULL;
  cpl_table *order_guess_table = NULL;

  const char *orf_filename = NULL;
  cpl_table *orf_table = NULL;

  const char *xt_all_filename = NULL;
  cpl_table *xt_all = NULL;

  int *fib_msk = NULL;
  double abs_fib_transm[MAXFIBRES];
  double rel_fib_transm[MAXFIBRES];
  int ord_fib_min[MAXFIBRES];
  int ord_fib_max[MAXFIBRES];
  int ord_fib_num[MAXFIBRES];

  double rsd_fib_min[MAXFIBRES];
  double rsd_fib_max[MAXFIBRES];
  double rsd_fib_avg[MAXFIBRES];
  double rsd_fib_rms[MAXFIBRES];
  int rsd_fib_tot[MAXFIBRES];
  int rsd_fib_sel[MAXFIBRES];
  double rsd_thres = 0.1;

  int ord_fib_seq[MAXFIBRES];
  double ord_fib_pos[MAXFIBRES];
  int nord_pred;
  int nflats = 0;
  double tot_transm = 0;
  int it = 0;
  double exp_time = 0;

  const char *parFibFFN = uves_sprintf("fibreff_%c_norm.fits", prefid);
  const char *parFibFFC = uves_sprintf("fibreff_%c_common.fits", prefid);
  header = uves_propertylist_load(parFibFFC, 0);
  nflats = uves_propertylist_get_int(header, "NFLATS");
  uves_free_propertylist(&header);
  {
    int fibre_mask_length;
    cpl_type fibre_mask_type;

    check( header = uves_propertylist_load(cpl_frame_get_filename(parAll), 0),
        "Could not load %s header", cpl_frame_get_filename(parAll));

    check(
        fib_msk = uves_read_midas_array( header, "FIBREMASK", &fibre_mask_length, &fibre_mask_type, NULL),
        "Error reading FIBREMASK");

    assure(
        fibre_mask_type == CPL_TYPE_INT,
        CPL_ERROR_TYPE_MISMATCH,
        "Type of FIBREMASK is %s, int expected", uves_tostring_cpl_type(fibre_mask_type));

    assure(
        MAXFIBRES == fibre_mask_length,
        CPL_ERROR_INCOMPATIBLE_INPUT,
        "FIBREMASK length is %d but MAXFIBRES is %d", fibre_mask_length, MAXFIBRES);
  }

  DRS_NLIT_FIBRES = 0;

  for (it = 1; it <= MAXFIBRES; it++) {

    DRS_NLIT_FIBRES += fib_msk[it - 1];

    uves_msg_debug("fibre mask(%d) = %d", it, fib_msk[it-1]);

    if (fib_msk[it - 1] == 1) {

      /* Computes total intensity in plane defined by y == fibre */
      int extension = 0;
      int nx, nz;
      int x, y, z; /* counting from 0, not FITS convention */
      int dummy;

      uves_free_imagelist(&ilist);
      check( ilist = cpl_imagelist_load(parFibFFN, CPL_TYPE_FLOAT, extension),
          "Failed to load image cube %s", parFibFFN);

      nx = cpl_image_get_size_x(cpl_imagelist_get(ilist, 0));
      //ny = cpl_image_get_size_y(cpl_imagelist_get(ilist, 0));
      nz = cpl_imagelist_get_size(ilist);

      /* typical size is 4096*9*21; therefore efficiency is not too
       important here */
      abs_fib_transm[it - 1] = 0;
      for (z = 0; z < nz; z++) {
        for (y = it - 1; y <= it - 1; y++) {
          for (x = 0; x < nx; x++) {
            abs_fib_transm[it - 1] += cpl_image_get(cpl_imagelist_get(ilist, z),
                x + 1, y + 1, &dummy);
          }
        }
      }
      tot_transm += abs_fib_transm[it - 1];

    }

  }
  check( exp_time = uves_flames_pfits_get_dit(header),
      "Error reading %s", cpl_frame_get_filename(parAll));

  /* This was a bug. outputi(1) is the column number */
  check(
      order_guess_table = cpl_table_load(cpl_frame_get_filename(ord_gue_tab), 1, 1),
      "Could not load table %s", cpl_frame_get_filename(ord_gue_tab));

  check_nomsg(
      nord_pred = cpl_table_get_column_max(order_guess_table, "ORDER") - cpl_table_get_column_min(order_guess_table, "ORDER") + 1);

  orf_filename = uves_sprintf("orf%c.fits", prefid);
  check( orf_table = cpl_table_load(orf_filename, 1, 1),
      "Could not load table %s", orf_filename);

  xt_all_filename = uves_sprintf("xt_all_%c.fits", prefid);
  check( xt_all = cpl_table_load(xt_all_filename, 1, 1),
      "Could not load table %s", xt_all_filename);

  for (it = 1; it <= MAXFIBRES; it++) {

    check_nomsg(
        ord_fib_seq[it-1] = cpl_table_get_int (xt_all, "FIBREORD", it-1, NULL));
    check_nomsg(
        ord_fib_pos[it-1] = cpl_table_get_double(xt_all, "FIBREPOS", it-1, NULL));

    if (fib_msk[it - 1] == 1) {

      rel_fib_transm[it - 1] = abs_fib_transm[it - 1] / tot_transm;
      abs_fib_transm[it - 1] = abs_fib_transm[it - 1] / exp_time;

      /* This was a bug. We don't want to accumulate the orders when calculating
       per order statistics. */
      check_nomsg(
          sel = uves_extract_table_rows(orf_table, "FIBRE", CPL_EQUAL_TO, it));

      check_nomsg( ord_fib_min[it-1] = cpl_table_get_column_min(sel, "ORDER"));
      check_nomsg( ord_fib_max[it-1] = cpl_table_get_column_max(sel, "ORDER"));
      ord_fib_num[it - 1] = ord_fib_max[it - 1] - ord_fib_min[it - 1] + 1;

      rsd_fib_tot[it - 1] = cpl_table_get_nrow(sel);

      check_nomsg(
          rsd_fib_min[it-1] = cpl_table_get_column_min(sel, "RESIDUAL"));
      check_nomsg(
          rsd_fib_max[it-1] = cpl_table_get_column_max(sel, "RESIDUAL"));
      check_nomsg(
          rsd_fib_avg[it-1] = cpl_table_get_column_mean(sel, "RESIDUAL"));
      check_nomsg(
          rsd_fib_rms[it-1] = cpl_table_get_column_stdev(sel, "RESIDUAL"));

      check_nomsg(
          uves_extract_table_rows_local(sel, "RESIDUAL", CPL_LESS_THAN, rsd_thres));
      check_nomsg( rsd_fib_sel[it-1] = cpl_table_get_nrow(sel));
      uves_free_table(&sel);

    } else { /* Fibre off */
      rel_fib_transm[it - 1] = 0;
      abs_fib_transm[it - 1] = 0;

      ord_fib_min[it - 1] = 0;
      ord_fib_max[it - 1] = 0;
      ord_fib_num[it - 1] = 0;

      rsd_fib_tot[it - 1] = 0;

      rsd_fib_min[it - 1] = 0;
      rsd_fib_max[it - 1] = 0;
      rsd_fib_avg[it - 1] = 0;
      rsd_fib_rms[it - 1] = 0;

      rsd_fib_sel[it - 1] = 0;
    }
  }

  check_nomsg(
      uves_qclog_add_string(qclog, "QC TEST1 ID", "Multi-Fibre-Order-Definition-Results", "Name of QC test", "%s"));

  check_nomsg( uves_qclog_add_common_wave(header, chip, qclog));
  uves_free_propertylist(&header);
  for (it = 1; it <= nflats; it++) {
    uves_free_string_const(&qc_frm_yshift);
    qc_frm_yshift = uves_sprintf("QC FRM%d YSHIFT", it);

    check_nomsg(
        uves_qclog_add_double(qclog, qc_frm_yshift, FRM_YSHIFT[it-1], "Y shift applied to frame", "%8.4f"));
  }

  check_nomsg(
      uves_qclog_add_double(qclog, "QC CHI2", DRS_CHI2_RED, "Chi square of solution", "%8.4f"));
  check_nomsg(
      uves_qclog_add_int(qclog, "QC BKGWINO", DRS_BKG_PIX, "No. of bkg windows", "%d"));

  for (it = 1; it <= MAXFIBRES; it++) {
    //int fi = 0;

    //fi = ord_fib_seq[it - 1];
    uves_free_string_const(&qc_fib_yshift);
    qc_fib_yshift = uves_sprintf("QC FIB%d YSHIFT", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_yshift, Y_SELF_SHF[it-1], "Y shift applied to fibre", "%8.4f"));

    uves_free_string_const(&qc_fib_drsno);
    qc_fib_drsno = uves_sprintf("QC FIB%d DRSNO", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_drsno, it, "DRS det. fibre deq. pos.", "%d"));

    uves_free_string_const(&qc_fib_seq);
    qc_fib_seq = uves_sprintf("QC FIB%d SEQ", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_seq, ord_fib_seq[it-1], "det. fibre deq. no.", "%d"));

    uves_free_string_const(&qc_fib_pos);
    qc_fib_pos = uves_sprintf("QC FIB%d POS", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_pos, ord_fib_pos[it-1], "det. fibre seq. rel. pos.", "%8.4f"));

    uves_free_string_const(&qc_fib_msk);
    qc_fib_msk = uves_sprintf("QC FIB%d MSK", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_msk, fib_msk[it-1], "DRS det. fibre mask value.", "%d"));

    uves_free_string_const(&qc_fib_reltrans);
    qc_fib_reltrans = uves_sprintf("QC FIB%d RELTRANS", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_reltrans, rel_fib_transm[it-1], "relative transmission", "%8.4f"));

    uves_free_string_const(&qc_fib_abstrans);
    qc_fib_abstrans = uves_sprintf("QC FIB%d ABSTRANS", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_abstrans, abs_fib_transm[it-1], "abs. trans. countrate", "%8.4f"));

    uves_free_string_const(&qc_fib_ord_residmin);
    qc_fib_ord_residmin = uves_sprintf("QC FIB%d ORD RESIDMIN", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_ord_residmin, rsd_fib_min[it-1], "min resid in ord def", "%8.4f"));

    uves_free_string_const(&qc_fib_ord_residmax);
    qc_fib_ord_residmax = uves_sprintf("QC FIB%d ORD RESIDMAX", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_ord_residmax, rsd_fib_max[it-1], "max resid in ord def", "%8.4f"));

    uves_free_string_const(&qc_fib_ord_residavg);
    qc_fib_ord_residavg = uves_sprintf("QC FIB%d ORD RESIDAVG", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_ord_residavg, rsd_fib_avg[it-1], "mean resid in ord def", "%8.4f"));

    uves_free_string_const(&qc_fib_ord_residrms);
    qc_fib_ord_residrms = uves_sprintf("QC FIB%d ORD RESIDRMS", it);
    check_nomsg(
        uves_qclog_add_double(qclog, qc_fib_ord_residrms, rsd_fib_rms[it-1], "sigma resid in ord def", "%8.4f"));

    uves_free_string_const(&qc_fib_ord_npred);
    qc_fib_ord_npred = uves_sprintf("QC FIB%d ORD NPRED", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_npred, nord_pred, "predicted number of orders", "%d"));

    uves_free_string_const(&qc_fib_ord_ndet);
    qc_fib_ord_ndet = uves_sprintf("QC FIB%d ORD NDET", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_ndet, ord_fib_num[it-1], "detected number of orders", "%d"));

    uves_free_string_const(&qc_fib_ord_nposall);
    qc_fib_ord_nposall = uves_sprintf("QC FIB%d ORD NPOSALL", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_nposall, rsd_fib_tot[it-1], "Number of positions found", "%d"));

    uves_free_string_const(&qc_fib_ord_npossel);
    qc_fib_ord_npossel = uves_sprintf("QC FIB%d ORD NPOSSEL", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_npossel, rsd_fib_sel[it-1], "Number of positions selected", "%d"));

    uves_free_string_const(&qc_fib_ord_ordmin);
    qc_fib_ord_ordmin = uves_sprintf("QC FIB%d ORDMIN", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_ordmin, ord_fib_min[it-1], "minimum order number detected", "%d"));

    uves_free_string_const(&qc_fib_ord_ordmax);
    qc_fib_ord_ordmax = uves_sprintf("QC FIB%d ORDMAX", it);
    check_nomsg(
        uves_qclog_add_int(qclog, qc_fib_ord_ordmax, ord_fib_max[it-1], "maximum order number detected", "%d"));

  }

  cleanup:
  uves_free_table(&sel);
  uves_free_propertylist(&header);
  uves_free_int(&fib_msk);
  uves_free_string_const(&parFibFFC);
  uves_free_string_const(&parFibFFN);
  uves_free_imagelist(&ilist);
  uves_free_table(&order_guess_table);
  uves_free_string_const(&orf_filename);
  uves_free_table(&orf_table);
  uves_free_string_const(&xt_all_filename);
  uves_free_table(&xt_all);

  uves_free_string_const(&qc_frm_yshift);
  uves_free_string_const(&qc_fib_yshift);
  uves_free_string_const(&qc_fib_drsno);
  uves_free_string_const(&qc_fib_seq);
  uves_free_string_const(&qc_fib_pos);
  uves_free_string_const(&qc_fib_msk);
  uves_free_string_const(&qc_fib_reltrans);
  uves_free_string_const(&qc_fib_abstrans);
  uves_free_string_const(&qc_fib_ord_residmin);
  uves_free_string_const(&qc_fib_ord_residmax);
  uves_free_string_const(&qc_fib_ord_residavg);
  uves_free_string_const(&qc_fib_ord_residrms);
  uves_free_string_const(&qc_fib_ord_npred);
  uves_free_string_const(&qc_fib_ord_ndet);
  uves_free_string_const(&qc_fib_ord_nposall);
  uves_free_string_const(&qc_fib_ord_npossel);
  uves_free_string_const(&qc_fib_ord_ordmin);
  uves_free_string_const(&qc_fib_ord_ordmax);
  return;
}

/**
 @brief prepare slitff

 @param  all      input all fibre flat field frame
 @param  parBias      master bias image
 @param  msflat       master slit flat
 @param  bias_method  bias subtraction method
 @param  bias_value   bias value to be subtracted if method is set to "value"
 @param  save_flat_size amount of pixels to be deduced from slit flat size measure to be sure to be in "flat" size
 @param  prefid       prefix id
 @param  sat_thr      parameter to control flagging of saturated pixels
 @param  chip         CCD chip
 @param  binx         detector X binning
 @param  biny         detector Y binning
 @param  filt_sw      parameter to activate (median) frame filtering
 @param  t_preppa2    recipe start time fibre preparation
 @param  outslitff    output set of slit flat fields
 @return void
 */

static void prepslit(const cpl_frame *all, const cpl_frame *parBias,
    const cpl_frameset *msflat, char bias_method, int bias_value,
    const int save_flat_size, char prefid, int sat_thr, enum uves_chip chip,
    int binx, int biny, const char *filt_sw, double *t_preppa2,
    cpl_frameset **outslitff) {

  //!This entry is to prepare slitff data
  cpl_frameset *bslff_prefid = cpl_frameset_new();
  const char *out_filename = uves_sprintf("rof%c.fits", prefid);
  const char *slitff = NULL;

  const cpl_frame *parSlitFF = NULL;
  cpl_frame *b_parSlitFF = NULL;
  bool must_cleanup_b_parSlitFF = true; /* ownership transfers to frameset */
  const char *bp_parSlitFF = NULL;

  const cpl_frameset *ref_cat = msflat;
#if FLAMES_DEBUG
  fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

  passure( cpl_frame_get_type(all) == CPL_FRAME_TYPE_IMAGE,
      "%s", uves_tostring_cpl_frame_type(cpl_frame_get_type(all)));
  int sz = cpl_frameset_get_size(ref_cat);
  int i=0;
  for (i= 0; i< sz ;i++) {
      parSlitFF=cpl_frameset_get_frame(ref_cat,i);
#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

    if (false) {

      /* We cannot use raw SFLAT frames, they need to be rotated first,
       so disable this section which is not used operationally anyway */

      uves_msg("Using raw Slit Flat Field");

      if (bias_method == 'N') {
        b_parSlitFF = flames_image_duplicate("b_", parSlitFF, true, false);
      } else if (bias_method == 'V') {
        b_parSlitFF = flames_image_subtract_scalar_create("b_", parSlitFF,
            bias_value);
        flames_add_extra_des(parSlitFF, b_parSlitFF);
      } else if (bias_method == 'M') {
        b_parSlitFF = flames_image_subtract_create("b_", parSlitFF, parBias);
      } else {
        uves_msg("Bias subtraction method %c not supported", bias_method);
      }

      check_nomsg(
          msffsz_flames2(b_parSlitFF, save_flat_size, out_filename, chip));

      uves_free_string_const(&bp_parSlitFF);
      bp_parSlitFF = uves_sprintf("bp_%s", cpl_frame_get_filename(parSlitFF));
#if FLAMES_DEBUG
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      check(
          flames_crea_bp_ima(b_parSlitFF, bp_parSlitFF, sat_thr, chip, binx, biny,NULL),
          "Error creating bad pixel map");

      check(
          flames_preppa_process(b_parSlitFF, bp_parSlitFF, filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
          "Error preparing bias subtracted frame");

      cpl_frameset_insert(bslff_prefid, b_parSlitFF);
      must_cleanup_b_parSlitFF = false;
    }/* if false */

    else if (strcmp(cpl_frame_get_tag(parSlitFF), UVES_MASTER_SFLAT(chip))
        == 0) {

#if FLAMES_DEBUG
      fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif

      uves_msg("Using master Slit Flat Field");
      /* Bias already subtracted here. Just duplicate frame */

      check_nomsg( b_parSlitFF = cpl_frame_duplicate(parSlitFF));

      uves_msg_warning("ROF table=%s", out_filename);
      check_nomsg(
          msffsz_flames2(b_parSlitFF, save_flat_size, out_filename, chip));

      uves_free_string_const(&bp_parSlitFF);
      bp_parSlitFF = uves_sprintf("bp_%s", cpl_frame_get_filename(parSlitFF));

      check(
          flames_crea_bp_ima(b_parSlitFF, bp_parSlitFF, sat_thr, chip, binx, biny,NULL),
          "Error creating bad pixel map");

      check(
          flames_preppa_process(b_parSlitFF, bp_parSlitFF, filt_sw, DRS_PTHRE_MIN, DRS_PTHRE_MAX),
          "Error preparing bias subtracted frame");

      cpl_frameset_insert(bslff_prefid, b_parSlitFF);
      must_cleanup_b_parSlitFF = false;

    }

#if FLAMES_DEBUG
    fprintf(stderr, "%s %d\n", __func__, __LINE__);
#endif
  } /* for each frame */

  *t_preppa2 = time(NULL);

  *t_preppa2 = difftime(time(NULL), *t_preppa2);

  uves_msg_debug("Calling prepslitff on %" CPL_SIZE_FORMAT " sflat frames",
      cpl_frameset_get_size(bslff_prefid));

  uves_print_cpl_frameset(bslff_prefid);

  slitff = uves_sprintf("slitff_%c", prefid);

  assure(
      0 == flames_prepslitff(bslff_prefid, outslitff, out_filename, slitff, &DECENTSNR),
      CPL_ERROR_ILLEGAL_OUTPUT, "prepslitff failed");
  uves_msg("end prepslitff");

  cleanup: uves_free_frameset(&bslff_prefid);
  uves_free_string_const(&out_filename);
  if (must_cleanup_b_parSlitFF) {
    uves_free_frame(&b_parSlitFF);
  }
  uves_free_string_const(&slitff);
  uves_free_string_const(&bp_parSlitFF);
  return;
}
/**@}*/
