/*                                                                           *
 *   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: 2011-01-17 08:02:36 $
 * $Revision: 1.16 $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_spectralformat Spectral wavelength limit by orders
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


/*-----------------------------------------------------------------------------
  Includes
  ----------------------------------------------------------------------------*/
#include <math.h>
#include <xsh_data_spectralformat.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <cpl.h>
#include <xsh_utils_table.h>
#include <xsh_drl.h>

/*----------------------------------------------------------------------------
  Function implementation
  ----------------------------------------------------------------------------*/

/*****************************************************************************/
/** 
  @brief
    Dump main info about an order table (for each order of the list)
 @param[in] list 
   Pointer to table list
 @param[in] fname 
   File name
*/
/*****************************************************************************/
void xsh_spectralformat_list_dump( xsh_spectralformat_list* list,
				   const char * fname )
{
  int i ;
  FILE * fout ;

  if ( fname == NULL ) fout = stdout ;
  else fout = fopen( fname, "w" ) ;

  for( i = 0 ; i<list->size ; i++ ) {
    fprintf( fout, "Order: %d, Lambda Min: %f,Lambda Max: %f\n",
	     list->list[i].absorder, list->list[i].lambda_min,
	     list->list[i].lambda_max ) ;
  }
  if ( fname != NULL ) fclose( fout ) ;
}
/*****************************************************************************/


/*****************************************************************************/
/**
  @brief
    Create an empty spectralformat list
  @param[in] size size of list 
  @param[in] instr 
    The instrument in use
  @return
    The spectralformat list structure
*/
/*****************************************************************************/
xsh_spectralformat_list* xsh_spectralformat_list_create( int size, 
  xsh_instrument* instr)
{
  xsh_spectralformat_list* result = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_ILLEGAL( size > 0);

  XSH_CALLOC( result, xsh_spectralformat_list, 1);
  result->size = size;

  result->instrument = instr;
  XSH_CALLOC(result->list, xsh_spectralformat, result->size);  
  XSH_NEW_PROPERTYLIST(result->header);
  
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_spectralformat_list_free(&result);
    }  
    return result;
}
/*****************************************************************************/

/*****************************************************************************/
/**
  @brief
    Load a spectralformat list from a frame
  @param[in] frame 
    The table wich contains the spectral format
  @param[in] instr 
    The instrument in use
  @return 
    The spectralformat list structure
*/
/*****************************************************************************/
xsh_spectralformat_list* xsh_spectralformat_list_load(cpl_frame* frame,
  xsh_instrument* instr)
{
  cpl_table* table = NULL; 
  cpl_propertylist* header = NULL;
  const char* tablename = NULL;
  xsh_spectralformat_list * result = NULL;
  int i = 0 ;
  double value ;
  int size;

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

  /* get table filename */
  check( tablename = cpl_frame_get_filename( frame));

  XSH_TABLE_LOAD( table, tablename);
  
  check( size = cpl_table_get_nrow( table));
  check( result = xsh_spectralformat_list_create( size, instr));

  check( header = cpl_propertylist_load(tablename,1));
  check( cpl_propertylist_append(result->header, header));

  for(i=0;i<result->size;i++){
    const char * lamp ;

    /* load the order in structure */
    check( xsh_get_table_value( table, XSH_SPECTRALFORMAT_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT, i, &(result->list[i].absorder)));
    /* Lamp */
    check( lamp = cpl_table_get_string(table, 
      XSH_SPECTRALFORMAT_TABLE_COLNAME_LAMP, i));
    strcpy( result->list[i].lamp, lamp);
    /* wlminful */
    check( value = cpl_table_get( table, 
      XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMINFUL, i, NULL));
    result->list[i].lambda_min_full = (float)value;
    /* wlmaxful */
    check( value = cpl_table_get( table, 
      XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMAXFUL, i, NULL));
    result->list[i].lambda_max_full = (float)value;
    /* Wlmin */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMIN, i, NULL));
    result->list[i].lambda_min = (float)value;
    /* Wlmax */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMAX, i, NULL));
    result->list[i].lambda_max = (float)value;
    /* flsr */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_FLSR, i, NULL));
    result->list[i].flsr = (float)value;
    /* ufsr */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_UFSR, i, NULL));
    result->list[i].ufsr = (float)value;

    /* xmin */
    if(cpl_table_has_column(table,XSH_SPECTRALFORMAT_TABLE_COLNAME_XMIN)) {
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_XMIN, i, NULL));
    result->list[i].xmin = (float)value;
    /* xmax */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_XMAX, i, NULL));
    result->list[i].xmax = (float)value;

    /* ymin */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_YMIN, i, NULL));
    result->list[i].ymin = (float)value;
    /* ymax */
    check( value = cpl_table_get( table,
      XSH_SPECTRALFORMAT_TABLE_COLNAME_YMAX, i, NULL));
    result->list[i].ymax = (float)value;

    }
  }
  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_error_msg( "can't load frame %s",cpl_frame_get_filename(frame));
      xsh_spectralformat_list_free( &result);
    }
    xsh_free_propertylist( &header);
    XSH_TABLE_FREE( table);
    return result;  
}
/*****************************************************************************/

/*****************************************************************************/
/**
  @brief 
    Free memory associated to an spactralformat_list
  @param[in] list 
    The spectralformat_list to free
*/
/*****************************************************************************/
void xsh_spectralformat_list_free( xsh_spectralformat_list** list)
{
  if ( list && *list){
    /* free the list */
    if ( ( *list)->list){
      cpl_free( (*list)->list);
    }
    xsh_free_propertylist( &((*list)->header));
    cpl_free( *list);
    *list = NULL;
  }
}
/*****************************************************************************/



