/*                                                                          *
 *   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-12-08 18:36:38 $
 * $Revision: 1.18 $
 */

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

/*-------------------------------------------------------------------------*/
/**
 * @defgroup test_xsh_extract_clean  Test Object extraction with bad pixels interpolation
 * @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_badpixelmap.h>
#include <xsh_parameters.h>

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

#include <getopt.h>

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

#define MODULE_ID "XSH_EXTRACT_CLEAN_SYM"

enum {
  DECODEBP_OPT,METHOD_OPT,DEBUG_OPT,HELP_OPT
} ;

static struct option long_options[] = {
  {"decode-bp", required_argument, 0, DECODEBP_OPT},
  {"method", required_argument, 0, METHOD_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_extract_clean");
  puts( "Usage: test_xsh_extract_clean [options] <input_files>");

  puts( "Options" ) ;
  puts( " --decode-bp=<n>    : Integer representation of the bits to be considered bad when decoding the bad pixel mask pixel values."); 
  puts( " --method=<n>       : method for extraction CLEAN | LOCALIZATION | FULL | NOD"); 
  puts( " --debug=<n>        : Level of debug LOW | MEDIUM | HIGH [MEDIUM]" );
  puts( " --help             : What you see" ) ;
  puts( "\nInput Files" ) ;
  puts( "The input files argument MUST be in this order:" ) ;
  puts( " 1. Rectified frame 2D" ) ;
  puts( " 2. Localization table");
  TESTS_CLEAN_WORKSPACE(MODULE_ID);
  TEST_END();
  exit(0);
}

static void HandleOptions( int argc, char **argv,
			   xsh_extract_param *extract_par,int* decode_bp)
{
  int opt ;
  int option_index = 0;

  while (( opt = getopt_long (argc, argv, "slit_position:slit_height:method",
    long_options, &option_index)) != EOF ){

    switch ( opt ) {
    case  METHOD_OPT:
      if ( strcmp(optarg, EXTRACT_METHOD_PRINT( LOCALIZATION_METHOD)) == 0){
        extract_par->method = LOCALIZATION_METHOD;
      }
      else if ( strcmp(optarg, EXTRACT_METHOD_PRINT( FULL_METHOD)) == 0){
        extract_par->method = FULL_METHOD;
      }
      else if ( strcmp(optarg, EXTRACT_METHOD_PRINT( CLEAN_METHOD)) == 0){
        extract_par->method = CLEAN_METHOD;
      }
      else if ( strcmp(optarg, EXTRACT_METHOD_PRINT( NOD_METHOD)) == 0){
        extract_par->method = NOD_METHOD;
      }
      else{
        xsh_msg("WRONG method %s", optarg);
        exit(-1);
      }
      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);
      }
      break;
    case DECODEBP_OPT:
      *decode_bp=atoi(optarg);
      break;

    case HELP_OPT:
      Help();
      break;
    default:
      Help();
      break;
    }
  }
  return;
}

cpl_error_code xsh_set_wcs(cpl_propertylist* header, const double crpix1,
    const double crval1, const double cdelt1, const double crpix2,
    const double crval2, const double cdelt2) {
  cpl_propertylist_append_double(header, "CRPIX1", crpix1);
  cpl_propertylist_append_double(header, "CRVAL1", crval1);
  cpl_propertylist_append_double(header, "CDELT1", cdelt1);
  cpl_propertylist_append_string(header, "CTYPE1", "LINEAR");

  cpl_propertylist_append_double(header, "CRPIX2", crpix2);
  cpl_propertylist_append_double(header, "CRVAL2", crval2);
  cpl_propertylist_append_double(header, "CDELT2", cdelt2);
  cpl_propertylist_append_string(header, "CTYPE2", "LINEAR");

  xsh_pfits_set_cd11(header, cdelt1);
  xsh_pfits_set_cd12(header, 0);
  xsh_pfits_set_cd21(header, 0);
  xsh_pfits_set_cd22(header, cdelt2);

  return cpl_error_get_code();
}
/*--------------------------------------------------------------------------
  Implementation
  --------------------------------------------------------------------------*/

