/* $Id: hawki_step_refine_offsets.c,v 1.19 2013-09-02 14:35:52 cgarcia Exp $
 *
 * This file is part of the HAWKI Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-09-02 14:35:52 $
 * $Revision: 1.19 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include <math.h>
#include <locale.h>
#include <cpl.h>
#include <string.h>

#include "irplib_utils.h"
#include "irplib_calib.h"

#include "hawki_utils_legacy.h"
#include "hawki_calib.h"
#include "hawki_load.h"
#include "hawki_save.h"
#include "hawki_pfits_legacy.h"
#include "hawki_dfs_legacy.h"
#include "hawki_saa.h"
#include "hawki_bkg.h"
#include "hawki_distortion.h"
#include "hawki_properties_tel.h"
#include "hawki_image_stats.h"

/*-----------------------------------------------------------------------------
                                Define
 -----------------------------------------------------------------------------*/

#define NEGLIG_OFF_DIFF     0.1
#define SQR(x) ((x)*(x))

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

static int hawki_step_refine_offsets_create(cpl_plugin *) ;
static int hawki_step_refine_offsets_exec(cpl_plugin *) ;
static int hawki_step_refine_offsets_destroy(cpl_plugin *) ;
static int hawki_step_refine_offsets(cpl_parameterlist *, cpl_frameset *) ;

static int hawki_step_refine_offsets_retrieve_input_param
(cpl_parameterlist  *  parlist);
static int hawki_step_refine_offsets_fine
(const cpl_frameset  *  science_objects_frames,
 const cpl_frameset  *  reference_objects_frames,
 cpl_bivector        ** refined_offsets,
 cpl_vector          ** correl);

static int hawki_step_refine_offsets_save
(cpl_bivector      ** refined_offsets,
 cpl_vector        ** correlations,
 cpl_parameterlist *  recipe_parlist,
 cpl_frameset      *  recipe_frameset);

static cpl_bivector ** hawki_step_refine_offsets_read_select_objects
(const cpl_frameset *  reference_obj_frames,
 double                first_image_off_x,
 double                first_image_off_y,
 int                   nx,
 int                   ny);

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

static struct 
{
    /* Inputs */
    int                 nbrightest;
    int                 sx ;
    int                 sy ;
    int                 mx ;
    int                 my ;
} hawki_step_refine_offsets_config;

static char hawki_step_refine_offsets_description[] =
"(OBSOLETE) hawki_step_refine_offsets -- utility to refine the nominal offsets.\n"
"This utility will take the offsets in the header as a first approach\n"
"and tries to refine them correlating the input images at the points\n"
"given by the detected objects.\n"
"The input of the recipe files listed in the Set Of Frames (sof-file)\n"
"must be tagged as:\n"
"images.fits "HAWKI_CALPRO_DIST_CORRECTED" or\n"
"images.fits "HAWKI_CALPRO_BKG_SUBTRACTED" and\n"
"det_obj_stats.fits "HAWKI_CALPRO_OBJ_PARAM"\n"
"The recipe creates as an output:\n"
"hawki_step_refine_offsets.fits ("HAWKI_CALPRO_OFFSETS"): Table with refined offsets\n"
"Return code:\n"
"esorex exits with an error code of 0 if the recipe completes successfully\n"
"or 1 otherwise";


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

/*----------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok

  This function is exported.
 */