/*****************************************************************************/
/**
  @brief 
    Get header of the table
  @param[in] list 
    The spectralformat_list
  @return 
    The header associated to the table
*/
/*****************************************************************************/
cpl_propertylist* xsh_spectralformat_list_get_header(
  xsh_spectralformat_list* list)
{
  cpl_propertylist *res = NULL;

  XSH_ASSURE_NOT_NULL(list);
  res = list->header;
 cleanup:
  return res;
}
/*****************************************************************************/

/*****************************************************************************/
/** 
  @brief
    Returns lambda min for a given absolute order. 
  @param[in] list 
    Pointer to spectralformat structure
  @param[in] absorder 
    Absolute order number
  @return 
    Lambda min of this order
 */
/*****************************************************************************/
float xsh_spectralformat_list_get_lambda_min( xsh_spectralformat_list *list,
  int absorder)
{
  int i ;
  float res = 0. ;

  XSH_ASSURE_NOT_NULL(list);

  for( i = 0; i<list->size; i++){
    if ( list->list[i].absorder == absorder){
      res = list->list[i].lambda_min ;
      break ;
    }
  }
  cleanup:
    return res ;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
const char * xsh_spectralformat_list_get_lamp( xsh_spectralformat_list * list,
                                          int absorder )
{
  int i ;
  const char *res = NULL;

  XSH_ASSURE_NOT_NULL( list);

  for( i = 0 ; i<list->size ; i++ )
    if ( list->list[i].absorder == absorder ) {
      res = list->list[i].lamp ;
      break ;
    }

 cleanup:
  return res ;
}
/*****************************************************************************/

/*---------------------------------------------------------------------------*/
/** 
 * Returns lambda max for a given absolute order.
 * 
 * @param list Pointer to spectralformat structure
 * @param absorder Absolute order number
 * 
 * @return Lambda max of this order
 */
/*---------------------------------------------------------------------------*/
float xsh_spectralformat_list_get_lambda_max( xsh_spectralformat_list * list,
					 int absorder )
{
  int i ;
  float res = 0. ;

  XSH_ASSURE_NOT_NULL(list);
  for( i = 0 ; i<list->size ; i++ )
    if ( list->list[i].absorder == absorder ) {
      res = list->list[i].lambda_max ;
      break ;
    }

 cleanup:
  return res ;
}
/*****************************************************************************/

/*****************************************************************************/
/**
  @brief
    Returns list of absolute orders containing lambda
  @param[in] list
    Pointer to spectralformat structure
  @param[in] lambda
    Wavelength in nm to search
  @return
    Vector containing absolute orders matching with given lambda
*/
/*****************************************************************************/
cpl_vector* xsh_spectralformat_list_get_orders(
  xsh_spectralformat_list *list, float lambda)
{
  cpl_vector* result = NULL;
  int i;
  int size = 0;
  int order_tab[20];

  XSH_ASSURE_NOT_NULL(list);

  for( i = 0 ; i<list->size ; i++ ){
    float min=0.0, max=0.0;
     
    min = list->list[i].lambda_min_full;
    max = list->list[i].lambda_max_full;

    xsh_msg_dbg_high( "search lambda %f in [%f,%f]", lambda, min, max);

    if (lambda >= min && lambda <= max){
      order_tab[size] = list->list[i].absorder;
      size++;
    }
  }

  if (size > 0){
    check( result = cpl_vector_new( size));
    for( i=0; i< size; i++){
      check( cpl_vector_set( result, i, order_tab[i]));
    }
  }
  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_vector( &result);
    }
    return result;
}

void xsh_spectralformat_check_wlimit( xsh_spectralformat_list *spectralformat,
  xsh_order_list* orderlist, xsh_wavesol *wavesol, 
  xsh_xs_3 *model, xsh_instrument *instr)
{
  int iorder = 0;

  XSH_ASSURE_NOT_NULL( spectralformat);
  XSH_ASSURE_NOT_NULL( orderlist);

  for( iorder=0; iorder < spectralformat->size; iorder++){
    double absorder = 0;
    double slit = 0.0;
    double wmin, wmax;
    double ymin, ymax;
    double y_wmin, y_wmax;

    absorder = (double)spectralformat->list[iorder].absorder;
    wmin = spectralformat->list[iorder].lambda_min;
    wmax = spectralformat->list[iorder].lambda_max;

    ymin = (double)orderlist->list[iorder].starty;
    ymax = (double)orderlist->list[iorder].endy;
    
    if (wavesol != NULL){    
      check( y_wmin = xsh_wavesol_eval_poly( wavesol, wmin, absorder, 
        slit));
      check( y_wmax = xsh_wavesol_eval_poly( wavesol, wmax, absorder, 
        slit));
    }
    else{
      double x;

      check( xsh_model_get_xy( model, instr, wmin, absorder, slit,
             &x, &y_wmin));
      check( xsh_model_get_xy( model, instr, wmax, absorder, slit,
             &x, &y_wmax));
    }
    if ( y_wmin < ymin || y_wmin > ymax){
      xsh_msg_warning("For order %f at wmin %f : y %f not in [%f,%f]",
        absorder, wmin, y_wmin, ymin, ymax);
    }
    if ( y_wmax > ymax || y_wmax < ymin){
      xsh_msg_warning("For order %f at wmax %f : y %f not in [%f,%f]",
        absorder, wmax, y_wmax, ymin, ymax);
    }

    
  }
  
  cleanup:
    return;
}
/*****************************************************************************/
/**@}*/
