/*                                                                          *
 *   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-01-16 21:09:42 $
 * $Revision: 1.11 $
 */

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

/*-------------------------------------------------------------------------*/
/**
 * @defgroup test_xsh_geom_ifu  Test geom_ifu result
 * @ingroup unit_tests
 * 
 */
/*-------------------------------------------------------------------------*/
/**@{*/

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

#include <tests.h>

#include <xsh_data_pre.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_data_instrument.h>
#include <xsh_data_the_map.h>
#include <xsh_data_localization.h>
#include <xsh_data_spectralformat.h>
#include <xsh_utils_table.h>

#include <xsh_drl.h>
#include <xsh_pfits.h>

#include <xsh_badpixelmap.h>

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

#include <getopt.h>

/*--------------------------------------------------------------------------
  Defines
  --------------------------------------------------------------------------*/

#define MODULE_ID "XSH_GEOM_IFU"

/*--------------------------------------------------------------------------
  Implementation
  --------------------------------------------------------------------------*/
enum {
  KERNEL_OPT, RADIUS_OPT, BIN_LAMBDA_OPT, BIN_SPACE_OPT, HELP_OPT, 
  MIN_ORDER_OPT, MAX_ORDER_OPT, SLIT_MIN_OPT, NSLIT_OPT, MERGE_METHOD_OPT,
  LAMBDA_REF_OPT
} ;

static const char * Options = "" ;

static struct option long_options[] = {
  {"kernel", required_argument, 0, KERNEL_OPT},
  {"radius", required_argument, 0, RADIUS_OPT},
  {"bin-lambda", required_argument, 0, BIN_LAMBDA_OPT},
  {"bin-space", required_argument, 0, BIN_SPACE_OPT},
  {"order-min", required_argument, 0, MIN_ORDER_OPT},
  {"order-max", required_argument, 0, MAX_ORDER_OPT},
  {"slit-min",required_argument, 0, SLIT_MIN_OPT},
  {"slit-n",required_argument, 0, NSLIT_OPT},
  {"merge-method", required_argument, 0, MERGE_METHOD_OPT},
  {"lambda-ref", required_argument, 0, LAMBDA_REF_OPT},
  {"help", 0, 0, HELP_OPT},
  {0, 0, 0, 0}
};

static void Help( void )
{
  puts( "Unitary test of xsh_geom_ifu" ) ;
  puts( "Usage: test_xsh_geom_ifu [options] <input_files>" ) ;
  puts( "Options" ) ;
  puts( " --kernel=<name>    : TANH, SINC, SINC2, LANCZOS, HAMMING, HANN [TANH]");
  puts( " --radius=<nn>      : Radius [4]" ) ;
  puts( " --bin-lambda=<n>   : Bin in Lambda [0.1]" ) ;
  puts( " --bin-space=<n>    : Bin in Slit [0.1]" ) ;
  puts( " --order-min=<n>    : Minimum abs order" );
  puts( " --order-max=<n>    : Maximum abs order" );
  puts( " --slit-min=<n>     : Minimum slit to rectify" );
  puts( " --slit-n=<n>       : Number of pixels in slit rectified frame" );
  puts( " --lambda-ref=<n>   : Lambda reference for creating SLICE OFFSET");
  puts( " --help             : What you see" ) ;
  puts( "\nInput Files" ) ;
  puts( "The input files argument MUST be in this order:" ) ;
  puts( " 1. Science frame in PRE format" ) ;
  puts( " 2. SOF\n" ) ;
  TEST_END();
}

static void HandleOptions( int argc, char **argv, 
  xsh_rectify_param *rectify_par, xsh_localize_obj_param *locobj_par,
  int *order_min, int *order_max,
  double *slit_min, int *nslit, double *lambda_ref)
{
  int opt ;
  int option_index = 0;

  while (( opt = getopt_long (argc, argv, Options,
                              long_options, &option_index)) != EOF )
    switch ( opt ) {
    case KERNEL_OPT:
      strcpy( rectify_par->rectif_kernel, optarg);
      if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_TANH)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_TANH;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_TANH)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_TANH;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_SINC)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_SINC;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_SINC2)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_SINC2;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_LANCZOS)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_LANCZOS;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_HAMMING)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_HAMMING;
      }
      else if ( strcmp( optarg, RECTIFY_KERNEL_PRINT( CPL_KERNEL_HANN)) == 0){
        rectify_par->kernel_type = CPL_KERNEL_HANN;
      }
      else{
        xsh_msg("Invalid kernel option %s", optarg);
        Help();
        exit(0);
      }
      break;
    case RADIUS_OPT:
      rectify_par->rectif_radius = atof( optarg);
      break ;
    case BIN_LAMBDA_OPT:
      sscanf( optarg, "%64lf", &rectify_par->rectif_bin_lambda ) ;
      break ;
    case BIN_SPACE_OPT:
      sscanf( optarg, "%64lf", &rectify_par->rectif_bin_space ) ;
      break ;
    case MIN_ORDER_OPT:
      sscanf( optarg, "%64d", order_min);
      break;
    case MAX_ORDER_OPT:
      sscanf( optarg, "%64d", order_max);
      break;
    case SLIT_MIN_OPT:
      sscanf( optarg, "%64lf", slit_min) ;
      break ;
    case LAMBDA_REF_OPT:
      sscanf( optarg, "%64lf", lambda_ref);
      break ;
    case NSLIT_OPT:
      sscanf( optarg, "%64d", nslit);
      break ;
    default: Help() ; exit( 0 ) ;
    }
}

