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

/*
 * $Author: amodigli $
 * $Date: 2013-08-08 12:31:13 $
 * $Revision: 1.58 $
 * $Name: not supported by cvs2svn $
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup flames_dfs  DFS related functions
 */
/*----------------------------------------------------------------------------*/

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

#include <flames_dfs.h>

#include <flames_midas_def.h>
#include <flames_pfits.h>
#include <flames_utils.h>

#include <uves_utils_wrappers.h>
#include <uves_propertylist.h>
#include <uves_dfs.h>
#include <uves_pfits.h>
#include <uves_chip.h>
#include <uves_dump.h>
#include <uves_msg.h>
#include <uves_error.h>

#include <cpl.h>
#include <string.h>
#include <stdbool.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
/**@{*/
static void
load_raw_image_fib(const char *filename, 
                   cpl_type type, bool blue,
                   cpl_image *raw_image[2],
                   uves_propertylist *raw_header[2],
                   uves_propertylist *rotated_header[2],
                   cpl_table* ext_tbl[2]);

static void
fibremask_fill(uves_propertylist *header, enum uves_chip chip, bool warn_tech,cpl_table* ozpoz);


/*----------------------------------------------------------------------------*/
/**
  @brief    Write HISTORY keyword
  @param    header    to write to
  @param    name      descriptor name
  @param    val       value to write
 **/
/*----------------------------------------------------------------------------*/
static void 
set_header_int_1(uves_propertylist *header,
                 const char *name, int val)
{
    const char *val_str = uves_sprintf("%d", val);
    flames_dfs_set_history_val(header, 'I', name, val_str);
    uves_free_string_const(&val_str);
    return;
}
/*----------------------------------------------------------------------------*/
/**
  @brief    Write HISTORY keyword
  @param    header    to write to
  @param    name      descriptor name
  @param    val       value to write
 **/