static cpl_frame*
xsh_extract_local_clean(cpl_frame* frame2D,xsh_instrument* instrument)
{
  cpl_frame* extracted=NULL;
  //float* prof = 0;
  //int nbad=0;
  //int ngood=0;
  //int islit=0;
  int nslit=0;
  //int idx=0;
  int nlambda=0;
  int ilambda=0;
  int mask_hsize=3;
  int mask_size=mask_hsize*2+1;
  //int m=0;
  //int mdx=0;

  int* qual=NULL;
  float* flux=NULL;
  float* errs=NULL;

  //int* sub_qual=NULL;
  //float* sub_flux=NULL;
  //float* sub_errs=NULL;

  int* res_qual=NULL;
  double* res_flux=NULL;
  double* res_errs=NULL;
  double* res_lambda=NULL;

  double* lambda=NULL;

  double fluxval=0;
  double errval=0;


  int qualval=0;
  //float num_fct=0;
  //float den_fct=0;
  //float num_add=0;
  //float den_add=0;

  //double ipol_fct=0;
  const char* fname=NULL;
  cpl_image* ima_data=NULL;
  cpl_image* ima_errs=NULL;
  cpl_image* ima_qual=NULL;

  //cpl_image* sub_ima_data=NULL;
  //cpl_image* sub_ima_errs=NULL;
  //cpl_image* sub_ima_qual=NULL;
  cpl_propertylist* header=NULL;
  //cpl_propertylist* plist=NULL;
  //double crpix1 = 1;
  double crval1 = 533.68;
  double cdelt1 = 0.04;
  //double crpix2 = 1;

  const char* res_name="extracted_signal.fits";

  cpl_table* res_table=NULL;

  int slit_border=6;
  int hslit=8;
  int slit_min=slit_border;
  int slit_max=slit_min+2*hslit;

  //int nbad_sub=0;
  //int mslit=0;

  fname=cpl_frame_get_filename(frame2D);
  ima_data=cpl_image_load(fname,CPL_TYPE_FLOAT,0,0);
  ima_errs=cpl_image_load(fname,CPL_TYPE_FLOAT,0,1);
  ima_qual=cpl_image_load(fname,CPL_TYPE_INT,0,2);
  header=cpl_propertylist_load(fname,0);

  nlambda=xsh_pfits_get_naxis1(header);
  nslit=xsh_pfits_get_naxis2(header);

  crval1=xsh_pfits_get_crval1(header);
  cdelt1=xsh_pfits_get_cdelt1(header);

 

  flux=cpl_image_get_data_float(ima_data);
  errs=cpl_image_get_data_float(ima_errs);
  check(qual=cpl_image_get_data_int(ima_qual));

  res_table=cpl_table_new(nlambda);
  cpl_table_new_column(res_table,"wave",CPL_TYPE_DOUBLE);
  cpl_table_new_column(res_table,"flux",CPL_TYPE_DOUBLE);
  cpl_table_new_column(res_table,"errs",CPL_TYPE_DOUBLE);
  cpl_table_new_column(res_table,"qual",CPL_TYPE_INT);

  cpl_table_fill_column_window_double(res_table,"wave",0,nlambda,0.);
  cpl_table_fill_column_window_double(res_table,"flux",0,nlambda,0.);
  cpl_table_fill_column_window_double(res_table,"errs",0,nlambda,0.);
  cpl_table_fill_column_window_int(res_table,"qual",0,nlambda,0);

  check(res_lambda=cpl_table_get_data_double(res_table,"wave"));
  res_flux=cpl_table_get_data_double(res_table,"flux");
  res_errs=cpl_table_get_data_double(res_table,"errs");
  res_qual=cpl_table_get_data_int(res_table,"qual");

  lambda = cpl_malloc(nlambda * sizeof(double));


  slit_min = nslit / 2 - hslit;
  slit_max = nslit / 2 + hslit;

 
  xsh_msg_debug("mask size=%d", mask_size);
  float* pima;
  float* fima;
  float* rima;
  for (ilambda = 0; ilambda < nlambda; ilambda++) {
    fluxval = 0;
    errval = 0;
    qualval = 0;
    lambda[ilambda] = (float) (crval1 + cdelt1 * ilambda);

    xsh_extract_clean_slice(flux,errs,qual,lambda,ilambda,instrument,slit_min, slit_max,nlambda,nslit,mask_hsize,&fluxval, &errval,&qualval, pima, fima, rima);
    
    res_lambda[ilambda] = lambda[ilambda];
    res_flux[ilambda] = fluxval;
    res_errs[ilambda] = sqrt(errval);
    res_qual[ilambda] = qualval;


  }


  check(cpl_table_save(res_table,NULL,NULL,res_name,CPL_IO_DEFAULT));

  extracted = cpl_frame_new();
  cpl_frame_set_filename(extracted, res_name);
  cpl_frame_set_level(extracted, CPL_FRAME_LEVEL_TEMPORARY);
  cpl_frame_set_group(extracted, CPL_FRAME_GROUP_RAW ) ;
  cpl_frame_set_tag(extracted,"TEST");

cleanup:

  XSH_FREE(lambda);
  xsh_free_propertylist(&header);
  xsh_free_image(&ima_data);
  xsh_free_image(&ima_errs);
  xsh_free_image(&ima_qual);
  xsh_free_table(&res_table);

  return extracted;
}

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

