/*                                                                           *
 *   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-10-02 11:35:51 $
 * $Revision: 1.30 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_flexcomp Update wave solution from computed shift between CAL 
 *   and ATT
 * @ingroup drl_functions
 */
/*---------------------------------------------------------------------------*/
/**@{*/

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

#include <xsh_drl.h>
#include <xsh_pfits.h>
#include <xsh_data_resid_tab.h>
#include <xsh_data_instrument.h>
#include <xsh_data_arclist.h>
#include <xsh_data_spectralformat.h>
#include <xsh_data_order.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_the_map.h>
#include <xsh_model_io.h>
#include <xsh_model_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <cpl.h>

/*----------------------------------------------------------------------------
                            Typedefs
  ---------------------------------------------------------------------------*/

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


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


/*---------------------------------------------------------------------------*/
/**
  @brief 
    This function applies the computed shift betwwen AFC CAL and AFC ATT 
    frame.

  @param[in] afc_frame flexure compensation frame
  @param[in] wave_tab_frame wave tab frame
  @param[in] model_config_frame model cfg frame
  @param[in] order_tab_frame order tab frame
  @param[in] attresidtab_frame residual tables for attached AFC frame
  @param[in] afc_xmin AFC xmin
  @param[in] afc_ymin AFC ymin
  @param[in] instr Instrument containing the arm , mode and lamp in use
  @param[out] afc_order_tab_frame order table AFC corrected
  @param[out] afc_model_config_frame model cfg frame AFC corrected
*/
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_flexcor( cpl_frame* afc_frame,
                        cpl_frame *wave_tab_frame, 
                        cpl_frame *model_config_frame,
                        cpl_frame *order_tab_frame, 
                        cpl_frame *attresidtab_frame, 
                        int afc_xmin, int afc_ymin, 
                        xsh_instrument *instr, 
                        cpl_frame **afc_order_tab_frame, 
                        cpl_frame **afc_model_config_frame)
{
  char result_name[256];
  cpl_frame *result = NULL;
  cpl_frame *afc_order_tab_result = NULL;
  cpl_table *trace = NULL;
  float xshift = 0.0, yshift = 0.0; 
  float T_effect_x = 0.0, T_effect_y = 0.0; 
  xsh_resid_tab *attresid_tab = NULL;
  int residtab_size, iline;
  const char *tag = NULL;
  xsh_wavesol *wavesol = NULL;
  xsh_order_list *order_tab = NULL;
  double lambda, order, slit=0.0;
  double att_x, att_y;
  double cal_x, cal_y;
  double cal_no_T_corr_x, cal_no_T_corr_y;
  XSH_INSTRCONFIG *config = NULL;
  XSH_ARM arm = XSH_ARM_UNDEFINED;
  xsh_xs_3 config_model;
  xsh_xs_3 cfg_mod_no_T_corr;
  //  xsh_xs_3* p_xs_3;
  cpl_table *model_tab = NULL;
  cpl_propertylist *model_header = NULL;
  cpl_propertylist *afc_header = NULL;
  cpl_vector *shiftx_vect = NULL;
  cpl_vector *shifty_vect = NULL;
  cpl_vector *T_effect_x_vect = NULL;
  cpl_vector *T_effect_y_vect = NULL;
  const char* afc_name=NULL;
  int found_temp=true;
  cpl_frame* MODEL_CONF_OPT_frame=NULL;
  cpl_propertylist* qc_head2copy=NULL;
  //xsh_pre * pre_afc = NULL ;
  //xsh_msg("p1=%p p2=%p",afc_order_tab_frame,*afc_model_config_frame);
  /* check input parameters */
  XSH_ASSURE_NOT_NULL( attresidtab_frame);
  XSH_ASSURE_NOT_NULL( order_tab_frame);
  XSH_ASSURE_NOT_NULL( afc_order_tab_frame);
  //XSH_ASSURE_NOT_NULL( afc_model_config_frame);

  /* First compute the shift */
  check( attresid_tab = xsh_resid_tab_load( attresidtab_frame));
  check( order_tab = xsh_order_list_load( order_tab_frame, instr));
  check( residtab_size = xsh_resid_tab_get_size( attresid_tab));

  XSH_REGDEBUG( "RESID_TAB SIZE %d", residtab_size);
  check(afc_name=cpl_frame_get_filename(afc_frame));
  check(afc_header=cpl_propertylist_load(afc_name,0));

  if ( wave_tab_frame != NULL){
    check( wavesol = xsh_wavesol_load( wave_tab_frame, instr));
  }
  else{
    XSH_ASSURE_NOT_NULL( model_config_frame);
    check( xsh_model_config_load_best( model_config_frame, &cfg_mod_no_T_corr));
    check(xsh_model_temperature_update_frame(&model_config_frame,afc_frame,
                                             instr,&found_temp));
    check( xsh_model_config_load_best( model_config_frame, &config_model));
  }

  check( shiftx_vect = cpl_vector_new( residtab_size));
  check( shifty_vect = cpl_vector_new( residtab_size));
  check( T_effect_x_vect = cpl_vector_new( residtab_size));
  check( T_effect_y_vect = cpl_vector_new( residtab_size));

  check(arm=xsh_instrument_get_arm(instr));

  for( iline=0; iline < residtab_size; iline++){

    lambda = attresid_tab->lambda[iline];
    order =  attresid_tab->order[iline];
    att_x = attresid_tab->xgauss[iline];
    att_y = attresid_tab->ygauss[iline];

    if ( wavesol != NULL){
      check( cal_x = xsh_wavesol_eval_polx( wavesol, lambda, order, slit)); 
      check( cal_y = xsh_wavesol_eval_poly( wavesol, lambda, order, slit));
    }
    else{
      check( xsh_model_get_xy( &config_model, instr, lambda, order, slit,
			       &cal_x, &cal_y));
      check( xsh_model_get_xy( &cfg_mod_no_T_corr, instr, lambda, order, slit,
			       &cal_no_T_corr_x, &cal_no_T_corr_y));
      /*thpre_x/y are only necessary for the pre-annealing residuals check
	in xsh_model_pipe_anneal*/
      attresid_tab->thpre_x[iline]=cal_x;
      attresid_tab->thpre_y[iline]=cal_y;

      attresid_tab->slit_index[iline]=4;

      T_effect_x=cal_x-cal_no_T_corr_x;
      T_effect_y=cal_y-cal_no_T_corr_y;
      xsh_msg(" T_effect_x %f T_effect_y %f",  T_effect_x,T_effect_y);
      check( cpl_vector_set( T_effect_x_vect, iline, T_effect_x));
      check( cpl_vector_set( T_effect_y_vect, iline, T_effect_y));
    }

    cal_x = cal_x-afc_xmin+1.0;
    cal_y =cal_y-afc_ymin+1.0;

    xshift = att_x-cal_x;
    yshift = att_y-cal_y;
    xsh_msg("lambda %f order %f slit %f calx %f caly %f shiftx %f shifty %f TeffX %f TeffY %f",
            lambda,order,slit,cal_x, cal_y,xshift,yshift, T_effect_x,T_effect_y);

    check( cpl_vector_set( shiftx_vect, iline, xshift));
    check( cpl_vector_set( shifty_vect, iline, yshift));
  }

  check( xshift = cpl_vector_get_median( shiftx_vect));
  check( yshift = cpl_vector_get_median( shifty_vect));
  check( T_effect_x = cpl_vector_get_median( T_effect_x_vect));
  check( T_effect_y = cpl_vector_get_median( T_effect_y_vect));

  xsh_msg("Measured shift IN X %f Y %f", xshift, yshift);
  if ( wavesol == NULL){
    xsh_msg("Temperature effect at (mean) reference line in X %f Y %f",
	    T_effect_x,T_effect_y);
  }


  if ( wavesol != NULL){
    cpl_table* tab=NULL;
    cpl_propertylist* plist=NULL;
    check( xsh_wavesol_apply_shift( wavesol, xshift, yshift));
    check( trace = xsh_wavesol_trace( wavesol, &lambda, &order, &slit, 1));
    tag = XSH_GET_TAG_FROM_ARM( XSH_WAVE_TAB_AFC, instr);
    sprintf( result_name, "%s.fits", tag);
    check( result = xsh_wavesol_save( wavesol, trace, result_name, tag));
    tab=cpl_table_load(result_name,1,0);
    plist=cpl_propertylist_load(result_name,0);
    cpl_propertylist_append_double(plist,XSH_QC_AFC_XSHIFT,xshift);
    cpl_propertylist_append_double(plist,XSH_QC_AFC_YSHIFT,yshift);
    cpl_propertylist_set_comment(plist,XSH_QC_AFC_XSHIFT,XSH_QC_AFC_XSHIFT_C);
    cpl_propertylist_set_comment(plist,XSH_QC_AFC_YSHIFT,XSH_QC_AFC_YSHIFT_C);
    check( cpl_table_save(tab,plist, NULL, result_name,CPL_IO_DEFAULT));
    xsh_free_table(&tab);
    xsh_free_propertylist(&plist);
  }
  else{
    if (arm!=XSH_ARM_NIR) {
      xsh_model_offset( xshift, yshift, &config_model);
      /*New cfg is now appropriate to the prism temperature(s) in the AFC
	data, when it is written to the table here it will have that new
	temperature*/
    }
    else {
      /*For NIR arm there are many more lines, so instead of just computing
	a shift we can anneal a bunch of parameters.*/
 
      check(MODEL_CONF_OPT_frame=xsh_model_pipe_anneal( model_config_frame,
							attresidtab_frame,
							2000,
							1.0,
							9,
							5));

      /* The result of pipe_anneal is a frame, so we need to extract the
	 config_model data structure to be consistent with what was done
	 for the other arms */
      check( xsh_model_config_load_best(MODEL_CONF_OPT_frame, &config_model));

    }

    xsh_msg("file name=%s",cpl_frame_get_filename(model_config_frame));
    qc_head2copy=cpl_propertylist_load_regexp(cpl_frame_get_filename(model_config_frame),0,"^ESO QC MODEL",0);
    
    check( model_tab = xsh_model_io_output_cfg( &config_model));
    tag = XSH_GET_TAG_FROM_ARM( XSH_MOD_CFG_OPT_AFC, instr);
    sprintf( result_name, "%s.fits", tag);
    check(  model_header = cpl_propertylist_new());
    check( xsh_pfits_set_pcatg( model_header, tag));
    cpl_propertylist_append_double(model_header,XSH_QC_AFC_XSHIFT,xshift);
    cpl_propertylist_append_double(model_header,XSH_QC_AFC_YSHIFT,yshift);
    cpl_propertylist_set_comment(model_header,XSH_QC_AFC_XSHIFT,XSH_QC_AFC_XSHIFT_C);
    cpl_propertylist_set_comment(model_header,XSH_QC_AFC_YSHIFT,XSH_QC_AFC_YSHIFT_C);
    cpl_propertylist_append(model_header,qc_head2copy);
    xsh_free_propertylist(&qc_head2copy);
    check( cpl_table_save( model_tab, model_header, NULL, result_name,
      CPL_IO_DEFAULT));
    check( *afc_model_config_frame = cpl_frame_new());
    check( cpl_frame_set_filename( *afc_model_config_frame, result_name));
    check( cpl_frame_set_tag( *afc_model_config_frame, tag));
  }

  check (xsh_order_list_apply_shift( order_tab, xshift, yshift));
  tag = XSH_GET_TAG_FROM_MODE( XSH_ORDER_TAB_AFC, instr);
  sprintf( result_name, "%s.fits", tag);

  check( config = xsh_instrument_get_config( instr));
  check( afc_order_tab_result = xsh_order_list_save( order_tab, instr,
    result_name, tag, config->ny));
  
  *afc_order_tab_frame = afc_order_tab_result;

  cleanup:
    if( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_free_frame( &result);
      xsh_free_frame( &afc_order_tab_result);
      
    }
    xsh_resid_tab_free( &attresid_tab);
    xsh_order_list_free( &order_tab);
    xsh_free_vector(&T_effect_x_vect);
    xsh_free_vector(&T_effect_y_vect);
    xsh_free_vector( &shiftx_vect);
    xsh_free_vector( &shifty_vect);
    xsh_free_table( &trace);
    xsh_free_table( &model_tab);
    xsh_free_propertylist( &model_header);
    xsh_free_propertylist( &afc_header);
    if (wavesol==NULL) {
      if (arm==XSH_ARM_NIR) {
        xsh_free_frame(&MODEL_CONF_OPT_frame);
      }
    }
    xsh_wavesol_free( &wavesol);
  //xsh_pre_free( &pre_afc);
    return result;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief 
    Create a The tab for AFC

  @param[in] wave_tab_frame wavelength table
  @param[in] model_config_frame model cfg frame
  @param[in] order order at which computations are done 
  @param[in] spectralformat_frame spectral format frame
  @param[in] arclines_frame arc lines frame
  @param[in] xmin x min
  @param[in] ymin y min
  @param[in] instr 
    Instrument containing the arm , mode and lamp in use

*/
/*---------------------------------------------------------------------------*/
cpl_frame* 
xsh_afcthetab_create( cpl_frame *wave_tab_frame, 
                      cpl_frame *model_config_frame,  
                      int order,
                      cpl_frame *spectralformat_frame, 
                      cpl_frame *arclines_frame,
                      int xmin, 
                      int ymin,
                      xsh_instrument *instr,
                      const int clean_tmp)
{

  cpl_frame *result = NULL;
  xsh_arclist *arclist = NULL;
  xsh_wavesol *wavesol = NULL;
  float lambda, slit_position;
  double x,y;
  int slit_index;
  int size, iline, iorder, ithe=0;
  xsh_the_map* themap = NULL;
  int the_size=0;
  xsh_xs_3 config_model;
  cpl_vector* orders = NULL;
  int orders_size;
  xsh_spectralformat_list *spectrallist = NULL;
  char thename[256];

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( arclines_frame);
  XSH_ASSURE_NOT_NULL( instr);

  check( arclist = xsh_arclist_load( arclines_frame));

  check( size = xsh_arclist_get_size( arclist));

  xsh_msg("size %d", size);

  if ( spectralformat_frame != NULL){
   check( spectrallist = xsh_spectralformat_list_load(
     spectralformat_frame, instr));

    for( iline=0; iline < size; iline++){
      check( lambda = xsh_arclist_get_wavelength(arclist, iline));
      check( orders = xsh_spectralformat_list_get_orders(spectrallist,
        lambda));
      if (orders != NULL){
        check( orders_size = cpl_vector_get_size( orders));
        the_size += orders_size;
      }
      xsh_free_vector( &orders);
    }
  }
  else{
    the_size = 1;
  }


  xsh_msg("THE SIZE %d", the_size);
  check( themap = xsh_the_map_create( the_size));

  if (wave_tab_frame != NULL){
    check( wavesol = xsh_wavesol_load( wave_tab_frame, instr));
  }
  else{
    XSH_ASSURE_NOT_NULL( model_config_frame);
    check( xsh_model_config_load_best( model_config_frame,
      &config_model));
  }

  for( iline=0; iline < size; iline++){
    check( lambda = xsh_arclist_get_wavelength(arclist, iline));
    slit_index = 4;
    slit_position = 0;
    if ( spectrallist != NULL){
      check( orders = xsh_spectralformat_list_get_orders( spectrallist,
        lambda));
    }
    else{
      orders = cpl_vector_new(1);
      cpl_vector_set( orders, 0, order);
    }
    if (orders != NULL){
      check( orders_size = cpl_vector_get_size( orders));
      
      for( iorder=0; iorder < orders_size; iorder++){
        int cur_order;

        check( cur_order = cpl_vector_get( orders, iorder));

        if (wavesol != 	NULL){
          check( x = xsh_wavesol_eval_polx( wavesol, lambda, cur_order, 
            slit_position));
          check( y = xsh_wavesol_eval_poly( wavesol, lambda, cur_order, 
            slit_position));
        }
        else{
          check( xsh_model_get_xy( &config_model, instr, lambda, cur_order,
            slit_position, &x, &y));
        }
        x = x-xmin+1.0;
        y = y-ymin+1.0;
        XSH_REGDEBUG("lambda %f order %d %f %f",lambda, cur_order, x, y);
        check( xsh_the_map_set_arcline( themap, ithe, lambda, cur_order, 
          slit_index, slit_position, x, y));
        ithe++;
      }
    }
    xsh_free_vector( &orders);
  }
  sprintf( thename, "AFC_THEOTAB_%s.fits", 
    xsh_instrument_arm_tostring( instr));
  check( result = xsh_the_map_save( themap, thename));
  if(clean_tmp) {
     xsh_add_temporary_file(thename);
  }

  cleanup:
    if( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_free_vector( &orders);
      xsh_free_frame( &result);
    }
    xsh_spectralformat_list_free( &spectrallist);
    xsh_arclist_free( &arclist);
    xsh_wavesol_free( &wavesol);
    xsh_the_map_free( &themap);

    return result;
}
/*---------------------------------------------------------------------------*/
/**@}*/