/*----------------------------------------------------------------------------*/
void 
flames_set_header_char_1(uves_propertylist *header,
                         const char *name, char val)
{
    const char *s = uves_sprintf("%c", val);
    flames_dfs_set_history_val(header, 'C', name, s);
    uves_free_string_const(&s);
    return;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Write HISTORY int array
  @param    header    to write to
  @param    name      descriptor name
  @param    val1      value to write
  @param    val2      value to write
  @param    val3      value to write
  @param    val4      value to write
  @param    val5      value to write
  @param    val6      value to write
  @param    val7      value to write
  @param    val8      value to write
 **/
/*----------------------------------------------------------------------------*/
static void 
set_header_int_9(uves_propertylist *header,
                 const char *name, 
                 int val1, int val2, int val3, int val4,
                 int val5, int val6, int val7, int val8, int val9)
{
    const char *s = uves_sprintf("%d %d %d %d %d %d %d %d %d", 
                    val1, val2, val3, val4, val5, val6, val7, val8, val9);

    flames_dfs_set_history_val(header, 'I', name, s);
    uves_free_string_const(&s);

    return;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Loads a raw frame of a type used by the order position recipe
  @param    frames        The input frame set
  @param    raw_filename  (output) Filename of raw frame
  @param    raw_image     (output) The loaded image(s)
  @param    raw_header    (output) The raw image header(s)
  @param    rotated_header (output) Header(s) containing keywords to describe
                           the output rotated frame(s)
  @param    ext_tbl (output) tables to describe the fiber caracteristics
  @param    blue          (output) Flag indicating if the raw frame is blue (true) or red (false)
  @return   CPL_ERROR_NONE iff OK

 */
/*----------------------------------------------------------------------------*/
cpl_error_code
flames_load_ofpos(const cpl_frameset *frames,
                  const char **raw_filename,
                  cpl_image *raw_image[2],
                  uves_propertylist *raw_header[2],
                  uves_propertylist *rotated_header[2], 
                  cpl_table *ext_tbl[2], 
                  bool *blue)
{
    const char *tags[] = {FLAMES_FIB_FF_ODD, 
                    FLAMES_FIB_FF_EVEN,
                    FLAMES_FIB_FF_ALL};
    int number_of_tags = sizeof(tags) / sizeof(char *);
    int indx;

    check( *raw_filename = uves_find_frame(frames, tags, number_of_tags, &indx,
                    NULL),
           "Could not find raw frame (%s, %s or %s) in SOF",
           tags[0], tags[1], tags[2]);

    *blue = (indx == 0) || (indx == 2);


    /* Load the image */
    check( load_raw_image_fib(*raw_filename,
                    CPL_TYPE_DOUBLE,
                    *blue,
                    raw_image,
                    raw_header,
                    rotated_header,ext_tbl),
           "Error loading image from file '%s'", *raw_filename);


    cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        *raw_filename = NULL;
    }

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Loads a raw frame of a given type
  @param    frames        The input frame set
  @param    raw_filename  (output) Filename of raw frame
  @param    raw_image     (output) The loaded image(s)
  @param    raw_header    (output) The raw image header(s)
  @param    rotated_header (output) Header(s) containing keywords to describe
                           the output rotated frame(s)
  @param    ext_tbl (output) tables to describe the fiber caracteristics
  @param    tag      frame tag 
  @return   CPL_ERROR_NONE iff OK

  The function searches the frame set for a frame with TAG=tag.
  Results are returned using the last five parameters (NULL on error).
 */
/*----------------------------------------------------------------------------*/
cpl_error_code
flames_load_frame(const cpl_frameset *frames,
                  const char **raw_filename,
                  cpl_image *raw_image[2],
                  uves_propertylist *raw_header[2], 
                  uves_propertylist *rotated_header[2], 
                  cpl_table *ext_tbl[2], 
                  const char* tag)
{
    const char *tags[1];
    int indx;

    tags[0] = tag;

    check( *raw_filename = uves_find_frame(frames, tags, 1, &indx, NULL),
           "Could not find raw frame (%s) in SOF", tags[0]);

    /* Load the image */
    check( load_raw_image_fib(*raw_filename,
                    CPL_TYPE_DOUBLE,
                    false,
                    raw_image,
                    raw_header,
                    rotated_header,ext_tbl),
           "Error loading image from file '%s'", *raw_filename);

    cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        *raw_filename = NULL;
    }

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Loads a raw frame of a given type
  @param    frames        The input frame set
  @param    raw_filename  (output) Filename of raw frame
  @param    raw_image     (output) The loaded image(s)
  @param    raw_header    (output) The raw image header(s)
  @param    rotated_header (output) Header(s) containing keywords to describe
                           the output rotated frame(s)
  @param    ext_tbl (output) tables to describe the fiber caracteristics
  @param    indx      frame index 
  @return   CPL_ERROR_NONE iff OK

  The function searches the frame set for a frame with TAG=tag.
  Results are returned using the last five parameters (NULL on error).
 */
/*----------------------------------------------------------------------------*/
cpl_error_code
flames_load_frame_index(const cpl_frameset *frames,
                        const char **raw_filename,
                        cpl_image *raw_image[2],
                        uves_propertylist *raw_header[2],
                        uves_propertylist *rotated_header[2],
                        cpl_table *ext_tbl[2],
                        const int indx)
{
    const cpl_frame* frm=NULL;

    frm = cpl_frameset_get_frame_const(frames,indx);
    *raw_filename=cpl_frame_get_filename(frm);

    /* Load the image */
    check( load_raw_image_fib(*raw_filename,
                    CPL_TYPE_DOUBLE,
                    false,
                    raw_image,
                    raw_header,
                    rotated_header,ext_tbl),
           "Error loading image from file '%s'", *raw_filename);

    cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        *raw_filename = NULL;
        frm=NULL;
    }

    return cpl_error_get_code();
}






/*----------------------------------------------------------------------------*/
/**
   @brief    Load the ordef image
   @param    frames               The input frame set
   @param    chip_name            CCD chip name
   @param    ordef_filename       (output) Filename of ordef image
   @param    ordef                (output) The ordef image
   @param    mdark_header         (output) FITS header
   @param    chip                 CCD chip
 */
/*----------------------------------------------------------------------------*/

void
flames_load_ordef(const cpl_frameset *frames, const char *chip_name,
                  const char **ordef_filename, cpl_image **ordef,
                  uves_propertylist **ordef_header, enum uves_chip chip)
{
    const char *tags[2];
    int number_of_tags = sizeof(tags) / sizeof(char *);
    int extension;
    int indx;
    const bool flames = true;

    *ordef        = NULL;
    *ordef_header = NULL;

    tags[0]   = FLAMES_ORDEF(flames, chip);
    extension = FLAMES_ORDEF_EXTENSION(flames, chip);

    check( *ordef_filename = uves_find_frame(frames, tags, number_of_tags, &indx, NULL), 
           "Could not find %s or %s in frame set", tags[0], tags[1]);

    /* Load the ordef image */
    check( *ordef = cpl_image_load(*ordef_filename,
                    CPL_TYPE_DOUBLE,           /* Convert to this type */
                    0,                         /* plane number */
                    extension                  /* Extension number */
    ),
           "Could not load master dark from extension %d of file '%s'",
           extension, *ordef_filename);

    /* Load the header */
    check( *ordef_header = uves_propertylist_load(*ordef_filename,
                    extension),
           "Could not load header from extension %d of file '%s'",
           extension, *ordef_filename);

    check_nomsg( uves_warn_if_chip_names_dont_match(*ordef_header, chip_name, chip) );
    /* FLAMES MIDAS C-code wants the START (CRVAL) to be 1 */
    check_nomsg( flames_reset_crval_to_one(ordef_header));


    /* The DPR keywords were (correctly) removed by cpl_dfs_setup_product_header(),
       But flames_dfs_write_descr() depends on these, therefore add */   
    check_nomsg( uves_pfits_set_dpr_catg(*ordef_header, "MOS"));
    check_nomsg( uves_pfits_set_dpr_type(*ordef_header, "LAMP,ORDERDEF,SimCal"));
    check_nomsg( uves_pfits_set_dpr_catg(*ordef_header, "CALIB"));

    check_nomsg( flames_dfs_write_descr(*ordef_header, chip, true,NULL) );

    cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) 
    {
        *ordef_filename = NULL;
        uves_free_image(ordef);
        uves_free_propertylist(ordef_header);
    }
    return;
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Load a table
  @param    frames    The input frameset
  @param    filename  File containing the table
  @param    extension The table extension
  @param    table     Loaded table
  @param    pheader   Loaded primary header, if non-NULL
  @param    theader   Loaded table header, if non-NULL
  @param    tag       The table TAG

  @return   CPL_ERROR_NONE iff OK

  The function...
 */
/*----------------------------------------------------------------------------*/
void
flames_load_table(const cpl_frameset *frames, 
                  const char **filename,
                  cpl_table **table,
                  uves_propertylist **pheader,
                  uves_propertylist **theader,
                  const int extension,
                  const char* tag)
{
    const char *tags[1];
    int indx;

    tags[0] = tag;

    /* Get filename */
    check( *filename = uves_find_frame(frames, tags, 1, &indx, NULL),
           "No line reference table (%s) found in SOF", tag);

    /* Load table */
    check( *table = cpl_table_load(*filename,
                    extension,
                    1),         /* Mark identified
                              invalid values? (1=yes) */
           "Error loading line reference table from extension %d of file '%s'",
           extension, *filename);


    if(uves_erase_invalid_table_rows(*table, NULL) > 0) { 
        uves_msg_warning("Table in extension %d of file '%s' "
                        "contains invalid rows", extension, *filename);
    }

    /* Load headers if requested */
    if (pheader != NULL)
    {
        check( *pheader = uves_propertylist_load(*filename, 0),
                        "Could not load primary header of FLAMES table in '%s'", *filename);
    }

    if (theader != NULL)
    {
        check( *theader = uves_propertylist_load(*filename, 1),
                        "Could not load header of FLAMES table in '%s'", *filename);
    }

    cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) 
    {
        uves_free_table(table);
        if (pheader != NULL) uves_free_propertylist(pheader);
        if (theader != NULL) uves_free_propertylist(theader);
    }
    return;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Loads a UVES raw image
  @param    filename       Filename of raw frame
  @param    blue           Flag indicating if the raw frame is blue (true) or red (false)
  @param    type           Load the image as this type
  @param    raw_image      (output) The loaded image(s)
  @param    raw_header     (output) The raw image header(s)
  @param    rotated_header (output) Header(s) containing the proper CDELT, CRPIX... 
  @param    ext_tbl (output) Extension table with FIBER mode info. 

  @return   CPL_ERROR_NONE iff OK

  The function loads a raw blue or red image from the specified file.
  Results are returned through the arrays @em raw_image and @em raw_header,
  which must be 2d arrays. If red, array index 0 is REDL and array index 1 is REDU.
  If blue, array index 1 is not used.

  The loaded images are rotated into standard orientation, and pre- and
  overscan areas are removed (see @c uves_crop_and_rotate() ).  

  @note The provided arrays must have size at least two.
 */
/*----------------------------------------------------------------------------*/

static void
load_raw_image_fib(const char *filename, 
                   cpl_type type, bool blue,
                   cpl_image *raw_image[2],
                   uves_propertylist *raw_header[2],
                   uves_propertylist *rotated_header[2],
                   cpl_table* ext_tbl[2])
{
    cpl_image *image = NULL;
    uves_propertylist *primary_header = NULL;
    uves_propertylist *ext_header = NULL;
    int nextensions;
    int plane = 0;   /* Only one plane in UVES raw files */

    /* Initialize parameters */
    raw_image[0] = NULL;
    raw_image[1] = NULL;
    raw_header[0] = NULL;
    raw_header[1] = NULL;
    rotated_header[0] = NULL;
    rotated_header[1] = NULL;
    ext_tbl[0]=NULL;
    ext_tbl[1]=NULL;


    check( nextensions = uves_get_nextensions(filename),
           "Error reading number of extensions of file '%s'", filename);

    uves_msg_low("Raw frame is %s, file '%s' has %d extensions", 
                 (blue) ? "blue" : "red", filename, nextensions);

    /* If the raw frame is blue, or if it's an old format red frame */
    if (blue || (!blue && nextensions == 0))
    {
        int extension;
        enum uves_chip chip;
        bool new_format = false;

        uves_msg("frame is blue or old format");

        assure( nextensions == 0, CPL_ERROR_ILLEGAL_INPUT, 
                "Unrecognized format of file '%s'. No extensions expected. %d found.",
                filename, nextensions);

        extension = 0;

        check( image = cpl_image_load(filename,
                        CPL_TYPE_DOUBLE,
                        plane,
                        extension
        ), "Could not load image from extension %d of file '%s' ",
               extension, filename);

        /* Load the header */
        check( raw_header[0] = uves_propertylist_load(filename,
                        extension),
                        "Could not load header from extension %d of file '%s'",
                        extension, filename);

        /* Get blue (or lower red) chip */
        chip = (blue) ? UVES_CHIP_BLUE : UVES_CHIP_REDL;
        check( raw_image[0] = uves_crop_and_rotate(image, raw_header[0], 
                        chip, raw_header[0],
                        new_format,
                        &rotated_header[0]),
                        "Error splitting image");

        check_nomsg( flames_dfs_write_descr(raw_header[0], chip, true,ext_tbl[0]) );

        if (!blue)
        {
            const uves_propertylist *redl_header;
            /* Upper red chip, use again the primary header */
            check( raw_header[1] = uves_propertylist_duplicate(raw_header[0]),
                   "Error duplicating FITS header");

            /* Get upper red chip */
            chip = UVES_CHIP_REDU;
            redl_header = raw_header[0];
            check( raw_image[1] = uves_crop_and_rotate(image, raw_header[1],
                            chip, redl_header,
                            new_format,
                            &rotated_header[1]),
                            "Error splitting red image");

            check_nomsg( flames_dfs_write_descr(raw_header[1], chip, true,ext_tbl[0]) );
        }
        else
        {
            raw_image[1] = NULL;
            raw_header[1] = NULL;
            rotated_header[1] = NULL;
        }
    }
    else if (blue || (!blue && nextensions == 2))
    {
        enum uves_chip chip;
        int extension;
        bool new_format = false;
        uves_msg_debug("Frame is red, old format, flames-uves");

        assure( nextensions == 2, CPL_ERROR_UNSUPPORTED_MODE,
                "File '%s' (red frame) has %d extensions. 0 or 2 HDUs expected",
                filename, nextensions);

        uves_msg_debug("Old red format, flames-uves");

        check(ext_tbl[0]=cpl_table_load(filename,1,0),"loading %s ext table1", filename);
        check(ext_tbl[1]=cpl_table_load(filename,2,0),"loading %s ext table2", filename);

        /* Load primary header */
        extension = 0;
        check( primary_header = uves_propertylist_load(filename,
                        extension),
               "Could not load header from extension %d of file '%s'",
               extension, filename);



        check( image = cpl_image_load(filename,
                        CPL_TYPE_DOUBLE,
                        plane,
                        extension
        ), "Could not load image from extension %d of file '%s' ",
               extension, filename);

        /* Load the header */
        check( raw_header[0] = uves_propertylist_load(filename,
                        extension),
                        "Could not load header from extension %d of file '%s'",
                        extension, filename);

        /* Get blue (or lower red) chip */
        chip = (blue) ? UVES_CHIP_BLUE : UVES_CHIP_REDL;
        /* Remove pre-, overscan */
        check( raw_image[0] = uves_crop_and_rotate(image, raw_header[0], 
                        chip, raw_header[0],
                        new_format,
                        &rotated_header[0]),
                        "Error splitting image");

        check_nomsg( flames_dfs_write_descr(raw_header[0], chip, true,ext_tbl[0]) );

        if (!blue)
        {
            const uves_propertylist *redl_header;

            /* Upper red chip, use again the primary header */
            check( raw_header[1] = uves_propertylist_duplicate(raw_header[0]),
                   "Error duplicating FITS header");

            /* Get upper red chip */
            chip = UVES_CHIP_REDU;
            redl_header = raw_header[0];
            check( raw_image[1] = uves_crop_and_rotate(image, raw_header[1],
                            chip, redl_header,
                            new_format,
                            &rotated_header[1]),
                            "Error splitting red image");

            check_nomsg( flames_dfs_write_descr(raw_header[1], chip, true,ext_tbl[0]) );
        }
        else
        {
            raw_image[1] = NULL;
            raw_header[1] = NULL;
            rotated_header[1] = NULL;
        }



    }
    else
        /* Red, new format. Must have 2 or 4 extensions */
    {
        int extension;
        bool new_format = true;

        assure( nextensions == 2 ||
                nextensions == 4, CPL_ERROR_UNSUPPORTED_MODE,
                "File '%s' (red frame) has %d extensions. 0, 2 or 4 HDUs expected",
                filename, nextensions);

        uves_msg("New red format");
        uves_msg_debug("New red format");

        /* Load primary header */
        extension = 0;
        check( primary_header = uves_propertylist_load(filename,
                        extension),
               "Could not load header from extension %d of file '%s'",
               extension, filename);

        for (extension = 1; extension <= 2; extension++)
        {
            /* In the FITS file, REDU is stored
               in extension 1, and REDL is stored in
               extension 2 */
            enum uves_chip chip = (extension == 1) ? UVES_CHIP_REDU : UVES_CHIP_REDL;

            int indx = uves_chip_get_index(chip);

            /* Load the extension header */
            uves_free_propertylist(&ext_header);
            check( ext_header = uves_propertylist_load(filename,
                            extension),
                   "Could not load header from extension %d of file '%s'",
                   extension, filename);

            /* Merge with primary header */
            check( raw_header[indx] = uves_propertylist_duplicate(primary_header),
                   "Error cloning primary header");


            if (!uves_propertylist_is_empty(ext_header))
            {
                check( uves_propertylist_copy_property_regexp(raw_header[indx], 
                                ext_header, ".*", 0),
                                "Error merging primary header with extension %d header",
                                extension);
            }

        }

        if (nextensions == 4) {
            check(ext_tbl[0]=cpl_table_load(filename,3,0),"loading %s ext table1", filename);
            check(ext_tbl[1]=cpl_table_load(filename,4,0),"loading %s ext table2", filename);
        }

        for (extension = 1; extension <= 2; extension++)
        {
            enum uves_chip chip = (extension == 1) ? UVES_CHIP_REDU : UVES_CHIP_REDL;

            int indx = uves_chip_get_index(chip);
            int indx_redl = uves_chip_get_index(UVES_CHIP_REDL);

            uves_free_image(&image);
            check( image = cpl_image_load(filename,
                            type,
                            plane,
                            extension),
                   "Could not load image from extension %d of file '%s' ",
                   extension, filename);

            /* Remove pre-, overscan */
            check( raw_image[indx] = uves_crop_and_rotate(image, 
                            raw_header[indx],
                            chip, raw_header[indx_redl],
                            new_format,
                            &rotated_header[indx]),
                            "Error splitting red image");

            check_nomsg( flames_dfs_write_descr(raw_header[indx], chip, true,ext_tbl[0]) );
        }


    }/* New format */

    cleanup:
    uves_free_image(&image);
    uves_free_propertylist(&primary_header);
    uves_free_propertylist(&ext_header);

    if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
        uves_free_image       (&raw_image[0]);
        uves_free_image       (&raw_image[1]);
        uves_free_propertylist(&raw_header[0]);
        uves_free_propertylist(&raw_header[1]);
        uves_free_propertylist(&rotated_header[0]);
        uves_free_propertylist(&rotated_header[1]);
    }

    return;
}