*/
int main( int argc, char **argv)
{
  /* Declarations */
  int ret = 0 ;
  xsh_instrument* instrument = NULL;
  cpl_propertylist *header= NULL; 
  const char *tag = "TELL_SLIT_ORDER2D_VIS";
  cpl_frame* result = NULL;
  //cpl_frame* result_eso = NULL;

  char* rec_name = "sym_extract2d.fits";
  cpl_frame* rec_frame = NULL;

  int decode_bp=DECODE_BP_FLAG_DEF;
  int sx=300;
  int sy=68;
  //int sy=20;
  const double crpix1=1.;
  const double crval1=533.68;
  const double cdelt1=0.04;
  xsh_extract_param extract_obj = { CLEAN_METHOD};
  const double crpix2=1.;
  const double crval2=-5.3;
  const double cdelt2=0.16;
  cpl_image* data=NULL;
  cpl_image* errs=NULL;
  cpl_image* qual=NULL;
  float* pdata=NULL;
  int* pqual=NULL;
  XSH_ARM arm = XSH_ARM_VIS;
  int i=0;
  int j=0;
  int k=0;
  int sc=sy/2;
  float* val=NULL;
  const int rad=3;
  const int nval=2*rad+1;
  const float crh=10000.;

  /* Initialize libraries */
  TESTS_INIT_WORKSPACE(MODULE_ID);
  TESTS_INIT(MODULE_ID);

  cpl_msg_set_level(CPL_MSG_DEBUG);
  xsh_debug_level_set(XSH_DEBUG_LEVEL_MEDIUM) ;


  /* Analyse parameters */
  HandleOptions( argc, argv, &extract_obj,&decode_bp);
  xsh_msg("argc=%d optind=%d",argc,optind);
  /*
  if ( (argc - optind) > 0 ) {
    rec_name = argv[optind];
    if ( (argc - optind) > 1){
      loc_name = argv[optind+1];
    }
  }
  else{
    Help();
  }
  */

  /* generate image */
  data=cpl_image_new(sx,sy,CPL_TYPE_FLOAT);
  //cpl_image_add_scalar(data,1);
  qual=cpl_image_new(sx,sy,CPL_TYPE_INT);
  pdata=cpl_image_get_data_float(data);
  pqual=cpl_image_get_data_int(qual);

  /* fill a central trace object ad hoc (with values that are squares to get proper errors: sum=83 */
  val = cpl_malloc(nval * sizeof(float));
  val[0]=4.;
  val[1]=9.;
  val[2]=16.;
  val[3]=25.;
  val[4]=16.;
  val[5]=9.;
  val[6]=4.;

  for (k = -rad; k <= rad; k++) {
    j = sc - k;
    for (i = 0; i < sx; i++) {
      pdata[j * sx + i] = val[k + rad];
    }
  }



  /* add a CRH  */
  // isolated

  j=sc;
  i=12;
  //cpl_image_set(data,i,j,crh);
  //cpl_image_set(qual,i,j,crh);
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;
  xsh_msg_debug("ingest CRH at pix[%d,%d]=pix[%g,%g]",i,j,crval1+i*cdelt1,crval2+j*cdelt2);


  
  // double on Y
  j=sc-1;
  i=52;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;

  j=sc+1;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;

  // double on X
  j=sc;
  i=102;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;
  i=103;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;


  // longer than extraction slit
  j=sc;
  for (i = 106; i <= 116; i++) {
     pdata[j * sx + i]=crh;
     pqual[j * sx + i]=1;
  }

  // L shaped
  j=sc+2;
  i=152;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;

  j=sc+1;
  i=152;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;

  j=sc;
  i=152;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;
  j=sc-1;

  i=152;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;

  j=sc-1;
  i=153;
  pdata[j * sx + i]=crh;
  pqual[j * sx + i]=1;


  // filling all the object: does not work?
  for (k = -rad; k <= rad; k++) {
    j = sc - k;
    i=202;
    pdata[j * sx + i] = crh;
    pqual[j * sx + i]=1;
  }


  // filling all the extraction slit: strange mask
  for (j= 0; j < sy; j++) {
    i=252;
    pdata[j * sx + i] = crh;
    pqual[j * sx + i]=1;
  }
  
 

  //cpl_image_add_scalar(data,100.);
  errs=cpl_image_duplicate(data);
  cpl_image_power(errs,0.5);
  cpl_image_add_scalar(errs,1);

  header=cpl_propertylist_new();
  xsh_set_wcs(header, crpix1,crval1, cdelt1, crpix2,crval2, cdelt2);
  cpl_image_save(data, rec_name, XSH_PRE_DATA_BPP, header, CPL_IO_DEFAULT);
  cpl_image_save(errs, rec_name, XSH_PRE_ERRS_BPP, NULL, CPL_IO_EXTEND);
  cpl_image_save(qual, rec_name, XSH_PRE_QUAL_BPP, NULL, CPL_IO_EXTEND);

  rec_frame = cpl_frame_new();
  XSH_ASSURE_NOT_NULL (rec_frame);
  cpl_frame_set_filename( rec_frame, rec_name) ;

  cpl_frame_set_level( rec_frame, CPL_FRAME_LEVEL_TEMPORARY);
  cpl_frame_set_group( rec_frame, CPL_FRAME_GROUP_RAW ) ;
  cpl_frame_set_tag( rec_frame, tag);

  /* Create instrument structure and fill */
  instrument = xsh_instrument_new();
  xsh_instrument_set_mode( instrument, XSH_MODE_IFU ) ;
  xsh_instrument_set_lamp( instrument, XSH_LAMP_QTH ) ;
  xsh_instrument_set_arm( instrument, arm);
  xsh_instrument_set_decode_bp(instrument,decode_bp);
  //xsh_msg("decode_bp=%d",decode_bp);


  check(result = xsh_extract_local_clean(rec_frame,instrument));

  cleanup:

    XSH_FREE(val);
    xsh_free_image( &data);
    xsh_free_image( &errs);
    xsh_free_image( &qual);

    xsh_instrument_free( &instrument);
    xsh_free_frame( &rec_frame);
    xsh_free_frame( &result);
    xsh_free_propertylist( &header);

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_error_dump(CPL_MSG_ERROR);
      ret= 1;
    }
    TESTS_CLEAN_WORKSPACE(MODULE_ID);
    TEST_END();
    return ret ;
}

/**@}*/
