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

/*
 * $Author: amodigli $
 * $Date: 2013-04-12 14:01:44 $
 * $Revision: 1.57 $
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_extract  Extract objects (xsh_extract)
 * @ingroup drl_functions
 *
 * Function ...
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>
#include <xsh_drl.h>

#include <xsh_utils_table.h>
#include <xsh_badpixelmap.h>
#include <xsh_data_pre.h>
#include <xsh_dfs.h>
#include <xsh_pfits.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_fit.h>
#include <xsh_data_instrument.h>
#include <xsh_data_localization.h>
#include <xsh_data_rec.h>
#include <xsh_ifu_defs.h>

#include <cpl.h>

/*-----------------------------------------------------------------------------
  Functions prototypes
  -----------------------------------------------------------------------------*/
#define debug_extraction 0
/*-----------------------------------------------------------------------------
  Implementation
  -----------------------------------------------------------------------------*/
/** 
  @brief
    Simple 1D extraction of point source like object
  @param[in] rec_frame
    Rectified frame (from xsh_rectify)
  @param[in] loc_frame 
    Localization table 
  @param[in] instrument 
    Instrument pointer
  @param[in] extract_par 
    Parameters for object extraction
  @param[in] tag
    Product catg
  @param[out] res_frame_ext
    extracted frame in ESO format

  @return
    A rectified frame 1D
*/
static cpl_frame *
xsh_extract_with_tag(cpl_frame * rec_frame, cpl_frame* loc_frame,
    xsh_instrument* instrument, xsh_extract_param* extract_par,
    const char * tag, cpl_frame** res_frame_ext) {
  cpl_frame *res_frame = NULL;
  xsh_localization *loc_list = NULL;
  xsh_rec_list *rec_list = NULL, *result_list = NULL;
  int i = 0;
  double slit_step = 0.0;
  char * fname = NULL;
  char * fname_drl = NULL;
  char * tag_drl = NULL;
  double slit_ext_min = 0;
  double slit_ext_max = 0;
  cpl_propertylist* plist = NULL;
  const char* name = NULL;

  XSH_ASSURE_NOT_NULL( rec_frame);

  xsh_msg_dbg_medium( "Entering xsh_extract_with_tag(%s)", tag);
  xsh_msg_dbg_medium( "Parameters");
  xsh_msg_dbg_medium( "  method %s", EXTRACT_METHOD_PRINT(extract_par->method));

  /* Load rectified table */
  check( rec_list = xsh_rec_list_load( rec_frame, instrument));
  check( slit_step = xsh_pfits_get_rectify_bin_space( rec_list->header));

  if (extract_par->method == NOD_METHOD) {
      double nod_throw=0;

    name = cpl_frame_get_filename(rec_frame);
    plist = cpl_propertylist_load(name, 0);
    slit_ext_min = xsh_pfits_get_extract_slit_min(plist);
    slit_ext_max = xsh_pfits_get_extract_slit_max(plist) - 2;
    nod_throw = xsh_pfits_get_nodthrow(plist);

    //xsh_msg("before slit min: %g max: %g",slit_ext_min,slit_ext_max);
    int nslit = xsh_rec_list_get_nslit(rec_list, 0);
    int hslit = round(0.5 * nod_throw / slit_step);
    slit_ext_max = nslit/2 + hslit;
    slit_ext_min = nslit/2 - hslit;

    //xsh_msg("after slit min: %g max: %g",slit_ext_min,slit_ext_max);
    xsh_free_propertylist(&plist);
  }


  /* Load localization table */
  if ((extract_par->method != FULL_METHOD)
      && (extract_par->method != NOD_METHOD)) {
    XSH_ASSURE_NOT_NULL( loc_frame);
    check( loc_list = xsh_localization_load( loc_frame));
  }check(
      result_list = xsh_rec_list_create_with_size( rec_list->size, instrument));
  /* loop over order */
  for (i = 0; i < rec_list->size; i++) {
    int order = xsh_rec_list_get_order(rec_list, i);
    int nslit = xsh_rec_list_get_nslit(rec_list, i);
    int nlambda = xsh_rec_list_get_nlambda(rec_list, i);
    float* slit = xsh_rec_list_get_slit(rec_list, i);
    double* lambda = xsh_rec_list_get_lambda(rec_list, i);
    float slitmin = slit[0];
    float slitmax = slit[nslit - 1];
    double lambdamin = lambda[0];
    double lambdamax = lambda[nlambda - 1];
    float *flux = xsh_rec_list_get_data1(rec_list, i);
    float *errs = xsh_rec_list_get_errs1(rec_list, i);
    int *qual = xsh_rec_list_get_qual1(rec_list, i);
    int ilambda, islit;
    /* result REC Frame */
    double *res_lambda = NULL;
    float *res_flux = NULL, *res_errs = NULL;
    int *res_qual = NULL;

    xsh_msg_dbg_medium(
        "order %d nslit %d (%f,%f) nlambda %d (%f %f)", order, nslit, slitmin, slitmax, nlambda, lambdamin, lambdamax);

    check( xsh_rec_list_set_data_size( result_list, i, order, nlambda, 1));

    check( res_lambda = xsh_rec_list_get_lambda( result_list, i));
    check( res_flux = xsh_rec_list_get_data1( result_list, i));
    check( res_errs = xsh_rec_list_get_errs1( result_list, i));
    check( res_qual = xsh_rec_list_get_qual1( result_list, i));

    for (ilambda = 0; ilambda < nlambda; ilambda++) {
      double slit_lo = 0.0, slit_up = 0.0;
      double ypix_low, ypix_up;

      int ylow = 0, yup = 0;
      double y_frac_low, y_frac_up;
      double fluxval = 0.0;
      double errval = 0.0;
      int qualval = 0;
      int idx;

      /* Localisation method */
      if (extract_par->method == LOCALIZATION_METHOD) {
        check(
            slit_lo = cpl_polynomial_eval_1d( loc_list->edglopoly, lambda[ilambda], NULL));
        check(
            slit_up = cpl_polynomial_eval_1d( loc_list->edguppoly, lambda[ilambda], NULL));

        xsh_msg_dbg_medium(
            "LOCALIZATION_METHOD get poly ok lambda %f slit_lo %f slit_up %f", lambda[ilambda], slit_lo, slit_up);
        /* Find limit in slit */
        ypix_low = (slit_lo - slit[0]) / slit_step;
        ypix_up = (slit_up - slit[0]) / slit_step;

        if (ypix_low < 0.0) {
          xsh_msg(
              "WARNING : localization give low pixel \
            outside of image : fix it to zero");
          ypix_low = 0.0;
        }
        if (ypix_up >= nslit) {
          xsh_msg(
              "WARNING : localization give up pixel \
            outside of image : fix it to %d", nslit-1);
          ypix_up = nslit - 1;
        }
        xsh_msg_dbg_medium("ypix_low %f ypix_up %f", ypix_low, ypix_up);

        ylow = (int) (ypix_low);
        yup = (int) (ypix_up);

        y_frac_up = ypix_up - yup;
        y_frac_low = 1 - (ypix_low - ylow);

        xsh_msg_dbg_medium("ylow %d yup %d", ylow, yup);

        /* consider the fraction of pixels for ylow */
        idx = ylow * nlambda + ilambda;
        if (idx >= (nlambda * nslit)) {
          xsh_msg_dbg_high(
              "Index Out of Image: %d >= %d (ylow: %d, ilambda: %d)", idx, nlambda*nslit, ylow, ilambda);
          continue;
        }
        fluxval += y_frac_low * flux[idx];
        errval += y_frac_low * (errs[idx] * errs[idx]);
        qualval |= qual[idx];

        /* Integrate on complete pixels */
        for (islit = ylow + 1; islit < yup; islit++) {
          idx = islit * nlambda + ilambda;
          fluxval += flux[idx];
          errval += errs[idx]*errs[idx];
          qualval |= qual[idx];
        }

        /* consider the fraction of pixels for yup */
        if (yup < nslit) {
          idx = yup * nlambda + ilambda;
          fluxval += y_frac_up * flux[idx];
          errval += y_frac_up * errs[idx]*errs[idx];
          qualval |= qual[idx];
        }

        /* Not used any more */
      } else if (extract_par->method == FULL_METHOD) {
        //xsh_msg_warning("ymin=%d ymax=%d",0,nslit);
        for (islit = 0; islit < nslit; islit++) {
          idx = islit * nlambda + ilambda;
          fluxval += flux[idx];
          errval += errs[idx]*errs[idx];
          qualval |= qual[idx];
        }

        /* Nod method */
      } else if (extract_par->method == NOD_METHOD) {
        /*
         xsh_msg_warning("slit_inf=%f slit_upp=%f slit0 %f",
         slit_ext_min,slit_ext_max,slit[0]);
         */

        /* original definition of extraction range
        ylow = (slit_ext_min - slit[0]) / slit_step;
        yup = (slit_ext_max - slit[0]) / slit_step;
         */

        ylow = slit_ext_min;
        yup = slit_ext_max;

        for (islit = ylow; islit <= yup; islit++) {
          idx = islit * nlambda + ilambda;
          fluxval += flux[idx];
          errval += errs[idx]*errs[idx];
          qualval |= qual[idx];
        }
      }
      res_lambda[ilambda] = lambda[ilambda];
      res_flux[ilambda] = fluxval;
      res_errs[ilambda] = sqrt(errval);
      res_qual[ilambda] = qualval;
    }
  }

  check( cpl_propertylist_append ( result_list->header, rec_list->header));
  check( xsh_pfits_set_pcatg( result_list->header, tag));
  fname = xsh_stringcat_any(tag, ".fits", (void*)NULL);
  tag_drl = xsh_stringcat_any(tag, "_DRL", (void*)NULL);
  fname_drl = xsh_stringcat_any(tag_drl, ".fits", (void*)NULL);

  check( *res_frame_ext= xsh_rec_list_save2( result_list,fname, tag));
  check( res_frame = xsh_rec_list_save( result_list, fname_drl, tag_drl,0));

  xsh_msg_dbg_medium("%s created", fname);

  cleanup: xsh_rec_list_free(&result_list);
  xsh_rec_list_free(&rec_list);
  xsh_localization_free(&loc_list);

  XSH_FREE( tag_drl);
  XSH_FREE( fname_drl);
  XSH_FREE( fname);

  return res_frame;
}