/*----------------------------------------------------------------------------*/
/**
  @brief    Write value to HISTORY keywords
  @param    plist        property list to write to
  @param    type         'I' for integer, 'C' for char    
  @param    name         name
  @param    val          value as string, e.g. '5 4 3 23 1' for an integer array

  This function adds 

  HISTORY 'name','I'
  HISTORY value
  HISTORY

  If an entry with value prefix "'name','I'" already exists, it will be updated.
 */
/*----------------------------------------------------------------------------*/
void
flames_dfs_set_history_val(uves_propertylist *header, char type,
                           const char *name, const char *val)
{
    char *new_name = NULL;
    cpl_property *existing = NULL;
    cpl_property *next = NULL;
    const long header_size = uves_propertylist_get_size(header);
    int i;

    uves_msg_debug("Searching for %s", name);

    for (i = 0;
                    existing == NULL && i < header_size-2; i++)
    {
        cpl_property *p = uves_propertylist_get(header, i);
        const char *pname = cpl_property_get_name(p);

        if (strcmp(pname, "HISTORY") == 0)
        {
            const char *pval;
            check( pval = cpl_property_get_string(p),
                   "Error reading property value");

            /* match the string  "'name','I" (for any type I) */

            if (strlen(pval) >= 1 + strlen(name) + 4 &&
                            pval[0] == '\'' &&
                            strncmp(pval+1, name, strlen(name)) == 0 &&
                            pval[strlen(name)+1] == '\'')
            {
                assure( strncmp(pval+strlen(name)+1, "','", 3) == 0 &&
                                pval[strlen(name)+4] == type,
                                CPL_ERROR_TYPE_MISMATCH,
                                "Keyword '%s' has wrong type, '%c' expected",
                                pval, type);

                /* Remember the next position and stop searching */
                existing = uves_propertylist_get(header, i+1);
                next = uves_propertylist_get(header, i+2);

                assure( strcmp(cpl_property_get_name(next), "HISTORY") == 0,
                        CPL_ERROR_ILLEGAL_INPUT,
                        "Missing HISTORY='' termination of keyword '%s'",
                        name);
            }
        }
    }

    uves_msg_debug("HISTORY %s keyword %sfound", name, (existing == NULL) ? "not " : "");

    assure( strlen("HISTORY ") + strlen(val) <= 80, CPL_ERROR_ILLEGAL_INPUT,
            "String 'HISTORY %s' length (%zu) is more than 80", val, 
            strlen("HISTORY ") + strlen(val));

    if (existing != NULL)
    {
        check( cpl_property_set_string(existing, val),
                        "Error updating HISTORY keyword with value '%s'", val);

        check( cpl_property_set_string(next, ""),
               "Error updating HISTORY keyword with value ''");
    }
    else
        /* append */
    {
        new_name = uves_sprintf("'%s','%c'", name, type);

        check( uves_propertylist_append_string(header, "HISTORY", new_name),
               "Error writing HISTORY keyword with value '%s'", new_name);

        check( uves_propertylist_append_string(header, "HISTORY", val),
               "Error updating HISTORY keyword with value '%s'", val);
        check( uves_propertylist_append_string(header, "HISTORY", ""),
               "Error updating HISTORY keyword with value ''");
    }

    cleanup:
    cpl_free(new_name);
    return;
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Write descriptors special for FLAMES reduction
  @param    header    to write to
  @param    warn_tech  if true, prints warning when DPR TECH is unknown
  @param    chip      CCD chip
 **/
/*----------------------------------------------------------------------------*/
void
flames_dfs_write_descr(uves_propertylist *header, enum uves_chip chip,
                       bool warn_tech, cpl_table* ozpoz)
{
    check( flames_set_header_char_1(header, FLAMES_CHIPCHOICE, uves_chip_tochar(chip)),
                    "Error writing chip choice descriptor");

    check( fibremask_fill(header, chip, warn_tech,ozpoz),
           "Error writing fibre mask to raw frame header");

    cleanup:
    return;
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Set fibremask keyword using ozpoz table info
  @param    table    ozpoz table
  @return   fibremask string
 **/
/*----------------------------------------------------------------------------*/

void flames_set_fibremask(cpl_table* table, char* fibremask,
                          const int size, const int mode) {

    int f0_bcal = 0;
    int f1_b3 = 0;
    int f2_b135 = 0;
    int f3_b37 = 0;
    int f4_b169 = 0;
    int f5_b69 = 0;
    int f6_b201 = 0;
    int f7_b103 = 0;
    int f8_b235 = 0;

    if (mode ==1) {
        f0_bcal = 1;
    }


    //cpl_table_dump(table,0,9,stdout);
    f1_b3 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 3);
    //uves_msg("f1_b3=%d",f1_b3);
    cpl_table_select_all(table);

    f2_b135 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 135);
    //uves_msg("f2_b135=%d",f2_b135);
    cpl_table_select_all(table);

    f3_b37 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 37);
    //uves_msg("f3_b37=%d",f3_b37);
    cpl_table_select_all(table);

    f4_b169 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 169);
    //uves_msg("f4_b169=%d",f4_b169);
    cpl_table_select_all(table);

    f5_b69 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 69);
    //uves_msg("f5_b69=%d",f5_b69);
    cpl_table_select_all(table);

    f6_b201 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 201);
    //uves_msg("f6_b201=%d",f6_b201);
    cpl_table_select_all(table);

    f7_b103 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 103);
    //uves_msg("f7_b103=%d",f7_b103);
    cpl_table_select_all(table);

    f8_b235 = cpl_table_and_selected_int(table, "BUTTON", CPL_EQUAL_TO, 235);
    //uves_msg("f8_b235=%d",f8_b235);
    cpl_table_select_all(table);

    snprintf(fibremask, size, "%d,%d,%d,%d,%d,%d,%d,%d,%d", f0_bcal, f1_b3,
             f2_b135, f3_b37, f4_b169, f5_b69, f6_b201, f7_b103, f8_b235);

    //uves_msg("fibremask=%s string of size %d", fibremask, size);

    return;
}