/**
  @brief
    Unit test of xsh_rectify. Needs the PRE frame, order table, wave solution,
        instrument, rectify parameters, the map.
  @return
    0 if success

*/
int main( int argc, char **argv)
{
  /* Declarations */
  int ret = 0 ;

  xsh_instrument* instrument = NULL;

  const char *sof_name = NULL;
  cpl_frameset *set = NULL;
  const char * sci_name = NULL ;
  xsh_localize_obj_param loc_obj_par = {10, 0.1, 1, 0, LOC_GAUSSIAN_METHOD, 0, 0.5, 2.0, 3, FALSE};;

  cpl_frameset *wavesol_frameset = NULL;
  cpl_frameset *rect_frameset = NULL;
  cpl_frameset *loc_frameset = NULL;
  cpl_frame *sci_frame = NULL;
  cpl_frame *orderlist_frame = NULL;
  cpl_frame *slitmap_frame = NULL;
  cpl_frame *model_frame = NULL;
  cpl_frame *spectralformat_frame = NULL;
  cpl_frame *recorder_frame = NULL ;
  cpl_frame *recordereso_frame = NULL ;
  cpl_frame *recordertab_frame = NULL ;
  cpl_frame *cube = NULL;
  int order_min = -1, order_max = -1;
  int rec_min_index=-1, rec_max_index=-1;
  double lambda_ref = -1;
  xsh_order_list* order_list = NULL;
  xsh_rectify_param rectify_par;
  int nslit_c, nslit=-100;
  double slit_min_c, slit_min=-100;
  XSH_MODE mode;
  const int decode_bp=2147483647;
  /* Initialize libraries */
  TESTS_INIT(MODULE_ID);
  cpl_msg_set_level(CPL_MSG_DEBUG);
  xsh_debug_level_set(XSH_DEBUG_LEVEL_MEDIUM);

  /* Set rectify default params */
  strcpy( rectify_par.rectif_kernel, "default");
  rectify_par.kernel_type = CPL_KERNEL_TANH;
  rectify_par.rectif_radius = 4 ;
  rectify_par.rectif_bin_lambda = 0.1 ;
  rectify_par.rectif_bin_space = 0.1 ;
  rectify_par.conserve_flux = FALSE;
  
  loc_obj_par.loc_deg_poly = 0;

  HandleOptions( argc, argv, &rectify_par, &loc_obj_par, &order_min, &order_max,
    &slit_min, &nslit, &lambda_ref);

  if ( (argc - optind) >=2 ) {
    sci_name = argv[optind];
    sof_name = argv[optind+1];
  }
  else {
    Help();
    exit(0);
  }
  /* Create frameset from sof */
  check( set = sof_to_frameset( sof_name));
  /* Validate frame set */
  check( instrument = xsh_dfs_set_groups( set));
  xsh_instrument_set_decode_bp( instrument, decode_bp ) ;

  check( spectralformat_frame = xsh_find_spectral_format( set, instrument));
  check( orderlist_frame = xsh_find_order_tab_edges( set, instrument));
  check( slitmap_frame = xsh_find_slitmap( set, instrument));

  TESTS_XSH_FRAME_CREATE( sci_frame, "SLIT_STARE_DIVIDED_arm", sci_name);
  check( mode = xsh_instrument_get_mode( instrument));

  xsh_msg("SCI            : %s",
    cpl_frame_get_filename( sci_frame));
  xsh_msg("ORDERLIST      : %s",
    cpl_frame_get_filename( orderlist_frame));
  xsh_msg("SPECTRALFORMAT : %s",
    cpl_frame_get_filename( spectralformat_frame));
  if ( slitmap_frame != NULL){
    xsh_msg("SLITMAP : %s",
      cpl_frame_get_filename( slitmap_frame));
  }
  
  wavesol_frameset = xsh_find_wave_tab_ifu( set, instrument);
  cpl_error_reset();
  if ( wavesol_frameset == NULL){
    model_frame = xsh_find_model_config_tab( set, instrument);
    xsh_msg("MODEL         : %s",
    cpl_frame_get_filename( model_frame));
  }
  else{
    int iframe;
    int size;

    size = cpl_frameset_get_size( wavesol_frameset);   	
    for(iframe=0; iframe < size; iframe++){
      cpl_frame *frame = cpl_frameset_get_frame(wavesol_frameset, iframe);
      xsh_msg("WAVESOL        : %s",
        cpl_frame_get_filename( frame));
    }
  }

  xsh_msg(" Parameters ");
  xsh_msg(" kernel %s", RECTIFY_KERNEL_PRINT( rectify_par.kernel_type));
  xsh_msg(" radius %f", rectify_par.rectif_radius);
  xsh_msg(" bin-space %f", rectify_par.rectif_bin_space);
  xsh_msg(" bin-lambda %f", rectify_par.rectif_bin_lambda);


  check( order_list = xsh_order_list_load ( orderlist_frame, instrument));

  if ( order_min != -1) {
    check( rec_min_index = xsh_order_list_get_index_by_absorder( order_list, 
      order_min));
    xsh_msg("Order min %d => index %d", order_min, rec_min_index);
  }
  else{
    rec_min_index = 0;
  }

  if ( order_max != -1) {
    check( rec_max_index = xsh_order_list_get_index_by_absorder( order_list,
      order_max));
    xsh_msg("Order max %d => index %d", order_max, rec_max_index);
  }
  else{
    rec_max_index = order_list->size;
  }


  /* Now rectify the frame */

  check( xsh_rec_slit_size( &rectify_par, 
    &slit_min_c, &nslit_c, mode));

  if (slit_min == -100){
    slit_min = slit_min_c;
  }
  if ( nslit == -100){
    nslit = nslit_c;
  }

  xsh_msg("SLIT min = %f and has size %d", slit_min, nslit);
  xsh_msg("Lambda ref %f", lambda_ref);
  if (lambda_ref < 0.0){
    check( rect_frameset = xsh_rectify_orders_ifu( sci_frame, order_list,
      wavesol_frameset, NULL, model_frame, instrument, &rectify_par, 
      spectralformat_frame, slitmap_frame,NULL,NULL,rec_min_index,
      rec_max_index, "test"));
  }
  else{
    double lambda_ref_min;
    double lambda_ref_max;
    const char * tablename = NULL;
    cpl_table *table = NULL;
    cpl_frame *spectralformat2_frame = NULL;
    xsh_spectralformat_list* list = NULL;
    cpl_vector* orders = NULL;
    int size, absorder, iorder;

    lambda_ref_min = lambda_ref-rectify_par.rectif_bin_lambda*10;
    lambda_ref_max = lambda_ref+rectify_par.rectif_bin_lambda*10;
    
    xsh_msg( "Do cube for reference %f ==> %f", lambda_ref_min, lambda_ref_max);
    check( list = xsh_spectralformat_list_load( spectralformat_frame, 
      instrument));
    check( orders = xsh_spectralformat_list_get_orders( list, lambda_ref));
    size = cpl_vector_get_size( orders);

    tablename = cpl_frame_get_filename( spectralformat_frame);
    XSH_TABLE_LOAD( table, tablename);

    for( iorder=0; iorder < size; iorder++){
      absorder = cpl_vector_get( orders, iorder);
      xsh_msg("In Order %d", absorder);
      cpl_table_set_int(table, XSH_SPECTRALFORMAT_TABLE_COLNAME_ORDER, iorder, absorder); 
      cpl_table_set_float(table, XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMIN, iorder, lambda_ref_min);
      cpl_table_set_float(table, XSH_SPECTRALFORMAT_TABLE_COLNAME_WLMAX, iorder, lambda_ref_max);
    }
    cpl_table_save( table, NULL,NULL,"LAMBDA_REF_FORMAT.fits",CPL_IO_DEFAULT);
    TESTS_XSH_FRAME_CREATE( spectralformat2_frame, "SPEC_arm", "LAMBDA_REF_FORMAT.fits");

    check( rect_frameset = xsh_rectify_orders_ifu( sci_frame, order_list,
      wavesol_frameset, NULL, model_frame, instrument, &rectify_par,
      spectralformat2_frame, slitmap_frame,NULL,NULL, 0,
      size-1, "test"));
  }
  /* Localize object */
  xsh_msg( "---Localize the objects for IFU");    
  check( loc_frameset = xsh_localize_obj_ifu( rect_frameset,
    NULL, instrument, &loc_obj_par, NULL));

  cleanup:
     if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_error_dump(CPL_MSG_ERROR);
      ret = 1;
    }
    xsh_order_list_free( &order_list);
    xsh_free_frame( &sci_frame);
    xsh_free_frameset( &rect_frameset);
    xsh_free_frameset( &loc_frameset);
    xsh_free_frameset( &wavesol_frameset);
    xsh_free_frameset( &set);
    xsh_instrument_free( &instrument);
    xsh_free_frame( &recorder_frame);
    xsh_free_frame( &recordereso_frame);
    xsh_free_frame( &recordertab_frame);
    xsh_free_frame( &cube);
    TEST_END();
    return ret;
}
/**@}*/


