/*                                                                            *
 *   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: 2012-12-18 14:15:44 $
 * $Revision: 1.17 $
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_compute_slice_dist  Compute Slice Offsets (xsh_geom_ifu)
 * @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_spectrum.h>
#include <xsh_data_slice_offset.h>
#include <xsh_data_rec.h>
#include <xsh_ifu_defs.h>

#include <cpl.h>

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

/*-----------------------------------------------------------------------------
  Implementation
  -----------------------------------------------------------------------------*/

cpl_frame* xsh_compute_slice_dist( cpl_frameset *loc_frame_set,
  cpl_frame *order_tab_frame, cpl_frame *slitmap_frame, 
  cpl_frameset *rec_frameset,
  double slicedist_lambda,
  xsh_instrument *instrument)
{
  cpl_frame * result = NULL ;
  cpl_frame *loc_frame = NULL;
  cpl_frame *rec_frame = NULL;
  xsh_slice_offset *slice = NULL ;
  xsh_localization *loc_down = NULL, *loc_cen = NULL, *loc_up = NULL;
  char fname[256];
  double slit_down, slit_cen, slit_up;
  double sdown, sldown, slup, sup;
  double dist_up, dist_down;
  double lambda;
  //xsh_order_list *orderlist = NULL ;
  xsh_rec_list *reclist = NULL;

  XSH_ASSURE_NOT_NULL( loc_frame_set);
  XSH_ASSURE_NOT_NULL( rec_frameset);
  XSH_ASSURE_NOT_NULL( instrument);

  xsh_msg( "Get Localization table for slitlet %s",
	   SlitletName[LOWER_IFU_SLITLET] ) ;
  check( loc_frame = cpl_frameset_get_frame( loc_frame_set, 0));
  xsh_msg( "     '%s'", cpl_frame_get_filename( loc_frame ) ) ;
  check( loc_down = xsh_localization_load( loc_frame));

  xsh_msg( "Get Localization table for slitlet %s",
	   SlitletName[CENTER_IFU_SLITLET] ) ;
  check( loc_frame = cpl_frameset_get_frame( loc_frame_set, 1));
  xsh_msg( "     '%s'", cpl_frame_get_filename( loc_frame ) ) ;
  check( loc_cen = xsh_localization_load( loc_frame));

  xsh_msg( "Get Localization table for slitlet %s",
	   SlitletName[UPPER_IFU_SLITLET] ) ;
  check( loc_frame = cpl_frameset_get_frame( loc_frame_set, 2));
  xsh_msg( "     '%s'", cpl_frame_get_filename( loc_frame ));
  check( loc_up = xsh_localization_load( loc_frame));

  check( rec_frame = cpl_frameset_get_frame( rec_frameset, 0));
  check( reclist = xsh_rec_list_load( rec_frame, instrument));
 
  check( slice = xsh_slice_offset_create());

  if ( slicedist_lambda < 0){
    check( lambda = xsh_rec_list_get_lambda_max( reclist));
  }
  else{
    lambda = slicedist_lambda;
  }
  xsh_msg(" Evaluate at lambda %f", lambda);
  check( slit_down = cpl_polynomial_eval_1d(loc_down->cenpoly, 
    lambda, NULL));
  check( slit_cen = cpl_polynomial_eval_1d(loc_cen->cenpoly, 
    lambda, NULL));
  check( slit_up = cpl_polynomial_eval_1d(loc_up->cenpoly, 
    lambda, NULL));

  xsh_msg("SLIT center from localization [%f,%f,%f]", slit_down, slit_cen,
    slit_up);

  check( xsh_get_slit_edges(
    slitmap_frame, &sdown, &sldown, &slup, &sup, instrument));

  xsh_msg("EDGES form order tab [%f,[%f %f],%f]",sdown, sldown,
    slup, sup);

  dist_up = 2*slup-slit_cen-slit_up;
  dist_down = 2*sldown-slit_cen-slit_down;
  xsh_msg("DISTANCES edgU-->Up = %f edgU-->cen %f ==> %f", slup-slit_up,
    slup-slit_cen, dist_up);
  xsh_msg("DISTANCES edgD-->Down = %f edgD-->cen %f ==> %f", sldown-slit_down,
    sldown-slit_cen, dist_down);

  slice->cen_up = dist_up;
  slice->cen_down = dist_down;

  sprintf( fname, "SLICE_OFFSET_%s.fits", 
    xsh_instrument_arm_tostring( instrument));
  check( result = xsh_slice_offset_save( slice, fname, instrument));

  cleanup:
    xsh_localization_free( &loc_down);
    xsh_localization_free( &loc_cen);
    xsh_localization_free( &loc_up);
    xsh_slice_offset_free( &slice);
    xsh_rec_list_free( &reclist);

    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      xsh_free_frame( &result);
    }
    return result;
}
/*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
   @brief Compute the shift in slit between reference wavelength and others
   @param[in] lambda_ref Reference wavelength
   @param[in] objpos_frame table containing for Wavelength the position of 
              object center on the slit
   @param[in] shiftifu_frame table containing a preceding shift ifu tab
              This table will be sum to the new
   @param[in] resname Name of result table
   @return frame table containing for wavelength ths offset with the reference
           wavelength in slit.
*/