/*----------------------------------------------------------------------------*/
int cpl_plugin_get_info(cpl_pluginlist * list)
{
    cpl_recipe  *   recipe = cpl_calloc(1, sizeof(*recipe)) ;
    cpl_plugin  *   plugin = &recipe->interface ;

    cpl_plugin_init(plugin,
                    CPL_PLUGIN_API,
                    HAWKI_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "hawki_step_refine_offsets",
                    "(OBSOLETE) Jitter recipe",
                    hawki_step_refine_offsets_description,
                    "ESO Pipeline Group",
                    PACKAGE_BUGREPORT,  
                    hawki_get_license_legacy(),
                    hawki_step_refine_offsets_create,
                    hawki_step_refine_offsets_exec,
                    hawki_step_refine_offsets_destroy) ;

    cpl_pluginlist_append(list, plugin) ;
    
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Create the recipe instance and make it available to the application using the 
  interface. 
 */
/*----------------------------------------------------------------------------*/
static int hawki_step_refine_offsets_create(cpl_plugin * plugin)
{
    cpl_recipe      * recipe ;
    cpl_parameter   * p ;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new() ;
    if (recipe->parameters == NULL)
        return 1;

    /* Fill the parameters list */
    /* --xcorr */
    p = cpl_parameter_new_value("hawki.hawki_step_refine_offsets.xcorr",
                                CPL_TYPE_STRING,
                                "Cross correlation search and measure sizes",
                                "hawki.hawki_step_refine_offsets",
                                "20,20,25,25") ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xcorr") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* --nbrightest */
    p = cpl_parameter_new_value("hawki.hawki_step_refine_offsets.nbrightest",
                                CPL_TYPE_INT,
                                "Number of brightest objects to use",
                                "hawki.hawki_step_refine_offsets",
                                3);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "nbrightest") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* Return */
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int hawki_step_refine_offsets_exec(cpl_plugin * plugin)
{
    cpl_recipe  *   recipe ;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;

    /* Issue a banner */
    hawki_print_banner();

    return hawki_step_refine_offsets(recipe->parameters, recipe->frames) ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int hawki_step_refine_offsets_destroy(cpl_plugin * plugin)
{
    cpl_recipe  *   recipe ;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin ;
    else return -1 ;

    cpl_parameterlist_delete(recipe->parameters) ;
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int hawki_step_refine_offsets(
        cpl_parameterlist   *   parlist, 
        cpl_frameset        *   framelist)
{
    cpl_frameset    *   science_obj_frames ;
    cpl_frameset    *   reference_obj_frames ;
    cpl_bivector    **  refined_offsets;
    cpl_vector      **  correl;
    int                 idet;

    /* Retrieve input parameters */
    if(hawki_step_refine_offsets_retrieve_input_param(parlist))
    {
        cpl_msg_error(__func__, "Wrong parameters");
        return -1;
    }

    /* Identify the RAW and CALIB frames in the input frameset */
    if (hawki_dfs_set_groups_legacy(framelist)) {
        cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
        return -1 ;
    }

    /* Get the object images frames */
    cpl_msg_info(__func__, "Identifying input frames");
    science_obj_frames = 
        hawki_extract_frameset(framelist, HAWKI_CALPRO_DIST_CORRECTED);
    if (science_obj_frames == NULL)
    {
    	science_obj_frames =
    			hawki_extract_frameset(framelist, HAWKI_CALPRO_BKG_SUBTRACTED);
    	if (science_obj_frames == NULL)
    	{
    		cpl_msg_error(__func__, "No science object frames provided (%s)",
    				HAWKI_CALPRO_DIST_CORRECTED);
    		return -1 ;
    	}
    }
    
    /* Get the detected "objects" frame */
    reference_obj_frames = 
        hawki_extract_frameset(framelist, HAWKI_CALPRO_OBJ_PARAM);
    if(cpl_frameset_get_size(reference_obj_frames) != 1)
    {
        cpl_msg_error(__func__, "One object parameters frame must be provided (%s)",
                HAWKI_CALPRO_OBJ_PARAM);
        cpl_frameset_delete(science_obj_frames) ;
        if(reference_obj_frames != NULL)
            cpl_frameset_delete(reference_obj_frames);
        return -1 ;
    }
    
    /* Get the offsets refinement */
    refined_offsets = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_bivector *));
    correl          = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_vector *));
    for(idet = 0 ; idet < HAWKI_NB_DETECTORS; ++idet)
    {
        refined_offsets[idet] = cpl_bivector_new
            (cpl_frameset_get_size(science_obj_frames));
        correl[idet] = cpl_vector_new
            (cpl_frameset_get_size(science_obj_frames));
    }
    if (hawki_step_refine_offsets_fine
            (science_obj_frames, reference_obj_frames, 
             refined_offsets, correl) == -1) 
   {
        cpl_msg_error(__func__, "Cannot refine the objects") ;
        cpl_frameset_delete(reference_obj_frames) ;
        cpl_frameset_delete(science_obj_frames) ;
        for(idet = 0 ; idet < HAWKI_NB_DETECTORS; ++idet)
        {
            cpl_bivector_delete(refined_offsets[idet]);
            cpl_vector_delete(correl[idet]);
        }
        cpl_free(refined_offsets);
        cpl_free(correl);
        return -1 ;
    }

    /* Save the products */
    cpl_msg_info(__func__, "Save the products") ;
    if (hawki_step_refine_offsets_save
            (refined_offsets, correl, parlist, framelist) == -1)
    {
        cpl_msg_warning(__func__,"Some data could not be saved. "
                                 "Check permisions or disk space");
        cpl_frameset_delete(science_obj_frames);
        cpl_frameset_delete(reference_obj_frames);
        for(idet = 0 ; idet < HAWKI_NB_DETECTORS; ++idet)
        {
            cpl_bivector_delete(refined_offsets[idet]);
            cpl_vector_delete(correl[idet]);
        }
        cpl_free(refined_offsets);
        cpl_free(correl);
        return -1 ;
    }
    
    /* Free and return */
    cpl_frameset_delete(science_obj_frames);
    cpl_frameset_delete(reference_obj_frames);
    for(idet = 0 ; idet < HAWKI_NB_DETECTORS; ++idet)
    {
        cpl_bivector_delete(refined_offsets[idet]);
        cpl_vector_delete(correl[idet]);
    }
    cpl_free(refined_offsets);
    cpl_free(correl);
    
    /* Return */
    if (cpl_error_get_code())
    {
        cpl_msg_error(__func__,
                      "HAWK-I pipeline could not recover from previous errors");
        return -1 ;
    }
    else return 0 ;
}