static cpl_error_code
xsh_extraction_get_slit_minmax(xsh_localization *loc_list,
    const double lambda, const double slit_0, const double slit_step,
    const int nslit, int* ylow, int* yup,double* y_frac_up,double* y_frac_low) {
  double slit_lo = 0;
  double slit_up = 0;
  double ypix_low = 0;
  double ypix_up = 0;

  slit_lo = cpl_polynomial_eval_1d(loc_list->edglopoly, lambda, NULL);
  slit_up = cpl_polynomial_eval_1d(loc_list->edguppoly, lambda, NULL);

  xsh_msg_dbg_medium(
      "LOCALIZATION_METHOD get poly ok lambda %f slit_lo %f slit_up %f", lambda, slit_lo, slit_up);
  /* Find limit in slit */
  ypix_low = (slit_lo - slit_0) / slit_step;
  ypix_up = (slit_up - slit_0) / slit_step;

  if (ypix_low < 0.0) {
    xsh_msg(
        "WARNING : localization give low pixel \
    outside of image : fix it to zero");
    ypix_low = 0.0;
  }
  if (ypix_up >= nslit) {
    xsh_msg(
        "WARNING : localization give up pixel \
    outside of image : fix it to %d", nslit-1);
    ypix_up = nslit - 1;
  }
  xsh_msg_dbg_medium("ypix_low %f ypix_up %f", ypix_low, ypix_up);

  *ylow = (int) (ypix_low);
  *yup = (int) (ypix_up);

  *y_frac_up = ypix_up - *yup;
  *y_frac_low = 1 - (ypix_low - *ylow);
  return cpl_error_get_code();
}