cpl_frame* xsh_compute_shift_ifu_slitlet( double lambda_ref, cpl_frame *objpos_frame,
  cpl_frame *shiftifu_frame, double lambdaref_hsize, const char* resname)
{
  cpl_frame *result = NULL;
  const char *objpos_name = NULL;
  cpl_table *objpos_tab = NULL;
  double *wave_pos_data = NULL;
  double *slit_pos_data = NULL;
  int pos_size;
  double slitref=0;
  cpl_table *res_tab = NULL;
  int i;
  char tablename[256];
  cpl_propertylist *header = NULL;

  const char *shiftifu_name = NULL;
  cpl_table *shiftifu_tab = NULL;
  //double *wave_shiftifu_data = NULL;
  double *slit_shiftifu_data = NULL;
  int shiftifu_size;
  cpl_vector *median_slitref_vect = NULL;
  double lambda=0;
  int istart, iend;
  int median_slitref_size = 0;
  
  XSH_ASSURE_NOT_NULL( objpos_frame);
  XSH_ASSURE_NOT_ILLEGAL( lambdaref_hsize >= 0);
  
  /* Load objpos table */
  check( objpos_name = cpl_frame_get_filename( objpos_frame));
  XSH_TABLE_LOAD( objpos_tab, objpos_name);
  /* Search slit position of reference wavelength */
  check( wave_pos_data = cpl_table_get_data_double( objpos_tab, 
    XSH_OBJPOS_COLNAME_WAVELENGTH));
  check( slit_pos_data = cpl_table_get_data_double( objpos_tab, 
    XSH_OBJPOS_COLNAME_SLIT));

  pos_size = cpl_table_get_nrow( objpos_tab);
  
  istart=0;
  
  while( lambda <= (lambda_ref-lambdaref_hsize) && istart< pos_size){
    lambda = wave_pos_data[istart];
    istart++;
  }
  iend = istart;
  
  while( lambda <= (lambda_ref+lambdaref_hsize) && iend< pos_size){
    lambda = wave_pos_data[iend];
    iend++;
  }
  median_slitref_size = iend-istart+1;

  median_slitref_vect = cpl_vector_new( median_slitref_size);
  
  for(i=istart; i<=iend; i++){
    double resslit = slit_pos_data[i];

    cpl_vector_set( median_slitref_vect, i-istart, resslit);
  } 
  
  
    
  slitref = xsh_data_interpolate( lambda_ref, pos_size, 
    wave_pos_data, slit_pos_data);
  
  xsh_msg_dbg_low("Old reference wavelength %f slit %f", 
    lambda_ref, slitref);
    
  slitref = cpl_vector_get_median( median_slitref_vect);

  xsh_msg_dbg_low("New reference wavelength %f slit %f", 
    lambda_ref, slitref);
    
  check( res_tab = cpl_table_new( pos_size));
  XSH_TABLE_NEW_COL( res_tab, XSH_SHIFTIFU_COLNAME_WAVELENGTH,
    XSH_SHIFTIFU_UNIT_WAVELENGTH, CPL_TYPE_DOUBLE);
  XSH_TABLE_NEW_COL( res_tab, XSH_SHIFTIFU_COLNAME_SHIFTSLIT,
    XSH_SHIFTIFU_UNIT_SHIFTSLIT, CPL_TYPE_DOUBLE);

  /* case of summing old result */
  if ( shiftifu_frame != NULL){
    check( shiftifu_name = cpl_frame_get_filename( shiftifu_frame));
    XSH_TABLE_LOAD( shiftifu_tab, shiftifu_name);
    /*
    check( wave_shiftifu_data = cpl_table_get_data_double( shiftifu_tab,
      XSH_SHIFTIFU_COLNAME_WAVELENGTH));
    */
    check( slit_shiftifu_data = cpl_table_get_data_double( shiftifu_tab,
      XSH_SHIFTIFU_COLNAME_SHIFTSLIT)); 
    shiftifu_size = cpl_table_get_nrow( shiftifu_tab);
    XSH_ASSURE_NOT_ILLEGAL( pos_size == shiftifu_size);
    for( i=0; i< pos_size; i++){
      double reswave, resslit;
            
      reswave = wave_pos_data[i];
      resslit = slit_pos_data[i]-slitref+slit_shiftifu_data[i];

      check( cpl_table_set_double( res_tab, XSH_SHIFTIFU_COLNAME_WAVELENGTH,
        i, reswave));
      check( cpl_table_set_double( res_tab, XSH_SHIFTIFU_COLNAME_SHIFTSLIT,
        i, resslit));
    }
  }
  else{
    for( i=0; i< pos_size; i++){
      double reswave, resslit;

      reswave = wave_pos_data[i];
      resslit = slit_pos_data[i]-slitref;
      check( cpl_table_set_double( res_tab, XSH_SHIFTIFU_COLNAME_WAVELENGTH,
        i, reswave));
      check( cpl_table_set_double( res_tab, XSH_SHIFTIFU_COLNAME_SHIFTSLIT,
        i, resslit));
    }
  }
  /* save result */
  sprintf( tablename, resname);
  header = cpl_propertylist_new();
  /* write reference in header */
  check( xsh_pfits_set_shiftifu_lambdaref( header, lambda_ref));
  check( xsh_pfits_set_shiftifu_slitref( header, slitref));


  check( cpl_table_save( res_tab, header, NULL, tablename, CPL_IO_DEFAULT));

  /* Create the frame */
  check(result=xsh_frame_product( tablename,
                                  "OFFSET_TAB",
                                   CPL_FRAME_TYPE_TABLE,
                                   CPL_FRAME_GROUP_PRODUCT,
                                   CPL_FRAME_LEVEL_TEMPORARY));
  check (xsh_add_temporary_file( tablename));

  cleanup:
    xsh_free_propertylist( &header);
    XSH_TABLE_FREE( shiftifu_tab);
    XSH_TABLE_FREE( res_tab);
    XSH_TABLE_FREE( objpos_tab);
    xsh_free_vector( &median_slitref_vect);
    return result;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
   @brief 
     Compute the shift in slit between reference wavelength and others for
     all the slitlets
   @param[in] lambda_ref 
     Reference wavelength
   @param[in] objpos_frameset
     Tables containing for wavelength the position of object center on the slit
   @param[in] shiftifu_frame 
     Tables containing a preceding shift ifu tab. This table will be sum to 
     the new
   @param[in  instrument
     Instrument structure
   @param[in] prefix
     Name of prefix 
   @return Table frameset containing for wavelength the offset with the reference
           wavelength in slit.
*/
cpl_frameset* xsh_compute_shift_ifu( double lambda_ref, double lambdaref_hsize, 
  cpl_frameset *objpos_frameset,
  cpl_frameset *shiftifu_frameset, xsh_instrument* instrument, const char* prefix)
{
  int i, slitlet;
  cpl_frameset *result_frameset = NULL;
  char fname[256];

  XSH_ASSURE_NOT_NULL( objpos_frameset);
  XSH_ASSURE_NOT_NULL( instrument);

  check( result_frameset = cpl_frameset_new());

  for( i = 0, slitlet = LOWER_IFU_SLITLET ; i < 3 ; i++, slitlet++ ) {
    cpl_frame *comp_shift_frame = NULL;
    cpl_frame *objpos_frame = NULL;
    cpl_frame *shift_frame = NULL;
    char tag[256];

    sprintf( fname ,"%s_SHIFTIFU_%s_%s.fits", prefix, SlitletName[slitlet],
      xsh_instrument_arm_tostring( instrument));

    xsh_msg( "Compute IFU shift for slitlet %s, frame '%s'", 
      SlitletName[slitlet], fname);

    check( objpos_frame = cpl_frameset_get_frame( objpos_frameset, i));

    if ( shiftifu_frameset != NULL){
      check( shift_frame = cpl_frameset_get_frame( shiftifu_frameset, i));
    }
    check( comp_shift_frame = xsh_compute_shift_ifu_slitlet( lambda_ref, 
      objpos_frame, shift_frame, lambdaref_hsize, fname));
    
    sprintf( tag, "OFFSET_TAB_%s_IFU_%s", SlitletName[slitlet],
      xsh_instrument_arm_tostring( instrument));
    check( cpl_frame_set_tag( comp_shift_frame, tag));
    check( cpl_frameset_insert( result_frameset, comp_shift_frame));
  }

 cleanup:
  if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
    xsh_free_frameset( &result_frameset);
  }
  return result_frameset;
}
/**@}*/