int hawki_step_refine_offsets_retrieve_input_param
(cpl_parameterlist  *  parlist)
{
    cpl_parameter   *   par ;
    const char      *   sval ;
    par = NULL ;
    par = cpl_parameterlist_find
        (parlist, "hawki.hawki_step_refine_offsets.nbrightest");
    hawki_step_refine_offsets_config.nbrightest =
        cpl_parameter_get_int(par);
    par = cpl_parameterlist_find
        (parlist, "hawki.hawki_step_refine_offsets.xcorr");
    sval = cpl_parameter_get_string(par);
    setlocale(LC_NUMERIC, "C"); // Behaviour of sscanf depends on the locale
    if (sscanf(sval, "%d,%d,%d,%d",
               &hawki_step_refine_offsets_config.sx,
               &hawki_step_refine_offsets_config.sy,
               &hawki_step_refine_offsets_config.mx,
               &hawki_step_refine_offsets_config.my)!=4)
    {
        return -1;
    }
    return 0;
}



/*----------------------------------------------------------------------------*/
/**
  @brief     The refinement of the offsets is implemented here
  @return   
 */
/*----------------------------------------------------------------------------*/
static int hawki_step_refine_offsets_fine
(const cpl_frameset  *  science_obj_frames,
 const cpl_frameset  *  reference_obj_frames,
 cpl_bivector        ** refined_offsets,
 cpl_vector          ** correl)
{
    cpl_imagelist    *   in ;
    cpl_bivector     *   nominal_offsets ;
    cpl_bivector     **  reference_objects;
    double           *   offs_est_x ;
    double           *   offs_est_y ;
    double               off_0_x;
    double               off_0_y;
    double               max_x, max_y ;
    int                  idet;
    int                  ioff;
    cpl_propertylist *   header;
    int                  nx;
    int                  ny;

    /* Get the nominal offsets from the header */
    cpl_msg_info(__func__,"Getting the nominal offsets");
    nominal_offsets = hawki_get_header_tel_offsets(science_obj_frames); 
    if (nominal_offsets  == NULL) 
    {
        cpl_msg_error(__func__, "Cannot load the header offsets") ;
        return -1;
    }
    offs_est_x = cpl_bivector_get_x_data(nominal_offsets);
    offs_est_y = cpl_bivector_get_y_data(nominal_offsets);
    
    /* Print the header offsets */
    cpl_msg_indent_more();
    for (ioff=0 ; ioff<cpl_bivector_get_size(nominal_offsets) ; ioff++) 
    {
        cpl_msg_info(__func__, "Telescope offsets (Frame %d): %g %g", ioff+1,
                offs_est_x[ioff], offs_est_y[ioff]) ;
    }
    cpl_msg_indent_less();
    
    /* Get the size of the detectors of the first extension */
    header = cpl_propertylist_load
        (cpl_frame_get_filename
                (cpl_frameset_get_position_const(science_obj_frames, 0)), 1);
    nx = hawki_pfits_get_naxis1(header);
    ny = hawki_pfits_get_naxis2(header);
    cpl_propertylist_delete(header);

    /* Get the first offset to all offsets */
    off_0_x = offs_est_x[0];
    off_0_y = offs_est_y[0];

    /* Get the objects (anchor points)*/
    /* They are already in the "first image" reference system */
    reference_objects = 
        hawki_step_refine_offsets_read_select_objects(reference_obj_frames,
                                                      off_0_x,
                                                      off_0_y,
                                                      nx,
                                                      ny);
    if(reference_objects == NULL)
    {
        cpl_msg_error(__func__,"Error reading the reference objects");
        cpl_bivector_delete(nominal_offsets);
        return -1;
    }

    /* Subtract the first offset to all offsets */
    max_x = max_y = 0.0 ;
    for (ioff=1 ; ioff<cpl_bivector_get_size(nominal_offsets) ; ioff++) 
    {
        offs_est_x[ioff] -= off_0_x;
        offs_est_y[ioff] -= off_0_y;
        if (fabs(offs_est_x[ioff]) > max_x) max_x = fabs(offs_est_x[ioff]) ;
        if (fabs(offs_est_y[ioff]) > max_y) max_y = fabs(offs_est_y[ioff]) ;
    }
    offs_est_x[0] = offs_est_y[0] = 0.00 ;

    /* Get the opposite offsets. This is to change from 
     * telescope convention to cpl convention */ 
    cpl_vector_multiply_scalar(cpl_bivector_get_x(nominal_offsets), -1.0);
    cpl_vector_multiply_scalar(cpl_bivector_get_y(nominal_offsets), -1.0);

    /* Loop on the detectors */
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        cpl_msg_info(__func__, "Working on detector number %d", idet+1) ;
        cpl_msg_indent_more();
        
        /* Load the input data */
        cpl_msg_info(__func__, "Loading the input data") ;
        cpl_msg_indent_more() ;
        if ((in = hawki_load_detector(science_obj_frames,
                                  idet+1, CPL_TYPE_FLOAT)) == NULL) 
        {
            cpl_msg_error(__func__, "Cannot load chip %d", idet+1) ;
            cpl_bivector_delete(nominal_offsets) ;
            for (idet=0 ; idet< HAWKI_NB_DETECTORS ; idet++)
            {
                cpl_bivector_delete(reference_objects[idet]);
            }
            cpl_free(reference_objects);
            cpl_msg_indent_less() ;
            cpl_msg_indent_less() ;
            return -1;
        }

        /* Get the refinement */
        cpl_msg_info(__func__, "Getting the refinement");
        cpl_msg_indent_more() ;
        if (hawki_geom_refine_images_offsets
                (in, 
                 nominal_offsets,
                 reference_objects[idet],
                 hawki_step_refine_offsets_config.sx,
                 hawki_step_refine_offsets_config.sy,
                 hawki_step_refine_offsets_config.mx,
                 hawki_step_refine_offsets_config.my,
                 refined_offsets[idet],
                 correl[idet]) == -1)
        {
            cpl_msg_error(__func__, "Cannot apply the shift and add") ;
            cpl_imagelist_delete(in) ;
            cpl_bivector_delete(nominal_offsets) ;
            for (idet=0 ; idet< HAWKI_NB_DETECTORS ; idet++)
                cpl_bivector_delete(reference_objects[idet]);
            cpl_free(reference_objects);
            cpl_msg_indent_less() ;
            cpl_msg_indent_less() ;
            return -1;
        }
        
        /* Convert to "telescope criteria" */
        /* Also add the offset of the first image */
        cpl_vector_multiply_scalar
            (cpl_bivector_get_x(refined_offsets[idet]), -1.0);
        cpl_vector_multiply_scalar
            (cpl_bivector_get_y(refined_offsets[idet]), -1.0);
        cpl_vector_add_scalar
            (cpl_bivector_get_x(refined_offsets[idet]), off_0_x);
        cpl_vector_add_scalar
            (cpl_bivector_get_y(refined_offsets[idet]), off_0_y);

        /* Print the new offsets */
        for (ioff=0 ; ioff<cpl_bivector_get_size(refined_offsets[idet]); ioff++) 
        {
            cpl_msg_info(__func__,"Refined telescope offsets (Frame %d): %g %g",
                         ioff+1,
                         cpl_vector_get(cpl_bivector_get_x
                                        (refined_offsets[idet]), ioff),
                         cpl_vector_get(cpl_bivector_get_y
                                        (refined_offsets[idet]), ioff));
        }
        cpl_imagelist_delete(in) ;
        cpl_msg_indent_less() ;
        cpl_msg_indent_less() ;
    }
    
    /* Freeing */
    cpl_bivector_delete(nominal_offsets);
    for (idet=0 ; idet< HAWKI_NB_DETECTORS ; idet++)
        cpl_bivector_delete(reference_objects[idet]);
    cpl_free(reference_objects);

    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    This function saves the refined objects as a FITS table 
  @param    refined_offsets  The compute refined offsets
  @param    recipe_parlist   The parameters of the recipe
  @param    recipe_frameset  The frameset the recipe was called with
  @return   0 if sucess, -1 elsewhere.
 */