cpl_error_code xsh_extract_clean_slice(const float* flux, const float* errs,
    int* qual, const double* lambda,
    const int ilambda, xsh_instrument* instrument, const int slit_min,
    const int slit_max, const int nlambda, const int nslit,
    const int mask_hsize, double* fluxval, double* errval, int* qualval,
    float* pima,float* fima,float* rima) {

  int mask_size = 0;

  int nbad = 0;
  int islit = 0;
  int idx = 0;
  const int slit_size = slit_max - slit_min + 1;
  double den_fct = 0;
  double num_fct = 0;
  int mdx = 0;
  int m = 0;

  float* prof = NULL;

  double ipol_fct = 0;
  cpl_vector* vec_wave_flag=NULL;
  double* pflag=NULL;
  int nflag=0;
  double inv_err2=0;
  double err2=0;
  int ilambda_min=0;
  int ilambda_max=0;
  int mstart=0;
  int mend=0;
  //int code_extrap=1048576;
  //int code_special=2143289343;
  //int code_special=2147483647;
  int decode_bp = instrument->decode_bp;
  //decode_bp = code_special;
  /* check if there are bad pixels to be interpolated, else we can integrate */
  for (islit = slit_min; islit <= slit_max; islit++) {
    idx = islit * nlambda + ilambda;
    if (( qual[idx] & decode_bp ) > 0) {
    //if ((qual[idx] & code_special) > 0) {
      nbad++;
    }
  }

  //xsh_msg_debug("lambda=%g nbad=%d",lambda[ilambda],nbad);
  if (nbad == slit_size || nbad == 0) {
    /* only good pixel, trivial case, we can sum up */
    for (islit = slit_min; islit <= slit_max; islit++) {
      idx = islit * nlambda + ilambda;
      //xsh_msg("flux=%g",flux[idx]);
      *fluxval += flux[idx];
      *errval += (errs[idx] * errs[idx]);
      *qualval |= qual[idx];
#if debug_extraction
      fima[idx]=flux[idx];
      rima[idx]=(flux[idx]-flux[idx])/errs[idx];
#endif
    }
  } else { // 0<nbad<nslit
    xsh_msg_debug("Found %d bad pix at lambda=%g", nbad, lambda[ilambda]);

    /* determine the wavelength index limits for integration */
    ilambda_min=ilambda-mask_hsize;
    ilambda_max=ilambda+mask_hsize;
    ilambda_min = (ilambda_min>=0) ? ilambda_min : 0;
    ilambda_max = (ilambda_max<nlambda) ? ilambda_max : nlambda-1;

    mask_size=ilambda_max-ilambda_min+1;
    mstart=ilambda_min-ilambda;
    mend=ilambda_max-ilambda;

    /* This part of the code is flagging wavelength with at least one bad pixel */
    xsh_free_vector(&vec_wave_flag);
    vec_wave_flag=cpl_vector_new(mask_size);
    pflag=cpl_vector_get_data(vec_wave_flag);
    for (m = 0; m < mask_size; m++) {
      pflag[m]=0;
    }
    nflag=0;
    for (m = mstart; m <= mend; m++) {
      for (islit = slit_min; islit <= slit_max; islit++) {
        mdx = islit * nlambda + ilambda + m;
        //xsh_msg("qual[%d,%d]=%d",m,islit,qual[mdx]);
        if (( qual[mdx] & decode_bp ) > 0) {

        //if ((qual[mdx] & code_special) > 0) {
          pflag[m-mstart]=1;
          nflag++;
          break;
        }
      }
    }

    //xsh_msg("m_start=%d m_end=%d",mstart,mend);
    

    /* if all wavelength within the extraction mask have at least one bad pixel then throw exception */
    if (nflag == mask_size) {
      /* 2a: exception: Too few good pixels for interpolation, flag this pixel */
      //xsh_msg_warning("Too few good pixels for interpolation at lambda %g. Try to increase interpolation box parameter", lambda[ilambda]);
      *qualval = QFLAG_MISSING_DATA;

    } else {
      //xsh_msg_warning("Determine interpolation profile at lambda %g.", lambda[ilambda]);
      /* 3: create profile */
      /* at least one good wavelength==>that means we can define an interpolation profile */
      prof = cpl_malloc(nslit * sizeof(float));
      for (islit = 0; islit < nslit; islit++) {
        prof[islit] = 0;
      }

      /* build profile using good wavelength */
      // For every wavelength coord
      for (m = mstart; m <= mend; m++) {
        if (pflag[m-mstart] == 0) {
          // For every spatial position *across* that wavelength
          for (islit = slit_min; islit <= slit_max; islit++) {
            mdx = islit * nlambda + ilambda+m;
            // Profile at that spatial position includes this flux
            prof[islit] += flux[mdx];
          }
        }
      }

      /* use the profile to calculate the interpolation factor */
      num_fct = 0;
      den_fct = 0;
      for (islit = slit_min; islit <= slit_max; islit++) {
        idx = islit * nlambda + ilambda;
        if (( qual[idx] & decode_bp ) == 0) {
        //if ((qual[idx] & code_special) == 0) {
          err2 = errs[idx] * errs[idx];
          inv_err2=1./ err2;
          num_fct += prof[islit] * flux[idx] * inv_err2;
          den_fct += prof[islit] * prof[islit] * inv_err2 ;
        }
#if debug_extraction
        pima[idx]=prof[islit];
#endif
      }/* end loop over slit */
      ipol_fct = num_fct / den_fct;

      /* finally apply interpolation */
      //xsh_msg("slit_min=%d slit_max=%d",slit_min,slit_max);
      for (islit = slit_min; islit <= slit_max; islit++) {
        idx = islit * nlambda + ilambda;
        if ((qual[idx] & decode_bp ) == 0) {
        //if ((qual[idx] & code_special) == 0) {
          *fluxval += flux[idx];
          *errval += (errs[idx] * errs[idx]);
          *qualval |= qual[idx];
#if debug_extraction
          fima[idx]=flux[idx];
          rima[idx]=(flux[idx]-flux[idx])/errs[idx];
#endif
        } else {
          *fluxval += ipol_fct * prof[islit];
          *errval += (prof[islit] * prof[islit])/ den_fct;
          *qualval |= QFLAG_INTERPOL_FLUX;
#if debug_extraction
          fima[idx]=ipol_fct * prof[islit];
          rima[idx]=(flux[idx]-ipol_fct * prof[islit])/((prof[islit] * prof[islit])/ den_fct);
#endif
        }
      }

      XSH_FREE(prof);
    } /* end case at least some bad pixels is found
        but not too many to interpolate bad ones */
    xsh_free_vector(&vec_wave_flag);

  }/* end case where we can try to interpolate a bad pixel */
  //xsh_msg("ck3 flux=%g err=%g qual=%d",fluxval,errval,qual);

  return cpl_error_get_code();

}


