/*                                                                          *
 *   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:10:13 $
 * $Revision: 1.20 $
 */

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

/*-------------------------------------------------------------------------*/
/**
 * @defgroup test_xsh_combine_nod  Test combine nod 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_COMBINE_NOD"

/*--------------------------------------------------------------------------
  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, USELOC_OPT,
  NOD_SET_OPT, NOD_ORDER_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},
  {"use-localization",required_argument, 0, USELOC_OPT},
  {"nod-set",required_argument, 0, NOD_SET_OPT},
  {"nod-order",required_argument, 0, NOD_ORDER_OPT},
  {"help", 0, 0, HELP_OPT},
  {0, 0, 0, 0}
};

static void Help( void )
{
  puts( "Unitary test of xsh_combine_nod" ) ;
  puts( "Usage: test_rectify [options] <input_files>" ) ;
  puts( "Options" ) ;
  puts( " --kernel=<name>    : Name of the rectify kernel" ) ;
  puts( " --radius=<nn>      : Radius (default 4)" ) ;
  puts( " --bin-lambda=<n>   : Bin in Lambda (default 0.1)" ) ;
  puts( " --bin-space=<n>    : Bin in Slit (default 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( " --use-localization : 1 if tou want to use it");
  puts( " --nod-set=<file>   : Set of rectified shifted frames with tag OBJECT_SLIT_NOD_arm");
  puts( " --nod-order=<n>    : abs order where we analyse nod" );
  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, int *order_min, int *order_max,
  double *slit_min, int *nslit, int *use_loc, char** nod_set_name)
{
  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 ) ;
      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 USELOC_OPT:
      sscanf( optarg, "%64d", use_loc);
      break;
    case NOD_SET_OPT:
      *nod_set_name = optarg;
      break;
    case NOD_ORDER_OPT:
      sscanf( optarg, "%64d", order_min);
      break;
    default: Help() ; exit( 0 ) ;
    }
}


cpl_frame *create_zero_rectify( cpl_frame* rec_frame, int iorder, xsh_instrument* instr)
{
  const char* rec_name = NULL;
  xsh_rec_list* rec_list = NULL;
  int ilambda=0, islit=0;
  int nlambda = 0, nslit=0;
  float *flux = NULL;
  char name[256];
  char *shortname = NULL;
  cpl_frame *res_frame = NULL;

  XSH_ASSURE_NOT_NULL( rec_frame);

  check( rec_name = cpl_frame_get_filename( rec_frame));

  shortname = strrchr( rec_name, '/');

  if (shortname != NULL){
    sprintf( name, "ZERO_%s",shortname+1);
  }
  else{
    sprintf( name, "ZERO_%s",rec_name);
  }

  xsh_msg( "name %s", name);

  check( rec_list = xsh_rec_list_load( rec_frame, instr));

  check( nlambda = xsh_rec_list_get_nlambda( rec_list, iorder));
  check( nslit = xsh_rec_list_get_nslit( rec_list, iorder));
  check( flux = xsh_rec_list_get_data1( rec_list, iorder));

  for( islit=0; islit < nslit; islit++){
    double val =0.0;

    for(ilambda=0; ilambda < nlambda; ilambda++){
      flux[ilambda+islit*nlambda] = val;
    }
  }

  check( res_frame = xsh_rec_list_save( rec_list, name, "ZERO_REC", 1));

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &res_frame);
    }
    xsh_rec_list_free( &rec_list);
    return res_frame; 
}





void analyse_rectify( cpl_frame* rec_frame, int iorder, xsh_instrument* instr)
{
  const char* rec_name = NULL;
  xsh_rec_list* rec_list = NULL;
  int ilambda=0, islit=0;
  int nlambda = 0, nslit=0;
  float *flux = NULL;
  float *slit = NULL;
  char name[256];
  FILE* datfile = NULL;
  char *shortname = NULL;

  XSH_ASSURE_NOT_NULL( rec_frame);

  check( rec_name = cpl_frame_get_filename( rec_frame));

  shortname = strrchr( rec_name, '/');

  if (shortname != NULL){
    rec_name = shortname+1;
  }

  check( rec_list = xsh_rec_list_load( rec_frame, instr));

  check( nlambda = xsh_rec_list_get_nlambda( rec_list, iorder));
  check( nslit = xsh_rec_list_get_nslit( rec_list, iorder));
  check( flux = xsh_rec_list_get_data1( rec_list, iorder));
  check( slit = xsh_rec_list_get_slit( rec_list, iorder));

  sprintf( name, "%s_profil.dat",rec_name);
  datfile = fopen( name, "w");
  fprintf( datfile, "#slit flux\n");
    
  for( islit=0; islit < nslit; islit++){
    double val =0.0;

    for(ilambda=0; ilambda < nlambda; ilambda++){
      val+= flux[ilambda+islit*nlambda];
    }
    fprintf( datfile,"%f %f\n", slit[islit], val);
  }
  fclose(datfile);

  cleanup:
    xsh_rec_list_free( &rec_list);
    return;
}
/**
  @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;

  char *sof_name = NULL;
  cpl_frameset *set = NULL;

  const char * raw_sof_name = NULL ;
  cpl_frameset *raw_set = NULL;

  cpl_frame *sci_frame = NULL;
  cpl_frame *orderlist_frame = NULL;
  cpl_frame *wavesol_frame = NULL;
  cpl_frame *model_frame = NULL;
  cpl_frame *spectralformat_frame = NULL;
  cpl_frame *rec_ab_frame = NULL ;
  cpl_frame *rec_abeso_frame = NULL ;
  cpl_frame *rec_abtab_frame = NULL ;
  cpl_frame *rec_ba_frame = NULL ;
  cpl_frame *rec_bashift_frame = NULL ;
  cpl_frame *rec_bashifteso_frame = NULL ;
  cpl_frame *loc_ab_frame = NULL;
  cpl_frame *loc_ba_frame = NULL;
  cpl_frameset *nod_set = NULL;
  cpl_frameset *zero_nod_set = NULL;
  cpl_frameset *combine_set = NULL;

  cpl_frame *combine_nod_frame = NULL;
  cpl_frame *combine_nodeso_frame = NULL;

  int order_min = -1, order_max = -1;
  int inod = 0;
  int rec_min_index=-1, rec_max_index=-1;
  xsh_order_list* order_list = NULL;
  xsh_rectify_param rectify_par;
  xsh_localize_obj_param loc_par;
  xsh_combine_nod_param cnod_par;
  int nslit_c, nslit=-100;
  int use_loc_obj = 0;
  double slit_min_c, slit_min=-100;
  XSH_MODE mode;
  const char *tag = NULL;
  int i, nb_frames;
  double *ref_ra = NULL, *ref_dec = 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 = 0 ;
  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_par.method = LOC_MAXIMUM_METHOD;
  loc_par.loc_chunk_nb = 10;
  loc_par.loc_deg_poly = 0;
  loc_par.loc_thresh = 0.1;

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

  if ( sof_name != NULL){
    xsh_msg("Using nod set : %s", sof_name);
    check( nod_set = sof_to_frameset( sof_name));
    check( instrument = xsh_dfs_set_groups( nod_set));
    xsh_instrument_set_decode_bp( instrument, decode_bp ) ;
    check( nb_frames = cpl_frameset_get_size( nod_set));

    for ( i = 0 ; i < nb_frames ; i++ ) {
      check( sci_frame = cpl_frameset_get_frame( nod_set, i));
      xsh_msg("NOD : %s", cpl_frame_get_filename( sci_frame));
      check( analyse_rectify( sci_frame, inod, instrument));
    }

  }
  else{
    xsh_msg("Building nod set");
    if ( (argc - optind) >=2 ) {
      raw_sof_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( raw_set = sof_to_frameset( raw_sof_name));
    check( mode = xsh_instrument_get_mode( instrument));

    /* Create list of_reclist */
    check( nb_frames = cpl_frameset_get_size( raw_set));
  
    for ( i = 0 ; i < nb_frames ; i++ ) {
      check( sci_frame = cpl_frameset_get_frame( raw_set, i));
      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 ( 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));
      }
    }

    xsh_msg(" Parameters ");
    xsh_msg(" kernel %s", rectify_par.rectif_kernel);
    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);

    tag = XSH_GET_TAG_FROM_ARM( XSH_ORDER2D, instrument);

    check( rec_ab_frame = xsh_rectify_orders( sci_frame, order_list,
      wavesol_frame, model_frame, instrument, &rectify_par,
      spectralformat_frame, NULL, "REC_A_B.fits", tag, 
                                            &rec_abeso_frame,&rec_abtab_frame,
      rec_min_index, rec_max_index, slit_min, nslit, 0,NULL));

    check( rec_ba_frame = xsh_rec_list_frame_invert( rec_ab_frame, tag,
      instrument));
    nod_set = cpl_frameset_new();
    cpl_frameset_insert( nod_set, rec_ab_frame);

    if ( use_loc_obj == 1){
      check( loc_ab_frame = xsh_localize_obj( rec_ab_frame, NULL,instrument, 
        &loc_par, NULL, "LOC_AB.fits"));
      check( loc_ba_frame = xsh_localize_obj( rec_ba_frame, NULL,instrument,  
        &loc_par, NULL, "LOC_BA.fits"));
    }
    else{
      check(  rec_bashift_frame = shift_with_kw( rec_ba_frame, instrument,
        &rectify_par, "REC_SHIFT_B_A.fits", &rec_bashifteso_frame, &ref_ra,
        &ref_dec, 1));

      xsh_msg("TEST REF RA DEC %f %f", *ref_ra, *ref_dec);

      cpl_frameset_insert( nod_set, rec_bashift_frame);
    }
  }

  zero_nod_set = cpl_frameset_new();

  /* create 0 frames */
  for ( i = 0 ; i < nb_frames ; i++ ) {
    cpl_frame *new_frame = NULL;

    check( sci_frame = cpl_frameset_get_frame( nod_set, i));
    check( new_frame = create_zero_rectify( sci_frame, inod, instrument));
    check( cpl_frameset_insert( zero_nod_set, new_frame));
  }

  cnod_par.nod_clip = FALSE;

  for ( i = 0 ; i < nb_frames ; i++ ) {
    cpl_frame *analyse_frame = NULL;
    cpl_frame *orig_frame = NULL;
    const char * orig_name = NULL;
    char tag[256];

    combine_set = cpl_frameset_duplicate( zero_nod_set);
    check( analyse_frame = cpl_frameset_get_frame( combine_set, i));
    check( orig_frame = cpl_frameset_get_frame( nod_set, i));
    check( orig_name = cpl_frame_get_filename( orig_frame));
    check(  cpl_frame_set_filename( analyse_frame, orig_name));

    sprintf( tag, "COMBINE_NOD%d", i);
    check( combine_nod_frame = xsh_combine_nod( combine_set, &cnod_par, 
      tag, instrument, &combine_nodeso_frame,0));
    check( analyse_rectify( combine_nod_frame, inod, instrument));
  
    xsh_free_frameset( &combine_set);
    xsh_free_frame( &combine_nod_frame);
    xsh_free_frame( &combine_nodeso_frame);
  }

  check( combine_nod_frame = xsh_combine_nod( nod_set, &cnod_par,
      "FULL_COMBINE_NOD", instrument,&combine_nodeso_frame,0));
    check( analyse_rectify( combine_nod_frame, inod, instrument));

  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_frameset( &raw_set);
    xsh_free_frameset( &set);
    xsh_instrument_free( &instrument);
    xsh_free_frameset( &nod_set);
    xsh_free_frameset( &zero_nod_set);
    xsh_free_frameset( &combine_set);
    xsh_free_frame( &rec_abeso_frame);
    xsh_free_frame( &rec_abtab_frame);
    xsh_free_frame( &rec_ba_frame);
    xsh_free_frame( &rec_bashifteso_frame);
    xsh_free_frame( &loc_ab_frame);
    xsh_free_frame( &loc_ba_frame);
    xsh_free_frame( &combine_nod_frame);
    xsh_free_frame( &combine_nodeso_frame);
    XSH_FREE( ref_ra);
    XSH_FREE( ref_dec);
    TEST_END();
    return ret;
}
/**@}*/