/*----------------------------------------------------------------------------*/

static int hawki_step_refine_offsets_save
(cpl_bivector      ** refined_offsets,
 cpl_vector        ** correlations,
 cpl_parameterlist *  recipe_parlist,
 cpl_frameset      *  recipe_frameset)
{
    cpl_table        ** offset_tables;
    int                 ioff;
    int                 idet;
    int                 noff;
    const char       *  recipe_name = "hawki_step_refine_offsets";
    cpl_errorstate      error_prevstate = cpl_errorstate_get();
    
    
    /* Convert the offsets to a table */
    offset_tables = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_table *));
    for(idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
    {
        offset_tables[idet] = cpl_table_new
            (cpl_bivector_get_size(refined_offsets[idet]));
        cpl_table_new_column(offset_tables[idet], 
                             HAWKI_COL_OFFSET_X, CPL_TYPE_FLOAT);
        cpl_table_set_column_unit(offset_tables[idet],HAWKI_COL_OFFSET_X,"pix");
        cpl_table_new_column(offset_tables[idet], 
                             HAWKI_COL_OFFSET_Y, CPL_TYPE_FLOAT);
        cpl_table_set_column_unit(offset_tables[idet],HAWKI_COL_OFFSET_Y,"pix");
        cpl_table_new_column(offset_tables[idet], 
                             HAWKI_COL_CORRELATION, CPL_TYPE_FLOAT);
        noff = cpl_bivector_get_size(refined_offsets[idet]);
        for(ioff = 0; ioff < noff; ++ioff)
        {
            double xoffset, yoffset, corr;
            xoffset = cpl_vector_get
                (cpl_bivector_get_x(refined_offsets[idet]), ioff);
            yoffset = cpl_vector_get
                (cpl_bivector_get_y(refined_offsets[idet]), ioff);
            corr    = cpl_vector_get(correlations[idet], ioff);
            cpl_table_set
                (offset_tables[idet], HAWKI_COL_OFFSET_X, ioff, xoffset);
            cpl_table_set
                (offset_tables[idet], HAWKI_COL_OFFSET_Y, ioff, yoffset);
            cpl_table_set
                (offset_tables[idet], HAWKI_COL_CORRELATION, ioff, corr);
        }
    }
    
    /* Write the table with the statistics */
    if (hawki_tables_save(recipe_frameset, 
                          recipe_parlist,
                          recipe_frameset,
                          (const cpl_table **)offset_tables,
                          recipe_name,
                          HAWKI_CALPRO_OFFSETS,
                          HAWKI_PROTYPE_OFFSETS,
                          NULL,
                          NULL,
                          "hawki_step_refine_offsets.fits") != CPL_ERROR_NONE) 
    {
        cpl_msg_error(__func__, "Cannot save the first extension table") ;
        for(idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
            cpl_table_delete(offset_tables[idet]);
        cpl_free(offset_tables);
        return -1 ;
    }

    /* Free and return */
    for(idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
        cpl_table_delete(offset_tables[idet]);
    cpl_free(offset_tables);
    if(!cpl_errorstate_is_equal(error_prevstate))
    {
        cpl_error_reset();
        return -1;
    }
    return  0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    This function reads the objects that are going to be used as 
            anchor points 
  @param    refined_offsets  The compute refined offsets
  @param    recipe_parlist   The parameters of the recipe
  @param    recipe_frameset  The frameset the recipe was called with
  @return   0 if sucess, -1 elsewhere.
  
  The function returns the positions of the objects in the "first image"
  reference system (using first_image_off_x, first_image_off_y). Note that 
  the objects are originally referred to the combined image offsets. The 
  function performs the necessary offset correction.
  
 */
/*----------------------------------------------------------------------------*/
static cpl_bivector ** hawki_step_refine_offsets_read_select_objects
(const cpl_frameset *  reference_obj_frames,
 double                first_image_off_x,
 double                first_image_off_y,
 int                   nx,
 int                   ny)
{
    const cpl_frame        *  reference_obj_frame;
    cpl_table              ** obj_param;
    cpl_propertylist       *  sort_column;
    cpl_bivector           ** reference_objects;
    int                    idet;
    
    /* Get the objects */
    cpl_msg_info(__func__,"Getting the reference object positions");
    reference_obj_frame = cpl_frameset_get_position_const(reference_obj_frames, 0);
    obj_param = hawki_load_tables(reference_obj_frame);
    if(obj_param == NULL)
    {
        cpl_msg_error(__func__,"Could not read the reference objects parameters");
        return NULL;
    }
    
    /* Create the sorting criteria: by flux */
    sort_column = cpl_propertylist_new();
    cpl_propertylist_append_bool(sort_column, HAWKI_COL_OBJ_FLUX, CPL_TRUE);

    /* Allocate partially the reference objects */
    reference_objects = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_bivector *));
    
    /* Loop on detectors */
    cpl_msg_indent_more();
    for (idet=0 ; idet< HAWKI_NB_DETECTORS ; idet++)
    {
        cpl_propertylist * objects_plist;
        cpl_vector       * obj_x;
        cpl_vector       * obj_y;
        int                nobj;
        int                nselect;
        int                iobj;
        int                ext_nb;
        double             reference_offset_x;
        double             reference_offset_y;
        
        /* Get the global offset */
        /* This allows to know which is the reference point of the detected 
         * objects positions (which are referred in general to the combined image */
        ext_nb=hawki_get_ext_from_detector
            (cpl_frame_get_filename(reference_obj_frame), idet + 1);
        objects_plist = cpl_propertylist_load
             (cpl_frame_get_filename(reference_obj_frame), ext_nb);
        reference_offset_x =
            hawki_pfits_get_comb_cumoffsetx(objects_plist);
        reference_offset_y = 
            hawki_pfits_get_comb_cumoffsety(objects_plist);
        if(cpl_error_get_code() != CPL_ERROR_NONE)
        {
            cpl_msg_error(__func__,"Could not find keywords "
                          "ESO QC COMBINED CUMOFFSETX,Y in reference objects frame");            
            cpl_propertylist_delete(objects_plist);
            cpl_propertylist_delete(sort_column);
            return NULL;
        }
        cpl_msg_info(__func__,"Objects offsets wrt telescope: %f %f", 
                     reference_offset_x, reference_offset_y);
        cpl_propertylist_delete(objects_plist);
        
        /* Sort the table by flux */
        cpl_table_sort(obj_param[idet], sort_column);
        nobj = cpl_table_get_nrow(obj_param[idet]); 
        
        /* Allocate objects vector */
        reference_objects[idet] = cpl_bivector_new(nobj);
        obj_x = cpl_bivector_get_x(reference_objects[idet]);
        obj_y = cpl_bivector_get_y(reference_objects[idet]);
        cpl_msg_info(__func__, "Number of objects in chip %d: %d", idet+1,nobj);
        
        /* Keep only those objects within the first image */
        cpl_table_unselect_all(obj_param[idet]);
        for(iobj = 0 ; iobj < nobj; ++iobj)
        {
            double xpos_orig = cpl_table_get
                (obj_param[idet], HAWKI_COL_OBJ_POSX, iobj, NULL);
            double ypos_orig = cpl_table_get
                (obj_param[idet], HAWKI_COL_OBJ_POSY, iobj, NULL);
            double xpos_rel = xpos_orig - reference_offset_x + first_image_off_x;
            double ypos_rel = ypos_orig - reference_offset_y + first_image_off_y;
            if(xpos_rel < 0.0 || xpos_rel >= nx ||
               ypos_rel < 0.0 || ypos_rel >= ny)
            {
                cpl_table_select_row(obj_param[idet], iobj);
            }
        }
        cpl_table_erase_selected(obj_param[idet]);
        nobj = cpl_table_get_nrow(obj_param[idet]);
        cpl_msg_info(__func__, "Number of objects within limits of detector "
                               "in chip %d: %d", idet+1,nobj);
        
        /* Apply the flux criteria */
        nselect = hawki_step_refine_offsets_config.nbrightest;
        if(nselect < 0 || nselect > nobj)
            nselect = nobj;
        cpl_msg_info(__func__, "Number of selected objects: %d", nselect);
        for(iobj = 0 ; iobj < nselect; ++iobj)
        {
            double xpos_orig = cpl_table_get
                (obj_param[idet], HAWKI_COL_OBJ_POSX, iobj, NULL);
            double ypos_orig = cpl_table_get
                (obj_param[idet], HAWKI_COL_OBJ_POSY, iobj, NULL);

            cpl_vector_set
                (obj_x, iobj, xpos_orig - reference_offset_x + first_image_off_x);
            cpl_vector_set
                (obj_y, iobj, ypos_orig - reference_offset_y + first_image_off_y);
            cpl_msg_debug(__func__,"Using anchor point at %f,%f",
                          cpl_vector_get(obj_x,iobj),
                          cpl_vector_get(obj_y,iobj));
            
        }
        cpl_vector_set_size(obj_x, nselect);
        cpl_vector_set_size(obj_y, nselect);
        
    }
    cpl_msg_indent_less();
    
    /* Freeing */
    for (idet=0 ; idet< HAWKI_NB_DETECTORS ; idet++) 
        cpl_table_delete(obj_param[idet]);
    cpl_free(obj_param);
    cpl_propertylist_delete(sort_column);
    
    return reference_objects;
}