/*----------------------------------------------------------------------------*/
/**
 @brief    Set fibremask keyword
 @param    header    to write to
 @param    chip      CCD chip
 **/
/*----------------------------------------------------------------------------*/

static void fibremask_fill(uves_propertylist *header, enum uves_chip chip,
                           bool warn_tech, cpl_table* ozpoz)

{
    const char * const maxfibres = "MAXFIBRES";
    const char * const fibremask = "FIBREMASK";
    const char *dpr_tech = " ";
    const char *dpr_type = " ";
    int maxf = 0;

    const int size = 40;
    char mask[size];
    int simc = 0;
    int check = 0;
    int wlen = 0;

    if (uves_propertylist_contains(header, UVES_DPR_TECH)) {
        check_nomsg( dpr_tech = uves_pfits_get_dpr_tech(header));
    }

    if (uves_propertylist_contains(header, UVES_DPR_TYPE)) {
        check_nomsg( dpr_type = uves_pfits_get_dpr_type(header));
    }
    if (uves_propertylist_contains(header, UVES_GRATWLEN(chip))) {
        check_nomsg(
                        wlen = uves_round_double( uves_pfits_get_gratwlen(header, chip)));
    }
    if (uves_propertylist_contains(header, UVES_OCS_SIMCAL)) {
        check_nomsg( simc = uves_pfits_get_ocs_simcal(header));
    }

    switch (wlen) {

    case 520:
        if (strcmp(dpr_tech, "ECHELLE") == 0) {
            if (strstr(dpr_type, "FLAT") != NULL) {
                check( set_header_int_1(header, maxfibres, 0),
                                "Could not write %s keyword", maxfibres);

                check = 2;
            } else if (strstr(dpr_type, "LAMP,SFLAT") != NULL) {
                check( set_header_int_1(header, maxfibres, 0),
                                "Could not write %s keyword", maxfibres);
                check = 2;
            } else if (strstr(dpr_type, "LAMP,FMTCHK") != NULL) {
                check = 3;
            } else {
                check = 1;
            }
        } else if (strstr(dpr_type, "LAMP,FLAT") != NULL) {
            if (strstr(dpr_type, "OzPoz") != NULL) {
                if (strstr(dpr_type, "ODD") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "EVEN") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "ALL") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else {
                    check = 1;
                }
            } else if (strstr(dpr_type, "SimCal") != NULL) {
                if (strstr(dpr_type, "ODD") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "EVEN") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,1);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "ALL") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,1);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else {
                    check = 1;
                }
            } else if (strstr(dpr_type, "NASMYTH") != NULL) {
                maxf = 9;
                if (simc == 0) {
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                } else {
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,1);
                    //uves_msg("computed fibremask='%s'",mask);
                }
                check = 0;
            } else {
                check = 1;
            }
        } else if (strstr(dpr_type, "LAMP,WAVE,OzPoz") != NULL) {
            maxf = 0;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,0);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "LAMP,WAVE,SimCal") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,1);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,OzPoz") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,0);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,COMBINED") != NULL) {
            maxf = 9;
            if (simc == 0) {
                //uves_msg("expected fibremask='%s'",mask);
                flames_set_fibremask(ozpoz, mask, size,0);
                //uves_msg("computed fibremask='%s'",mask);
            } else {
                //uves_msg("expected fibremask='%s'",mask);
                flames_set_fibremask(ozpoz, mask, size,1);
                //uves_msg("computed fibremask='%s'",mask);
            }
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,SimCal") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,1);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "LAMP,FMTCHK,OzPoz") != NULL
                        || strstr(dpr_type, "LAMP,FMTCHK,SimCal") != NULL
                        || strstr(dpr_type, "LAMP,FMTCHK") != NULL) {
            check = 3;
        }
        else if (strstr(dpr_type, "LAMP,ORDERDEF,OzPoz") != NULL) {
            maxf = 9;
            snprintf(mask, size, "%s", "0,0,0,0,1,0,0,0,0");
            check = 0;
        }

        else if (strstr(dpr_type, "LAMP,ORDERDEF,SimCal") != NULL) {
            maxf = 9;
            snprintf(mask, size, "%s", "1,0,0,0,0,0,0,0,0");
            check = 0;
        } else {
            check = 1;
        }

        break;
    case 580:
    case 860:

        if (strcmp(dpr_tech, "ECHELLE") == 0) {
            if (strstr(dpr_type, "FLAT") != NULL) {
                check( set_header_int_1(header, maxfibres, 0),
                                "Could not write %s keyword", maxfibres);

                check = 2;
            } else if (strstr(dpr_type, "LAMP,SFLAT") != NULL) {
                check( set_header_int_1(header, maxfibres, 0),
                                "Could not write %s keyword", maxfibres);
                check = 2;
            } else if (strstr(dpr_type, "LAMP,FMTCHK") != NULL) {
                check = 3;
            } else {
                check = 1;
            }
        } else if (strstr(dpr_type, "LAMP,FLAT") != NULL) {
            if (strstr(dpr_type, "OzPoz") != NULL) {
                if (strstr(dpr_type, "ODD") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "EVEN") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "ALL") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else {
                    check = 1;
                }
            } else if (strstr(dpr_type, "SimCal") != NULL) {
                if (strstr(dpr_type, "ODD") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "EVEN") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                    check = 0;
                } else if (strstr(dpr_type, "ALL") != NULL) {
                    maxf = 9;
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,1);
                    //uves_msg("computed fibremask='%s'",mask);

                    check = 0;
                } else {
                    check = 1;
                }
            }
            else if (strstr(dpr_type, "NASMYTH") != NULL) {
                maxf = 9;
                if (simc == 0) {
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,0);
                    //uves_msg("computed fibremask='%s'",mask);
                } else {
                    //uves_msg("expected fibremask='%s'",mask);
                    flames_set_fibremask(ozpoz, mask, size,1);
                    //uves_msg("computed fibremask='%s'",mask);
                }
                check = 0;
            } else {
                check = 1;
            }
        } else if (strstr(dpr_type, "LAMP,WAVE,OzPoz") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,0);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,OzPoz") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,0);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "LAMP,WAVE,OzPoz") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,0);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,COMBINED") != NULL) {
            maxf = 9;
            if (simc == 0) {
                //uves_msg("expected fibremask='%s'",mask);
                flames_set_fibremask(ozpoz, mask, size,0);
                //uves_msg("computed fibremask='%s'",mask);
            } else {
                //uves_msg("expected fibremask='%s'",mask);
                flames_set_fibremask(ozpoz, mask, size,1);
                //uves_msg("computed fibremask='%s'",mask);
            }
            check = 0;
        }
        else if (strstr(dpr_type, "OBJECT,SimCal") != NULL) {
            maxf = 9;
            //uves_msg("expected fibremask='%s'",mask);
            flames_set_fibremask(ozpoz, mask, size,1);
            //uves_msg("computed fibremask='%s'",mask);
            check = 0;
        }
        else if (strstr(dpr_type, "LAMP,FMTCHK,OzPoz") != NULL
                        || strstr(dpr_type, "LAMP,FMTCHK,SimCal") != NULL
                        || strstr(dpr_type, "LAMP,FMTCHK") != NULL) {
            check = 3;
        }
        else if (strstr(dpr_type, "LAMP,ORDERDEF,OzPoz") != NULL) {
            maxf = 9;
            snprintf(mask, size, "%s", "0,0,0,0,1,0,0,0,0");
            check = 0;
        }
        else if (strstr(dpr_type, "LAMP,ORDERDEF,SimCal") != NULL) {
            maxf = 9;
            snprintf(mask, size, "%s", "1,0,0,0,0,0,0,0,0");
            check = 0;
        } else {
            check = 1;
        }
        break;
    default:
        uves_msg_warning("Unrecognized (non-standard) wavelength: %d", wlen);
        break;
    }

    if (check == 0) {
        check(
                        set_header_int_9(header, fibremask, mask[0*2] - '0', mask[1*2] - '0', mask[2*2] - '0', mask[3*2] - '0', mask[4*2] - '0', mask[5*2] - '0', mask[6*2] - '0', mask[7*2] - '0', mask[8*2] - '0'),
                        "Could not write %s keyword", maxfibres);

        check( set_header_int_1(header, maxfibres, maxf),
               "Could not write %s keyword", maxfibres);
    } else if (check == 3) {
        uves_msg("Type %s tech %s is known and "
                        "does not require FIBREMASK & MAXFIBRES descriptors", dpr_tech, dpr_type);
    } else if (check == 2) {
        uves_msg("Type %s tech %s is known and "
                        "requires FIBREMASK & MAXFIBRES descriptors", dpr_tech, dpr_type);
    }
    else {
        if (warn_tech) {
            uves_msg_warning("Type '%s' tech '%s' is unknown!", dpr_type, dpr_tech);
        }

        check( set_header_int_1(header, maxfibres, maxf),
               "Could not write %s keyword", maxfibres);
    }

    cleanup: return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Set extension to fits if bdf
  @param    filename    to check for extension
  @return   outname     newly allocated filename with corrected extension
 **/
