/*                                                                          *
 *   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-05-14 07:02:49 $
 * $Revision: 1.37 $
 */

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

/*-------------------------------------------------------------------------*/
/**
 * @defgroup test_xsh_rectify  Test rectify function
 * @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_drl.h>
#include <xsh_pfits.h>

#include <xsh_badpixelmap.h>

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

#include <getopt.h>

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

#define MODULE_ID "XSH_RECTIFY"

/*--------------------------------------------------------------------------
  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,
  DEBUG_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},
  {"debug", required_argument, 0, DEBUG_OPT},
  {"help", 0, 0, HELP_OPT},
  {0, 0, 0, 0}
};

static void Help( void )
{
  puts( "Unitary test of xsh_rectify" ) ;
  puts( "Usage: test_rectify [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( " --debug=<n>        : Level of debug NONE | LOW | MEDIUM | HIGH [MEDIUM]" );
  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" ) ;
  puts( " 3. [OPTIONAL] Shift slit tab\n" ) ;
  TEST_END();
}

static void HandleOptions( int argc, char **argv, 
  xsh_rectify_param *rectify_par, int *order_min, int *order_max,
  double *slit_min, int *nslit)
{
  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 NSLIT_OPT:
      sscanf( optarg, "%64d", nslit);
      break ;
    case DEBUG_OPT:
      if ( strcmp( optarg, "LOW")==0){
        xsh_debug_level_set( XSH_DEBUG_LEVEL_LOW);
      }
      else if ( strcmp( optarg, "HIGH")==0){
        xsh_debug_level_set( XSH_DEBUG_LEVEL_HIGH);
      }
      else if ( strcmp( optarg, "NONE")==0){
        xsh_debug_level_set( XSH_DEBUG_LEVEL_NONE);
      }
      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 ;

  cpl_frameset *wavesol_frameset = NULL;
  cpl_frameset *result_frameset = NULL;
  cpl_frameset *merge_frameset = NULL;
  cpl_frame *sci_frame = NULL;
  cpl_frame *orderlist_frame = NULL;
  cpl_frame *slitmap_frame = NULL;
  cpl_frame *wavesol_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 ;
  int order_min = -1, order_max = -1;
  int rec_min_index=-1, rec_max_index=-1;
  xsh_order_list* order_list = NULL;
  xsh_rectify_param rectify_par;
  int nslit_c=20, nslit=-100;
  double slit_min_c=0, slit_min=-100;
  XSH_MODE mode = XSH_MODE_SLIT;
  int merge_par = 0;
  cpl_frame *spectrum_frame = NULL;
  cpl_frame *slit_shifttab_frame = NULL;
  const char *slit_shifttab_name = NULL;
  cpl_frameset *slit_shifttab_frameset = NULL;
  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;
  rectify_par.rectify_full_slit=CPL_TRUE;

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

  if ( (argc - optind) >=2 ) {
    sci_name = argv[optind];
    sof_name = argv[optind+1];
    if ( (argc - optind) >=3 ) {
      slit_shifttab_name = argv[optind+2];
    }
  }
  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));

  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_instrument_set_decode_bp( instrument, decode_bp ) ;
  if ( slit_shifttab_name != NULL){
    if ( mode == XSH_MODE_SLIT){
      TESTS_XSH_FRAME_CREATE( slit_shifttab_frame, "SHIFTTAB_arm", slit_shifttab_name);
    }
    else{
      int i;
      slit_shifttab_frameset = cpl_frameset_new();
      for( i=0; i< 3; i++){
        TESTS_XSH_FRAME_CREATE( slit_shifttab_frame, "SHIFTTAB_arm", slit_shifttab_name);
        cpl_frameset_insert( slit_shifttab_frameset, slit_shifttab_frame);
      }
    }
  }
  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));
  }
  if ( slit_shifttab_frame != NULL){
    xsh_msg("SHIFTTAB : %s",
      cpl_frame_get_filename( slit_shifttab_frame));
  }  

  if ( mode == XSH_MODE_SLIT){ 
    wavesol_frame = xsh_find_wave_tab_2d( set, instrument);

    if ( wavesol_frame == NULL){
      model_frame = xsh_find_model_config_tab( set, instrument);
    }

    if ( wavesol_frame != NULL){
      xsh_msg("WAVESOL        : %s",
        cpl_frame_get_filename( wavesol_frame));
    }
    else {
      xsh_msg("MODEL         : %s",
      cpl_frame_get_filename( model_frame));
    }
  }
  else{
    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;
  }

  if ( mode == XSH_MODE_SLIT){
    const char *tag = XSH_GET_TAG_FROM_ARM( XSH_ORDER2D, instrument);
    check( recorder_frame = xsh_rectify_orders( sci_frame, order_list,
      wavesol_frame, model_frame, instrument, &rectify_par,
      spectralformat_frame, NULL, "RECTIFIED.fits", tag, &recordereso_frame,
      &recordertab_frame,rec_min_index, rec_max_index, slit_min, nslit, 0,
      slit_shifttab_frame));
    check( spectrum_frame = xsh_merge_ord( recorder_frame, instrument, 
      merge_par,"test"));
  }
  else{
    xsh_msg("Rectify in IFU");
    check( result_frameset = xsh_rectify_orders_ifu( sci_frame, order_list,
    wavesol_frameset, slit_shifttab_frameset, model_frame, instrument, &rectify_par, 
    spectralformat_frame, slitmap_frame,NULL,NULL,rec_min_index,
    rec_max_index, "test"));
    xsh_msg( "Merge in IFU");
    check( merge_frameset = xsh_merge_ord_ifu( result_frameset, instrument, merge_par,"test"));
  }

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