/** 
  @brief
    Simple 1D extraction of point source like object
  @param[in] rec_frame
    Rectified frame (from xsh_rectify)
  @param[in] loc_frame 
    Localization table 
  @param[in] instrument 
    Instrument pointer
  @param[in] extract_par 
    Parameters for object extraction
  @param[in] tag
    Product catg
  @param[out] res_frame_ext
    extracted frame in ESO format

  @return
    A rectified frame 1D
*/
static cpl_frame *
xsh_extract_clean_with_tag(cpl_frame * rec_frame, cpl_frame* loc_frame,
    xsh_instrument* instrument, xsh_extract_param* extract_par,
    xsh_interpolate_bp_param * ipol_bp_par, const char * tag,
    cpl_frame** res_frame_ext) {
  cpl_frame *res_frame = NULL;
  xsh_localization *loc_list = NULL;
  xsh_rec_list *rec_list = NULL, *result_list = NULL;
  int i = 0;
  double slit_step = 0.0;
  char * fname = NULL;
  char * fname_drl = NULL;
  char * tag_drl = NULL;
  double slit_ext_min = 0;
  double slit_ext_max = 0;
  cpl_propertylist* plist = NULL;
  const char* name = NULL;

  XSH_ASSURE_NOT_NULL( rec_frame);
  xsh_msg("Extract method clean with tag");

  xsh_msg_dbg_medium( "Entering xsh_extract_with_tag(%s)", tag);
  xsh_msg_dbg_medium( "Parameters");
  xsh_msg_dbg_medium( "  method %s", EXTRACT_METHOD_PRINT(extract_par->method));

  /* Load rectified table */
  check( rec_list = xsh_rec_list_load( rec_frame, instrument));
  check( slit_step = xsh_pfits_get_rectify_bin_space( rec_list->header));
  if (extract_par->method == NOD_METHOD) {
    name = cpl_frame_get_filename(rec_frame);
    plist = cpl_propertylist_load(name, 0);
    slit_ext_min = xsh_pfits_get_extract_slit_min(plist);
    slit_ext_max = xsh_pfits_get_extract_slit_max(plist) - 2;
    double nod_throw = xsh_pfits_get_nodthrow(plist);
    //xsh_msg("before slit min: %g max: %g",slit_ext_min,slit_ext_max);
    int nslit = xsh_rec_list_get_nslit(rec_list, 0);
    int hslit =0;
    if (nod_throw <= 5.5 ) {
       hslit = round(0.5 * nod_throw / slit_step);
    } else {
       hslit = round(0.5 * (11.-nod_throw) / slit_step);
    }
    xsh_msg("nod_throw=%g slit_step=%g nslit=%d hslit=%d",nod_throw,slit_step,nslit,hslit);
    slit_ext_max = nslit/2 + hslit;
    slit_ext_min = nslit/2 - hslit;
    xsh_msg("after slit min: %g max: %g",slit_ext_min,slit_ext_max);
    xsh_free_propertylist(&plist);
  }



  /* Load localization table */
  if ((extract_par->method != FULL_METHOD)
      && (extract_par->method != NOD_METHOD)) {
    XSH_ASSURE_NOT_NULL( loc_frame);
    xsh_msg("loc_frame name=%s", cpl_frame_get_filename(loc_frame));
    check( loc_list = xsh_localization_load( loc_frame));
  }check(
      result_list = xsh_rec_list_create_with_size( rec_list->size, instrument));
  int mask_hsize = ipol_bp_par->mask_hsize;
  /* loop over order */
  xsh_msg("Number of orders=%d", rec_list->size);
  for (i = 0; i < rec_list->size; i++) {
    int order = xsh_rec_list_get_order(rec_list, i);
    int nslit = xsh_rec_list_get_nslit(rec_list, i);
    int nlambda = xsh_rec_list_get_nlambda(rec_list, i);
    float* slit = xsh_rec_list_get_slit(rec_list, i);
    double* lambda = xsh_rec_list_get_lambda(rec_list, i);
    float slitmin = slit[0];
    float slitmax = slit[nslit - 1];
    double lambdamin = lambda[0];
    double lambdamax = lambda[nlambda - 1];
    float *flux = xsh_rec_list_get_data1(rec_list, i);
    float *errs = xsh_rec_list_get_errs1(rec_list, i);
    int *qual = xsh_rec_list_get_qual1(rec_list, i);
    int ilambda, islit;
    /* result REC Frame */
    double *res_lambda = NULL;
    float *res_flux = NULL, *res_errs = NULL;
    int *res_qual = NULL;
    float* pima=NULL;
    float* fima=NULL;
    float* rima=NULL;
#if debug_extraction
    cpl_image* prof_ima =cpl_image_new(nlambda,nslit,CPL_TYPE_FLOAT);
    cpl_image* flux_ima =cpl_image_new(nlambda,nslit,CPL_TYPE_FLOAT);
    cpl_image* res_ima =cpl_image_new(nlambda,nslit,CPL_TYPE_FLOAT);
    pima=cpl_image_get_data_float(prof_ima);
    fima=cpl_image_get_data_float(flux_ima);
    rima=cpl_image_get_data_float(res_ima);
#endif
    xsh_msg_dbg_medium(
        "order %d nslit %d (%f,%f) nlambda %d (%f %f)", order, nslit, slitmin, slitmax, nlambda, lambdamin, lambdamax);

    check( xsh_rec_list_set_data_size( result_list, i, order, nlambda, 1));

    check( res_lambda = xsh_rec_list_get_lambda( result_list, i));
    check( res_flux = xsh_rec_list_get_data1( result_list, i));
    check( res_errs = xsh_rec_list_get_errs1( result_list, i));
    check( res_qual = xsh_rec_list_get_qual1( result_list, i));
    /* loop over wavelength */


    for (ilambda = 0; ilambda < nlambda; ilambda++) {

      int ylow = 0, yup = 0;
      double y_frac_low, y_frac_up;
      double fluxval = 0.0;
      double errval = 0.0;
      int qualval = 0;
      int idx;
      /* Localisation method */
      if (extract_par->method == LOCALIZATION_METHOD) {

        xsh_extraction_get_slit_minmax(loc_list, lambda[ilambda], slit[0],
            slit_step, nslit, &ylow, &yup, &y_frac_up, &y_frac_low);

        xsh_msg_dbg_medium("ylow %d yup %d", ylow, yup);

        if (mask_hsize <= 0) {
          /* consider the fraction of pixels for ylow */
          idx = ylow * nlambda + ilambda;
          if (idx >= (nlambda * nslit)) {
            xsh_msg_dbg_high(
                "Index Out of Image: %d >= %d (ylow: %d, ilambda: %d)", idx, nlambda*nslit, ylow, ilambda);
            continue;
          }
          fluxval += y_frac_low * flux[idx];
          errval += y_frac_low * (errs[idx] * errs[idx]);
          qualval |= qual[idx];

          /* Integrate on complete pixels */
          if(i==0 && ilambda==0) {
             xsh_msg("Object extraction limits: [%d,%d] [bin]/[%g,%g] [arcsec]",
                     ylow+1,yup-1,slit_step*(ylow+1)+slit[0],slit_step*(yup-1)+slit[0]);
          }
          for (islit = ylow + 1; islit < yup; islit++) {
            idx = islit * nlambda + ilambda;
            fluxval += flux[idx];
            errval += errs[idx] * errs[idx];
            qualval |= qual[idx];
          }

          /* consider the fraction of pixels for yup */
          if (yup < nslit) {
            idx = yup * nlambda + ilambda;
            fluxval += y_frac_up * flux[idx];
            errval += y_frac_up * errs[idx] * errs[idx];
            qualval |= qual[idx];
          }

        } else {
          int slit_min = ylow + 1;
          int slit_max = yup;
          if(i==0 && ilambda==0) {
             xsh_msg("Object extraction limits: [%d,%d] [bin]/[%g,%g] [arcsec]",
                     ylow+1,yup,slit_step*(ylow+1)+slit[0],slit_step*(yup)+slit[0]);
          }
          xsh_extract_clean_slice(flux, errs, qual, lambda,
              ilambda, instrument, slit_min, slit_max, nlambda, nslit,
              mask_hsize, &fluxval, &errval, &qualval,pima,fima,rima);

        }

        /* Nod method */
      } else if (extract_par->method == NOD_METHOD) {

        /*
         xsh_msg_warning("slit_inf=%f slit_upp=%f slit0 %f",
         xsh_utils_scired_slit.c:slit_ext_min,slit_ext_max,slit[0]);
         */

        /* original definition of extraction range
         ylow = (slit_ext_min - slit[0]) / slit_step;
         yup = (slit_ext_max - slit[0]) / slit_step;
         */

        ylow = slit_ext_min;
        yup = slit_ext_max;
        //xsh_msg("mask_hsize=%d",mask_hsize);
        if (mask_hsize <= 0) {
          if(i == 0 && ilambda==0) {
             xsh_msg("Object extraction limits: [%d,%d] [bin]/[%g,%g] [arcsec]",
                     ylow,yup,slit_step*(ylow)+slit[0],slit_step*(yup)+slit[0]);
          }
          for (islit = ylow; islit <= yup; islit++) {
            idx = islit * nlambda + ilambda;
            fluxval += flux[idx];
            errval += errs[idx] * errs[idx];
            qualval |= qual[idx];
          }

        } else {

          int slit_min = ylow + 1;
          int slit_max = yup;
          if(i==0 && ilambda==0) {
             xsh_msg("Object extraction limits: [%d,%d] [bin]/[%g,%g] [arcsec]",
                     ylow+1,yup,slit_step*(ylow+1)+slit[0],slit_step*(yup)+slit[0]);
          }
          xsh_extract_clean_slice(flux, errs, qual, lambda,
              ilambda, instrument, slit_min, slit_max, nlambda, nslit,
              mask_hsize, &fluxval, &errval, &qualval,pima,fima,rima);
        }
      }
      res_lambda[ilambda] = lambda[ilambda];
      res_flux[ilambda] = fluxval;
      res_errs[ilambda] = sqrt(errval);
      res_qual[ilambda] = qualval;
    }
#if debug_extraction
    //xsh_msg("saving image profile");
    char prof_name[80];
    sprintf(prof_name,"prof_ord_%d.fits",i);
    cpl_image_save(prof_ima, prof_name, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
    sprintf(prof_name,"flux_ord_%d.fits",i);
    cpl_image_save(flux_ima, prof_name, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
    sprintf(prof_name,"res_ord_%d.fits",i);
    cpl_image_save(res_ima, prof_name, CPL_TYPE_DOUBLE, NULL, CPL_IO_DEFAULT);
    cpl_image_delete(prof_ima);
    cpl_image_delete(flux_ima);
    cpl_image_delete(res_ima);
#endif

  }

  check( cpl_propertylist_append ( result_list->header, rec_list->header));
  check( xsh_pfits_set_pcatg( result_list->header, tag));
  fname = xsh_stringcat_any(tag, ".fits", (void*)NULL);
  tag_drl = xsh_stringcat_any(tag, "_DRL", (void*)NULL);
  fname_drl = xsh_stringcat_any(tag_drl, ".fits", (void*)NULL);

  check( *res_frame_ext= xsh_rec_list_save2( result_list,fname, tag));
  check( res_frame = xsh_rec_list_save( result_list, fname_drl, tag_drl,0));

  xsh_msg_dbg_medium("%s created", fname);

  cleanup: xsh_rec_list_free(&result_list);
  xsh_rec_list_free(&rec_list);
  xsh_localization_free(&loc_list);

  XSH_FREE( tag_drl);
  XSH_FREE( fname_drl);
  XSH_FREE( fname);

  return res_frame;
}





/** 
 @brief simple 1D extraction of point source like object

 @param[in] rec_frame Rectified frame (from xsh_rectify)
 @param[in] loc_frame Localization table
 @param[in] instrument Instrument pointer
 @param[in] extract_par Parameters for object extraction
 @param[out] res_frame_ext extracted frame in ESO format
 @param[in] rec_prefix recipe prefix (for PRO.CATG definition)
 @return a rectified frame
 */
cpl_frame*
xsh_extract(cpl_frame * rec_frame, cpl_frame* loc_frame,
    xsh_instrument* instrument, xsh_extract_param* extract_par,
    cpl_frame** res_frame_ext, const char* rec_prefix) {
  cpl_frame *res_frame = NULL;
  char tag[256];

  sprintf(tag, "%s_%s", rec_prefix,
  XSH_GET_TAG_FROM_ARM( XSH_ORDER1D, instrument));

  check( res_frame = xsh_extract_with_tag( rec_frame, loc_frame, instrument,
          extract_par, tag,res_frame_ext ) );
  xsh_add_temporary_file(cpl_frame_get_filename(res_frame));
  cleanup: return res_frame;
}

/** 
 @brief simple 1D extraction of point source like object

 @param[in] rec_frame Rectified frame (from xsh_rectify)
 @param[in] loc_frame Localization table
 @param[in] instrument Instrument pointer
 @param[in] extract_par Parameters for object extraction
 @param[in] ipol_bp_par Parameters for bad pixel interpolation during extraction
 @param[out] res_frame_ext extracted frame in ESO format
 @param[in] rec_prefix recipe prefix (for PRO.CATG definition)
 @return a rectified frame
 */
cpl_frame*
xsh_extract_clean(cpl_frame * rec_frame, cpl_frame* loc_frame,
    xsh_instrument* instrument, xsh_extract_param* extract_par,
    xsh_interpolate_bp_param * ipol_bp_par, cpl_frame** res_frame_ext,
    const char* rec_prefix) {
  cpl_frame *res_frame = NULL;
  char tag[256];

  sprintf(tag, "%s_%s", rec_prefix,
  XSH_GET_TAG_FROM_ARM( XSH_ORDER1D, instrument));

  check(
      res_frame=xsh_extract_clean_with_tag( rec_frame, loc_frame, instrument, extract_par, ipol_bp_par, tag,res_frame_ext ));
  
  /*
  check( res_frame = xsh_extract_with_tag( rec_frame, loc_frame, instrument,
          extract_par, tag,res_frame_ext ) );

  */
  xsh_add_temporary_file(cpl_frame_get_filename(res_frame));
  cleanup: return res_frame;
}

/** 
 @brief Loop on IFU Frames to extract simple 1D (1 per slitlet)

 @param[in] rec_frameset Rectified frameset (from xsh_rectify_ifu)
 @param[in] loc_frameset Localization table frameset (xsh_localize_ifu)
 @param[in] instrument Instrument pointer
 @param[in] extract_par Parameters for object extraction
 @param[in] rec_prefix recipe prefix (to define PRO.CATG value)
 @return a rectified frame
 */
cpl_frameset * xsh_extract_ifu(cpl_frameset * rec_frameset,
    cpl_frameset * loc_frameset, xsh_instrument* instrument,
    xsh_extract_param * extract_par, const char* rec_prefix) {
  cpl_frameset * result_set = NULL;
  int slitlet;
  int i;

  xsh_msg( " Entering xsh_extract_ifu");
  XSH_ASSURE_NOT_NULL( rec_frameset);
  //XSH_ASSURE_NOT_NULL( loc_frameset ) ;
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( extract_par);

  check( result_set = cpl_frameset_new());

  /* Loop over the 3 IFU slitlets */
  for (i = 0, slitlet = LOWER_IFU_SLITLET; slitlet <= UPPER_IFU_SLITLET;
      slitlet++, i++) {
    cpl_frame * loc_frame = NULL;
    cpl_frame * rec_frame = NULL;
    cpl_frame * ext_frame = NULL;
    cpl_frame * ext_frame_eso = NULL;
    char tag[256];
    const char * tag_suf = NULL;

    switch (slitlet) {
    case LOWER_IFU_SLITLET:
      tag_suf = XSH_GET_TAG_FROM_ARM( XSH_ORDER1D_DOWN_IFU,
          instrument);
      break;
    case CENTER_IFU_SLITLET:
      tag_suf = XSH_GET_TAG_FROM_ARM( XSH_ORDER1D_CEN_IFU,
          instrument);
      break;
    case UPPER_IFU_SLITLET:
      tag_suf = XSH_GET_TAG_FROM_ARM( XSH_ORDER1D_UP_IFU,
          instrument);
      break;
    }
    sprintf(tag, "%s_%s", rec_prefix, tag_suf);
    xsh_msg( "  Slitlet %s", SlitletName[slitlet]);
    check( rec_frame = cpl_frameset_get_frame( rec_frameset, i ));
    xsh_msg( " REC Frame '%s' Got", cpl_frame_get_filename( rec_frame ));

    if (loc_frameset != NULL) {
      check( loc_frame = cpl_frameset_get_frame( loc_frameset, i ));
      xsh_msg( " LOC Frame '%s' Got", cpl_frame_get_filename( loc_frame ));
    } else {
      xsh_msg_dbg_medium( "No localization table");
    }

    /* Now extract this slitlet */xsh_msg( "Calling xsh_extract");
    check(
        ext_frame = xsh_extract_with_tag( rec_frame, loc_frame, instrument, extract_par, tag,&ext_frame_eso));
    xsh_msg( "Extracted frame '%s'", cpl_frame_get_filename( ext_frame ));
    check( cpl_frameset_insert( result_set, ext_frame_eso ));
    check( cpl_frameset_insert( result_set, ext_frame ));
  }

  cleanup:
  xsh_msg( "Exit xsh_extract_ifu");
  return result_set;
}

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

/**@}*/