/*--------------------------------------------------------------------------*/
char *
flames_fix_estention(const char* filename)
{
    if (strlen(filename) > 4 &&
                    strcmp(filename + strlen(filename) - 4, ".bdf") == 0) {

        int n = strlen(filename);
        char *outname = cpl_malloc((n + 2)*sizeof(char));

        strcpy(outname, filename);
        outname[n - 3] = 'f';
        outname[n - 2] = 'i';
        outname[n - 1] = 't';
        outname[n - 0] = 's';
        outname[n + 1] = '\0';
        return outname;
    }
    else {
        return cpl_strdup(filename);
    }
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Create FLAMES pipeline product
  @param    frames       @see uves_frameset_insert
  @param    group        @see uves_frameset_insert
  @param    type         @see uves_frameset_insert
  @param    level        @see uves_frameset_insert
  @param    filename     input/output FITS filename
  @param    tag          @see uves_frameset_insert
  @param    raw_header   @see uves_frameset_insert
  @param    parameters   @see uves_frameset_insert
  @param    recipe_id    @see uves_frameset_insert
  @param    pipeline     @see uves_frameset_insert
  @param    qc           @see uves_frameset_insert
  @param    start_time   @see uves_frameset_insert
  @param    dump_paf     @see uves_frameset_insert
  @param    stats_mask   @see uves_frameset_insert

  This function is like uves_frameset_insert() but supports 3d, 4d images
 **/
/*--------------------------------------------------------------------------*/

void
flames_frameset_insert(cpl_frameset *frames, 
                       cpl_frame_group group,
                       cpl_frame_type type,
                       cpl_frame_level level,
                       const char *filename,
                       const char *tag,
                       const uves_propertylist *raw_header,
                       const cpl_parameterlist *parameters,
                       const char *recipe,
                       const char *pipeline,
                       cpl_table **qc,
                       const char *start_time,
                       bool dump_paf,
                       unsigned stats_mask)
{
    void *buffer;

    cpl_table *table = NULL;
    cpl_image *out_image = NULL;
    uves_propertylist *primary_header = NULL;
    uves_propertylist *extension_header = NULL;

    /*  Pseudocode:

        If image
             Load the image buffer, determine dimension (2d, 3d, 4d)

             Call uves_frameset_insert() to create an empty image with the correct
             header.

             Load the file, write the image data, don't change the header. Save.

        If table
             Load table + headers
             Call uves_frameset_insert()
     */

    if (type == CPL_FRAME_TYPE_IMAGE) {
        int image;
        int dattype;
        int dimension;
        int naxis[4];
        double crpix[4] = {1, 1, 1, 1};
        double crval[4] = {1, 1, 1, 1};
        double cdelt[4] = {1, 1, 1, 1};
        const char *bunit = NULL;
        const char *ctype1 = NULL;
        const char *ctype2 = NULL;
        const char *ctype3 = NULL;
        const char *ctype4 = NULL;

        int size;

        /* Get type */
        {
            int info[2];
            assure( 0 == SCFINF(filename, 4, info),
                    CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not get %s image data type", filename);

            dattype = info[1];

            uves_msg_debug("dattype is %d", dattype);

        }

        /* Open */
        assure( 0 == SCFOPN(filename, dattype, 0, F_IMA_TYPE, &image),
                CPL_ERROR_ILLEGAL_OUTPUT,
                "Could not open image %s", filename);

        /* Get dimension */
        {
            int maxvals = 4;
            int felem = 1;
            int unit, null;

            assure( 0 == SCDRDI(image, "NPIX", felem, maxvals, &dimension, naxis, &unit, &null),
                    CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not read NAXIS from %s", filename);

            uves_msg_debug("Image dimension is %d", dimension);
        }

        /* Load pixel data */
        {
            int actsize;
            cpl_type ctype;

            switch (dimension) {
            case 1: size = naxis[0]; break;
            case 2: size = naxis[0] * naxis[1]; break;
            case 3: size = naxis[0] * naxis[1] * naxis[2]; break;
            case 4: size = naxis[0] * naxis[1] * naxis[2] * naxis[3]; break;
            default:
                assure( false, CPL_ERROR_UNSUPPORTED_MODE,
                                "Dimension is %d", dimension);
                break;
            }

            ctype = flames_midas_image_dtype_to_cpltype(dattype);
            switch(ctype) {
            case CPL_TYPE_INT:
                buffer = cpl_malloc(sizeof(int)   * size); 
                out_image = cpl_image_wrap_int(size, 1, buffer);
                break;
            case CPL_TYPE_FLOAT: 
                buffer = cpl_malloc(sizeof(float) * size);
                out_image = cpl_image_wrap_float(size, 1, buffer);
                break;
            default:
                assure( false, CPL_ERROR_UNSUPPORTED_MODE,
                                "Type is %s", uves_tostring_cpl_type(type));
                break;
            }
            assure_mem( buffer );

            assure( 0 == SCFGET(image, 1, size, &actsize, buffer),
                    CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not load image %s data", filename);

            assure( actsize == size, CPL_ERROR_ILLEGAL_OUTPUT,
                    "Tried to read %d pixels from %s, got %d",
                    size, filename, actsize);

            assure( 0 == SCFCLO(image), CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not close file %s", filename);
        }

        /* Create empty file with correct header */
        check( primary_header = uves_propertylist_load(filename, 0),
               "Could not load primary header from %s", filename);

        /* Cannot write stats (DATAMIN, DATAMAX, ...) inside uves_frameset_insert(),
           need the image buffer for that
         */


        if (stats_mask != 0) {
            check_nomsg( uves_dfs_write_statistics(out_image, primary_header,
                            stats_mask));
        }

        if (dimension >= 1 &&
                        uves_propertylist_contains(primary_header, "CRPIX1")) {
            crpix[0] = uves_propertylist_get_double(primary_header, "CRPIX1");
        }
        if (dimension >= 2 &&
                        uves_propertylist_contains(primary_header, "CRPIX2")) {
            crpix[1] = uves_propertylist_get_double(primary_header, "CRPIX2");
        }
        if (dimension >= 3 &&
                        uves_propertylist_contains(primary_header, "CRPIX3")) {
            crpix[2] = uves_propertylist_get_double(primary_header, "CRPIX3");
        }
        if (dimension >= 4 &&
                        uves_propertylist_contains(primary_header, "CRPIX4")) {
            crpix[3] = uves_propertylist_get_double(primary_header, "CRPIX4");
        }

        if (dimension >= 1 &&
                        uves_propertylist_contains(primary_header, "CRVAL1")) {
            crval[0] = uves_propertylist_get_double(primary_header, "CRVAL1");
        }
        if (dimension >= 2 &&
                        uves_propertylist_contains(primary_header, "CRVAL2")) {
            crval[1] = uves_propertylist_get_double(primary_header, "CRVAL2");
        }
        if (dimension >= 3 &&
                        uves_propertylist_contains(primary_header, "CRVAL3")) {
            crval[2] = uves_propertylist_get_double(primary_header, "CRVAL3");
        }
        if (dimension >= 4 &&
                        uves_propertylist_contains(primary_header, "CRVAL4")) {
            crval[3] = uves_propertylist_get_double(primary_header, "CRVAL4");
        }

        if (dimension >= 1 &&
                        uves_propertylist_contains(primary_header, "CDELT1")) {
            cdelt[0] = uves_propertylist_get_double(primary_header, "CDELT1");
        }
        if (dimension >= 2 &&
                        uves_propertylist_contains(primary_header, "CDELT2")) {
            cdelt[1] = uves_propertylist_get_double(primary_header, "CDELT2");
        }
        if (dimension >= 3 &&
                        uves_propertylist_contains(primary_header, "CDELT3")) {
            cdelt[2] = uves_propertylist_get_double(primary_header, "CDELT3");
        }
        if (dimension >= 4 &&
                        uves_propertylist_contains(primary_header, "CDELT4")) {
            cdelt[3] = uves_propertylist_get_double(primary_header, "CDELT4");
        }

        if (dimension >= 1 &&
                        uves_propertylist_contains(primary_header, "CTYPE1")) {
            ctype1 = uves_propertylist_get_string(primary_header, "CTYPE1");
        }
        if (dimension >= 2 &&
                        uves_propertylist_contains(primary_header, "CTYPE2")) {
            ctype2 = uves_propertylist_get_string(primary_header, "CTYPE2");
        }
        if (dimension >= 3 &&
                        uves_propertylist_contains(primary_header, "CTYPE3")) {
            ctype3 = uves_propertylist_get_string(primary_header, "CTYPE3");
        }
        if (dimension >= 4 &&
                        uves_propertylist_contains(primary_header, "CTYPE4")) {
            ctype4 = uves_propertylist_get_string(primary_header, "CTYPE4");
        }

        if (uves_propertylist_contains(primary_header, "BUNIT")) {
            bunit = uves_propertylist_get_string(primary_header, "BUNIT");
        }


        for (int i = 0; i < uves_propertylist_get_size(primary_header); i++) {
            cpl_property *p = uves_propertylist_get(primary_header, i);

            if (strcmp(cpl_property_get_name(p), "HISTORY") == 0) {
                const char *value = cpl_property_get_string(p);

                if (strstr(value, "ESO-DESCRIPTORS START")) {
                    cpl_property_set_string(p, "EDS");
                    /* This is to avoid that MIDAS' INTAPE/FITS command
                       warns about the header not being in MIDAS format */
                }
            }
        }

        check_nomsg( uves_frameset_insert(frames,
                        NULL,      /* Image */
                        group,
                        type,
                        level,
                        filename,
                        tag,
                        raw_header,
                        primary_header,
                        NULL,
                        parameters,
                        recipe,
                        pipeline,
                        qc,
                        start_time,
                        dump_paf,
                        0));



        /* Write pixels data */
        {
            int unit;


            assure( 0 == SCFOPN(filename, dattype, 0, F_IMA_TYPE, &image),
                    CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not open image %s", filename);

            /* The image should now have NAXIS = 0, overwrite with correct dimension/size */
            assure( 0 == SCDWRI(image, "NPIX", naxis,
                            1, dimension, &unit),
                    CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not set size of %dd image %s", dimension, filename);

            assure_nomsg( 0 == SCDWRD(image, "CRPIX", crpix, 1, dimension, &unit),
                          CPL_ERROR_ILLEGAL_OUTPUT);
            assure_nomsg( 0 == SCDWRD(image, "CRVAL", crval, 1, dimension, &unit),
                          CPL_ERROR_ILLEGAL_OUTPUT);
            assure_nomsg( 0 == SCDWRD(image, "CDELT", cdelt, 1, dimension, &unit),
                          CPL_ERROR_ILLEGAL_OUTPUT);

            if (bunit == NULL) {
                bunit = "                ";
            }

            assure_nomsg( 0 == SCDWRC(image, "BUNIT", 1, bunit, 1, strlen(bunit)+1, &unit),
                          CPL_ERROR_ILLEGAL_OUTPUT);

            if (ctype1 != NULL) {
                assure_nomsg( 0 == SCDWRC(image, "CTYPE1", 1, ctype1, 1, strlen(ctype1)+1, &unit),
                                CPL_ERROR_ILLEGAL_OUTPUT);
            }
            if (ctype2 != NULL) {
                assure_nomsg( 0 == SCDWRC(image, "CTYPE2", 1, ctype2, 1, strlen(ctype2)+1, &unit),
                                CPL_ERROR_ILLEGAL_OUTPUT);
            }
            if (ctype3 != NULL) {
                assure_nomsg( 0 == SCDWRC(image, "CTYPE3", 1, ctype3, 1, strlen(ctype3)+1, &unit),
                                CPL_ERROR_ILLEGAL_OUTPUT);
            }
            if (ctype4 != NULL) {
                assure_nomsg( 0 == SCDWRC(image, "CTYPE4", 1, ctype4, 1, strlen(ctype4)+1, &unit),
                                CPL_ERROR_ILLEGAL_OUTPUT);
            }

            assure( 0 == SCFPUT(image, 1, size, buffer), CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not rewrite image data to %dd image %s", dimension, filename);

            assure( 0 == SCFCLO(image), CPL_ERROR_ILLEGAL_OUTPUT,
                    "Could not close file %s", filename);
        }

    }
    else if (type == CPL_FRAME_TYPE_TABLE) {


        check( table = cpl_table_load(filename, 1, 1),
                        "Could not load table %s", filename);
        check( primary_header = uves_propertylist_load(filename, 0),
               "Could not load primary header from %s", filename);
        check( extension_header = uves_propertylist_load(filename, 1),
               "Could not load 1st extension header from %s", filename);


        check_nomsg( uves_frameset_insert(frames,
                        table,
                        group,
                        type,
                        level,
                        filename,
                        tag,
                        raw_header,
                        primary_header,
                        extension_header,
                        parameters,
                        recipe,
                        pipeline,
                        qc,
                        start_time,
                        dump_paf,
                        stats_mask));


    }


    cleanup:
    uves_free_table(&table);
    uves_free_image(&out_image);
    uves_free_propertylist(&primary_header);
    uves_free_propertylist(&extension_header);
    return;
}


cpl_error_code
flames_clean_tmp_products_ofpos(enum uves_chip chip, const int cubify)
{

    char command[1024];

    //REDU ok
    //  sprintf(command,"rm -f  slitff_*_bound*.fits slitff_*_badpixel*.fits bp_b_set_?_mf???*.fits b_set_?_mf???*.fits *odd*.fits *even*.fits *all*.fits middumm*.fits bkg_*.fits mbias_*.fits ord_gue_*.fits first.fits third.fits flames_create_full_ot.fits rof*.fits sp_red*.fits");
    uves_msg("cip=%d vs %d",chip,UVES_CHIP_REDU);
    if(chip == UVES_CHIP_REDL) {
        uves_msg("cubify=%d",cubify);
        if (cubify==0) {
            sprintf(command,"rm -f bp_ordef_redl.fits ordef_redl.fits b_ordef_redl.fits b_ordef_redl_*.fits odd*.fits even*.fits all*.fits trap_redl.fits b*odd_l*.fits b*even_l*.fits b*all_l*.fits *set?*_redl*.fits odd*_sigma.fits bp_b_set_?_mf???*.fits b_set_?_mf???*.fits middumm*.fits bkg_*.fits mbias_*.fits ord_gue_*.fits first.fits third.fits flames_create_full_ot.fits rofl.fits sp_redl.fits");
        } else {
            sprintf(command,"rm -f bp_ordef_redl.fits ordef_redl.fits b_ordef_redl.fits b_ordef_redl_*.fits first.fits third.fits flames_create_full_ot.fits trap_redl.fits sp_redl.fits rofl.fits bkg_l.fits mbias_redl.fits ord_gue_redl.fits odd*.fits b*odd_l*.fits even*.fits b*even_l*.fits all*.fits b*all_l*.fits middumm*.fits *set?_*redl*.fits b_*set_l_mf???*.fits b_odd_l*.fits b_even_l*.fits b_all_l*.fits fibreff_l_data0?.fits fibreff_l_sigma0?.fits fibreff_l_badpixel0?.fits slitff_l_data0?.fits slitff_l_sigma0?.fits slitff_l_bound0?.fits  slitff_l_badpixel0?.fits slitff_l_bound0?.fits");
        }

    } else {
        if (cubify==0) {
            sprintf(command,"rm -f bp_ordef_redu.fits ordef_redu.fits b_ordef_redu.fits b_ordef_redu_*.fits odd*.fits even*.fits all*.fits trap_redu.fits *odd_u*.fits *even_u*.fits *all_u*.fits xt_*u.fits *set?*_redu*.fits odd*_sigma.fits bp_b_set_?_mf???*.fits b_set_?_mf???*.fits middumm*.fits bkg_*.fits mbias_*.fits ord_gue_*.fits first.fits third.fits flames_create_full_ot.fits rofu.fits sp_redu.fits");
        } else {
            sprintf(command,"rm -f bp_ordef_redu.fits ordef_redu.fits b_ordef_redu.fits b_ordef_redu_*.fits xt_*u.fits first.fits third.fits flames_create_full_ot.fits trap_redu.fits sp_redu.fits rofu.fits bkg_u.fits mbias_redu.fits ord_gue_redu.fits odd*.fits b*odd_u*.fits even*.fits b*even_u*.fits all*.fits b*all_u*.fits middumm*.fits *set?_*redu*.fits b_*set_u_mf???*.fits b_odd_u*.fits b_even_u*.fits b_all_u*.fits fibreff_u_data0?.fits fibreff_u_sigma0?.fits fibreff_u_badpixel0?.fits slitff_u_data0?.fits slitff_u_sigma0?.fits slitff_u_bound0?.fits  slitff_u_badpixel0?.fits slitff_u_bound0?.fits");
        }
    }
    system(command);
    uves_msg("end cleanup");
    return cpl_error_get_code();
}

cpl_error_code
flames_clean_tmp_products_sci(enum uves_chip chip, const int cubify)
{

    char command[1024];

    if(chip == UVES_CHIP_REDL) {
        if (cubify==0) {
            sprintf(command,"rm -f   sp_redl.fits sci_l.fits cor_shape_l.fits m_tbl_redl.fits trap_redl.fits mbias_l.fits ordef_l.fits bkg_l.fits ext?.fits *sci_l*.fits *pack.fits middumm*.fits fibreff_l_data0?.fits fibreff_l_sigma0?.fits fibreff_l_badpixel0?.fits fibreff_l_dtc.fits fibreff_l_sgc.fits fibreff_l_bpc.fits fibreff_l_norm.fits fibreff_l_nsigma.fits fibreff_l_common.fits slitff_l_data0?.fits slitff_l_sigma0?.fits slitff_l_bound0?.fits slitff_l_badpixel0?.fits slitff_l_dtc.fits slitff_l_sgc.fits slitff_l_bpc.fits slitff_l_bnc.fits slitff_l_norm.fits slitff_l_nsigma.fits slitff_l_common.fits mwfxb_*mask.fits fxb_*extco*.fits wfxb_*extco*.fits");
        } else {
            sprintf(command,"rm -f  sp_redl.fits sci_l.fits cor_shape_l.fits m_tbl_redl.fits trap_redl.fits mbias_l.fits ordef_l.fits bkg_l.fits ext?.fits *b_sci_l*.fits middumm*.fits *fxb_l_*0*.fits fibreff_l_data0?.fits fibreff_l_sigma0?.fits fibreff_l_badpixel0?.fits fibreff_l_dtc.fits fibreff_l_sgc.fits fibreff_l_bpc.fits fibreff_l_nsigma.fits fibreff_l_norm.fits fibreff_l_common.fits slitff_l_data0?.fits slitff_l_sigma0?.fits slitff_l_bound0?.fits slitff_l_badpixel0?.fits slitff_l_dtc.fits slitff_l_sgc.fits slitff_l_bpc.fits slitff_l_bnc.fits slitff_l_norm.fits slitff_l_common.fits b_set_l_mf???*.fits *fxb_l_*extco*.fits");
        }
    } else {
        if (cubify==0) {
            sprintf(command,"rm -f   sp_redu.fits sci_u.fits cor_shape_u.fits m_tbl_redu.fits trap_redu.fits mbias_u.fits ordef_u.fits bkg_u.fits ext?.fits *sci_u*.fits *pack.fits middumm*.fits fibreff_u_data0?.fits fibreff_u_sigma0?.fits fibreff_u_badpixel0?.fits fibreff_u_dtc.fits fibreff_u_sgc.fits fibreff_u_bpc.fits fibreff_u_norm.fits fibreff_u_nsigma.fits fibreff_u_common.fits slitff_u_data0?.fits slitff_u_sigma0?.fits slitff_u_bound0?.fits slitff_u_badpixel0?.fits slitff_u_dtc.fits slitff_u_sgc.fits slitff_u_bpc.fits slitff_u_bnc.fits slitff_u_norm.fits slitff_u_nsigma.fits slitff_u_common.fits mwfxb_*mask.fits fxb_*extco*.fits wfxb_*extco*.fits");
        } else {
            sprintf(command,"rm -f  sp_redu.fits sci_u.fits cor_shape_u.fits m_tbl_redu.fits trap_redu.fits mbias_u.fits ordef_u.fits bkg_u.fits ext?.fits *b_sci_u*.fits middumm*.fits *fxb_u_*0*.fits fibreff_u_data0?.fits fibreff_u_sigma0?.fits fibreff_u_badpixel0?.fits fibreff_u_dtc.fits fibreff_u_sgc.fits fibreff_u_bpc.fits fibreff_u_nsigma.fits fibreff_u_norm.fits fibreff_u_common.fits slitff_u_data0?.fits slitff_u_sigma0?.fits slitff_u_bound0?.fits slitff_u_badpixel0?.fits slitff_u_dtc.fits slitff_u_sgc.fits slitff_u_bpc.fits slitff_u_bnc.fits slitff_u_norm.fits slitff_u_common.fits b_set_u_mf???*.fits *fxb_u_*extco*.fits");
        }

    }
    system(command);

    return cpl_error_get_code();
}

/**@}*/
