/*                                                                          *
 *   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.30 $
 */

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

/*-------------------------------------------------------------------------*/
/**
  @defgroup test_xsh_opt_extract  Test Object optimal extraction
  @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_rec.h>
#include <xsh_data_localization.h>
#include <xsh_drl.h>
#include <xsh_pfits.h>
#include <xsh_model_utils.h>
#include <xsh_badpixelmap.h>

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

#include <getopt.h>

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

#define MODULE_ID "XSH_OPT_EXTRACT"

enum {
  OVERSAMPLE_OPT, BOX_HSIZE_OPT, CHUNK_SIZE_OPT, LAMBDA_STEP_OPT, 
  CLIP_KAPPA_OPT, CLIP_FRAC_OPT, CLIP_NITER_OPT, NITER_OPT, METHOD_OPT, 
  MIN_ORDER_OPT, MAX_ORDER_OPT, DEBUG_OPT, HELP_OPT
};

static struct option long_options[] = {
  {"oversample", required_argument, 0, OVERSAMPLE_OPT},
  {"box-hsize", required_argument, 0, BOX_HSIZE_OPT},
  {"chunk-size", required_argument, 0, CHUNK_SIZE_OPT},
  {"lambda-step", required_argument, 0, LAMBDA_STEP_OPT},
  {"clip-kappa", required_argument, 0, CLIP_KAPPA_OPT},
  {"clip-frac", required_argument, 0, CLIP_FRAC_OPT},
  {"clip-niter", required_argument, 0, CLIP_NITER_OPT},
  {"niter", required_argument, 0, NITER_OPT},
  {"method", required_argument, 0, METHOD_OPT},
  {"order-min", required_argument, 0, MIN_ORDER_OPT},
  {"order-max", required_argument, 0, MAX_ORDER_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_opt_extract");
  puts( "Usage: test_xsh_opt_extract [options] <input_files>");

  puts( "Options" ) ;
  puts( " --oversample=<n>   : Oversample factor [5]" ) ;
  puts( " --box-hsize=<n>    : Extract box half size [pixels] [10]" ) ;
  puts( " --chunk-size=<n>   : Chunk size [pixels] [50]" ) ;
  puts( " --lambda-step=<n>  : Step in wavelength [0.02]" );
  puts( " --clip-kappa=<nn>  : Kappa for cosmics Ray hits rejection [3]" ) ;
  puts( " --clip-frac=<nn>   : Maxium bad pixels fraction for cosmics Ray hits rejection [0.4]" );  
  puts( " --clip-niter=<n>   : Number of iterations for cosmics Ray hits rejection [2]" ) ;
  puts( " --niter=<n>        : Number of iterations [1]" ) ;
  puts( " --method=<string>  : Extraction method GAUSSIAN | GENERAL [GAUSSIAN]" ) ;
  puts( " --order-min=<n>    : Minimum abs order" );
  puts( " --order-max=<n>    : Maximum abs order" );
  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 sky subtracted" ) ;
  puts( " 2. Localization table" );
  puts( " 3. SOF [SPECTRAL_FORMAT, ORDER_TAB_EDGES,WAVE_TAB_2D, WAVEMAP, SLITMAP, MASTER_FLAT]\n" ) ;
  TEST_END();
}

static void HandleOptions( int argc, char **argv,
  xsh_opt_extract_param *opt_extract_par, int *order_min, int *order_max)
{
  int opt ;
  int option_index = 0;

  while (( opt = getopt_long (argc, argv, "oversample:box-hsize:chunk-size",
    long_options, &option_index)) != EOF ){

    switch ( opt ) {
    case  OVERSAMPLE_OPT:
      opt_extract_par->oversample = atoi( optarg);
      break ;
    case  BOX_HSIZE_OPT:
      opt_extract_par->box_hsize = atoi( optarg);
      break ;
    case CHUNK_SIZE_OPT:
      opt_extract_par->chunk_size = atoi( optarg);
      break; 
    case LAMBDA_STEP_OPT:
      opt_extract_par->lambda_step = atof( optarg);
      break;
    case CLIP_KAPPA_OPT:
      opt_extract_par->clip_kappa = atof( optarg);
      break;
    case CLIP_FRAC_OPT:
      opt_extract_par->clip_frac = atof( optarg);
      break;
    case CLIP_NITER_OPT:
      opt_extract_par->clip_niter = atoi( optarg);
      break;
    case NITER_OPT:
      opt_extract_par->niter = atoi( optarg);
      break;
    case METHOD_OPT:
      if ( strcmp( OPTEXTRACT_METHOD_PRINT(GAUSS_METHOD), optarg) == 0){
        opt_extract_par->method = GAUSS_METHOD;
      } 
      else {
        opt_extract_par->method = GENERAL_METHOD;
      }
      break;  
    case MIN_ORDER_OPT:
      sscanf( optarg, "%64d", order_min);
      break;
    case MAX_ORDER_OPT:
      sscanf( optarg, "%64d", order_max);
      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(-1);
    }
  }
  return;
}

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

/**
  @brief
    Unit test of xsh_opt_extract
  @return
    0 if success

*/
int main( int argc, char **argv)
{
  int ret;
  /* Declarations */
  xsh_instrument* instrument = NULL;
  const char *sof_name = NULL;
  cpl_frameset *set = NULL;
 
  const char* sci_name = NULL;
  const char* loc_name = NULL;
  xsh_opt_extract_param opt_extract_par = { 5, 10, 10, 0.01, 10, 1., 2, 2, GAUSS_METHOD};
  int merge_par = 0; /* MEAN_MERGE_METHOD */
  int order_min = -1;
  int order_max = -1;
  int rec_min_index = -1;
  int rec_max_index = -1;

  xsh_order_list* order_list = NULL;

  cpl_frame *sci_frame = NULL;
  cpl_frame *loc_frame = NULL;
  cpl_frame *orderlist_frame = NULL;
  cpl_frame *wavesol_frame = NULL;
  cpl_frame *model_frame = NULL;
  cpl_frame *wavemap_frame = NULL;
  cpl_frame *slitmap_frame = NULL;
  cpl_frame *spectralformat_frame = NULL;
  cpl_frame *masterflat_frame = NULL;
  cpl_frame *orderext1d_frame = NULL;
  cpl_frame *orderoxt1d_frame = NULL;
  cpl_frame *orderoxt1d_eso_frame = NULL;
  cpl_frame *qc_subex_frame = NULL;
  cpl_frame *qc_s2ddiv1d_frame = NULL;
  cpl_frame *qc_model_frame = NULL;
  cpl_frame *qc_weight_frame = NULL;

  cpl_frame *spectrumext1d_frame = NULL;
  cpl_frame *spectrumoxt1d_frame = 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);

  opt_extract_par.oversample = 5;
  opt_extract_par.box_hsize = 10;
  opt_extract_par.chunk_size = 50;
  opt_extract_par.lambda_step = 0.02;
  opt_extract_par.clip_kappa = 3;
  opt_extract_par.clip_frac = 0.4;
  opt_extract_par.clip_niter = 2;
  opt_extract_par.niter = 1;
  opt_extract_par.method = GAUSS_METHOD;

  /* Analyse parameters */
  HandleOptions( argc, argv, &opt_extract_par, &order_min, &order_max);

  if ( (argc - optind) >= 3 ) {
    sci_name = argv[optind];
    loc_name = argv[optind+1];
    sof_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( wavemap_frame = xsh_find_wavemap( set, instrument));
  check( slitmap_frame = xsh_find_slitmap( set, instrument));
  check( masterflat_frame = xsh_find_master_flat(  set, instrument));
  xsh_instrument_set_decode_bp( instrument, decode_bp ) ;

  if(( model_frame = xsh_find_frame_with_tag( set, XSH_MOD_CFG_OPT_2D,
    instrument)) == NULL) {
    xsh_error_reset();
    if ((model_frame = xsh_find_frame_with_tag( set,XSH_MOD_CFG_TAB,
                                               instrument)) == NULL) {
       xsh_error_reset();
    }
  }
  if ( model_frame == NULL){
    check( wavesol_frame = xsh_find_wave_tab_2d( set, instrument));
  } 
  TESTS_XSH_FRAME_CREATE( sci_frame, "OBJECT_SLIT_STARE_arm", sci_name);
  TESTS_XSH_FRAME_CREATE( loc_frame, "LOCALIZATION_arm", loc_name);
  /* USE load function */
  xsh_msg("SCI            : %s",
    cpl_frame_get_filename( sci_frame));
  xsh_msg("LOCALIZATION   : %s",
    cpl_frame_get_filename( loc_frame));
  xsh_msg("ORDERLIST      : %s",
    cpl_frame_get_filename( orderlist_frame));
  if (model_frame != NULL){
    int found_line=0;
    check(xsh_model_temperature_update_frame(&model_frame,sci_frame,
                                             instrument,&found_line));
    xsh_msg("MODEL        : %s",
      cpl_frame_get_filename( model_frame));
  }
  else{
    xsh_msg("WAVESOL        : %s",
        cpl_frame_get_filename( wavesol_frame));
  }
  xsh_msg("SPECTRALFORMAT : %s",
    cpl_frame_get_filename( spectralformat_frame)); 
  xsh_msg("WAVEMAP        : %s",
    cpl_frame_get_filename( wavemap_frame));
  xsh_msg("SLITMAP        : %s",
    cpl_frame_get_filename( slitmap_frame));
  xsh_msg("MASTERFLAT     : %s",
    cpl_frame_get_filename( masterflat_frame));

  xsh_msg(" Parameters ");
  xsh_msg(" oversample %d", opt_extract_par.oversample);
  xsh_msg(" box-hsize %d", opt_extract_par.box_hsize);
  xsh_msg(" chunk-size %d", opt_extract_par.chunk_size);
  xsh_msg(" lambda-step %f", opt_extract_par.lambda_step);
  xsh_msg(" clip-kappa %f", opt_extract_par.clip_kappa);
  xsh_msg(" clip-frac %f", opt_extract_par.clip_frac);
  xsh_msg(" clip-niter %d", opt_extract_par.clip_niter);
  xsh_msg(" niter %d", opt_extract_par.niter);
  xsh_msg(" method %s", OPTEXTRACT_METHOD_PRINT(opt_extract_par.method));

  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;
  }

  check( xsh_opt_extract_orders( sci_frame, orderlist_frame, wavesol_frame, model_frame,
    wavemap_frame, slitmap_frame, loc_frame, spectralformat_frame, masterflat_frame,
    instrument, &opt_extract_par, rec_min_index, rec_max_index, "TEST",
                                 &orderext1d_frame, &orderoxt1d_frame,
                                 &orderoxt1d_eso_frame, &qc_subex_frame, &qc_s2ddiv1d_frame,
                                 &qc_model_frame, &qc_weight_frame));

  check( spectrumext1d_frame = xsh_merge_ord( orderext1d_frame, instrument, merge_par,"test"));
  check( spectrumoxt1d_frame = xsh_merge_ord( orderoxt1d_frame, instrument, merge_par,"test"));

  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_frame( &loc_frame);
    xsh_free_frameset( &set);
    xsh_instrument_free( &instrument);
    xsh_free_frame( &orderext1d_frame);
    xsh_free_frame( &orderoxt1d_frame);
    xsh_free_frame( &orderoxt1d_eso_frame);
    xsh_free_frame( &qc_subex_frame);
    xsh_free_frame(&qc_s2ddiv1d_frame);
    xsh_free_frame(&qc_model_frame);
    xsh_free_frame(&qc_weight_frame);

    xsh_free_frame( &spectrumext1d_frame);
    xsh_free_frame( &spectrumoxt1d_frame);

    TEST_END();
    return ret;
}

/**@}*/
