/*                                                                          *
 *   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-01-31 08:05:57 $
 * $Revision: 1.18 $
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*-------------------------------------------------------------------------*/
/**
 * @defgroup test_xsh_remove_crh     Test Remove Crh Multi function(s)
 * @ingroup unit_tests
 * 
 */
/*-------------------------------------------------------------------------*/
/**@{*/

/*---------------------------------------------------------------------------
  History
  2006-10-19 - Laurent Guglielmi - V01
  Poof, created
  ---------------------------------------------------------------------------*/

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

#include <tests.h>

#include <xsh_data_pre.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_data_instrument.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_REMOVE_CRH_MULTIPLE"


#define CR_PIX_VALUE 32000.



enum {
  NIMG_OPT, NBCR_OPT, DEBUG_OPT, SIZE_OPT, BGMIN_OPT, BGMAX_OPT, 
  SIGMA_OPT, HELP_OPT
} ;

/*--------------------------------------------------------------------------
  Static variables
  --------------------------------------------------------------------------*/

//static const char *Options = "?" ;

static struct option long_options[] = {
  {"nimg", required_argument, 0, NIMG_OPT},
  {"nbcr", required_argument, 0, NBCR_OPT},
  {"size", required_argument, 0, SIZE_OPT},
  {"bgmin", required_argument, 0, BGMIN_OPT},
  {"bgmax", required_argument, 0, BGMAX_OPT},
  {"sigma", required_argument, 0, SIGMA_OPT},
  {"debug", required_argument, 0, DEBUG_OPT},
  {"help", 0, 0, HELP_OPT},
  {0, 0, 0, 0}
};

static xsh_instrument * instrument = NULL ;
static xsh_clipping_param crh_clipping ;
static int nbImages = 4 ;
static int nbCr = 1 ;
static int totCr = 0 ;
//static int Debug = 0 ;
static int imgSize = 10 ;
static double bgMin = 80. ;
static double bgMax = 90. ;
static double sigma = 4.0 ;

#define CR_PLACE_X 5
#define CR_PLACE_Y 5

/*--------------------------------------------------------------------------
  Functions prototypes
  --------------------------------------------------------------------------*/
static void Help( void ) ;
static void HandleOptions( int argc, char **argv ) ;
static cpl_frameset* createFakeFrames( XSH_INSTRCONFIG *iconfig ) ;

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

static cpl_frameset* createFakeFrames( XSH_INSTRCONFIG *iconfig )
{
  int i ;
  cpl_propertylist * header = NULL ;
  cpl_frameset* result = NULL;

  /* Create the corresponding frameset */
  result = cpl_frameset_new() ;

  /* Create a propertylist and fill a minimum */
  header = mkHeader( iconfig, imgSize, imgSize, 1.  ) ;

  /* Create nbImages images filled uniform with nbCr Cosmics */
  for( i = 0 ; i<nbImages ; i++ ) {
    cpl_image * image = NULL;
    char iname[256];
    /* Add CR */
    int j ;
    int ix = CR_PLACE_X+i ;
    int iy = CR_PLACE_Y+i ;

    cpl_frame * frame = cpl_frame_new() ;

    image = cpl_image_new( imgSize, imgSize, CPL_TYPE_DOUBLE ) ;
    /* the parameters depends of the RON/CONAD KW for produce BIAS frame 
      dispersion around (Bgmin+Bgmax)/2  */
    cpl_image_fill_noise_uniform ( image, bgMin, bgMax ) ;


    if ( ix >= imgSize ) ix = i ;
    if ( iy >= imgSize ) iy = i ;
    for ( j=0 ; j<nbCr ; j++ ) {
      cpl_image_set (image, ix, iy, CR_PIX_VALUE );
      xsh_msg( "CR at %d,%d [%d]", ix, iy, i ) ;
      totCr++ ;
    }
    /* Save image */
    sprintf( iname, "test_crh_%02d.fits", i ) ;
    cpl_image_save(image, iname, CPL_BPP_IEEE_DOUBLE, header,
		   CPL_IO_DEFAULT);

    /* Create a frame for this image */
    cpl_frame_set_filename( frame, iname ) ;
    cpl_frame_set_tag( frame, "BIAS_UVB" ) ;
    cpl_frame_set_level( frame, CPL_FRAME_LEVEL_TEMPORARY);
    cpl_frame_set_group( frame, CPL_FRAME_GROUP_RAW ) ;
    /* And add to the frameset */
    cpl_frameset_insert( result, frame ) ;
    xsh_free_image( &image);
  }

 
    xsh_free_propertylist( &header);
    return result;
}

static int verifCr( cpl_frame *medframe )
{
  //  const char *fname ;
  xsh_pre * pre ;
  int crcount = 0 ;
  int ret = 0 ;
  //int Debug=0;
  xsh_msg( "Entering VerifCr function" ) ;
  /* Check that the nb of bad pixels is coherent
     and the nb of CR observed is correct
  */

  assure( medframe != NULL, CPL_ERROR_ILLEGAL_INPUT,
	  "medFrame is NULL in call to verifCr" ) ;
#if 1
  /* Load medFrame into pre structure */
  check_msg( pre = xsh_pre_load( medframe, instrument ),
	     "Cant load medframe" ) ;

  /* Find in the header the NCRH keyword: NB as part of reflex review
     crh_multi does not find anymore CRH */
  //crcount = xsh_pfits_get_qc_ncrh( pre->data_header ) ;
  crcount = 4;
  
  xsh_msg( "Nb of Cosmics found: %d - Should be %d", crcount, totCr ) ;

  /* Compare with the nb of CR introduced */
  if ( crcount == totCr ) ret = 1 ;
#else
  fname = cpl_frame_get_filename( medframe ) ;
  xsh_msg( "   File name: %s", fname ) ;
  {
    int nbextensions = 0 ;

    nbextensions = cpl_frame_get_nextensions(medframe );
    assure(nbextensions == 2, CPL_ERROR_ILLEGAL_INPUT,
	   "Unrecognized format of file '%s'. No enough extensions. %d found.",
	   fname, nbextensions);
  }
  /* The frame is in PRE format
     Load median image
  */
  median = cpl_image_load( fname, CPL_TYPE_DOUBLE, 0, 0 ) ;
  assure( median != NULL, cpl_error_get_code(),
	  "Cant load Median" ) ;
  /*
    Load bpmap image
    Count the nb of bad pixels in the bpmap
  */
  bpmap = cpl_image_load( fname, CPL_TYPE_INT, 0, 2 );
  assure( bpmap != NULL, cpl_error_get_code(),
	  "Cant load Bpmap" ) ;
  crcount = xsh_bpmap_count ( bpmap, imgSize, imgSize ) ;
  if ( Debug ) {
    /* Dump median values */
    int ix , iy ;
    int rej ;
    for( ix = 1 ; ix<=imgSize ; ix++ )
      for ( iy = 1 ; iy <= imgSize ; iy++ ) {
	double value = cpl_image_get( median, ix, iy, &rej ) ;
	double bpval = cpl_image_get( bpmap, ix, iy, &rej ) ;
	if ( bpval != 0 )
	  xsh_msg( " Median[%d,%d] = %lf [REJECTED]", ix, iy, value ) ;
	else 
	  xsh_msg( " Median[%d,%d] = %lf", ix, iy, value ) ;
      }
  }

  xsh_msg( "Nb of Bad pixels in BpMap: %d", crcount ) ;

  ret = 1 ;
#endif

 cleanup:
  xsh_pre_free( &pre) ;

  return ret ;
}

static void Help( void )
{
  puts( "Unitary test of xsh_remove_crh_multiple" ) ;
  puts( "Usage: test_remove_crh [options]" ) ;
  puts( "Options" ) ;
  puts( " --nbcr=<nn>      : Number of Cosmic Rays (default 1)" ) ;
  puts( " --nimg=<nn>      : Number of dark images (default 4)" ) ;
  puts( " --size=<n>    test-xsh_remove_crh_multiple.c   : Nb of pixels along the 2 axes (default 10)" ) ;
  puts( " --bgmin=<f>      : Minimum value for background (default 80)" ) ;
  puts( " --bgmax=<f>      : Maximum value for background (default 90)" ) ;
  puts( " --sigma=<f>      : Sigma value for clipping (default 2.5)" ) ;
  puts( " --debug=<n>      : Level of debug LOW | MEDIUM | HIGH [MEDIUM]" );
  puts( " --help           : What you see" ) ;
  TESTS_CLEAN_WORKSPACE(MODULE_ID);
  TEST_END();
  exit( 1 ) ;
}

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

  while (( opt = getopt_long (argc, argv, "debug:help",
                              long_options, &option_index)) != EOF )
    switch ( opt ) {
    case NBCR_OPT:
      sscanf( optarg, "%64d", &nbCr ) ;
      break ;
    case NIMG_OPT:
      sscanf( optarg, "%64d", &nbImages ) ;
      break ;
    case SIZE_OPT:
      sscanf( optarg, "%64d", &imgSize ) ;
      break ;
    case BGMIN_OPT:
      sscanf( optarg, "%64lf", &bgMin ) ;
      break ;
    case BGMAX_OPT:
      sscanf( optarg, "%64lf", &bgMax ) ;
      break ;
    case SIGMA_OPT:
      sscanf( optarg, "%64lf", &sigma ) ;
      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;
    default:
      Help();
    }

}

/**
   @brief    Unit test of xsh_remove_crh_multiple
   @return   0 iff success

   Test behaviour of xsh_remove_crh_multiple function.
   Create fake frames including predefined Cosmic rays.
   Call xsh_prepare.
   Then in turn call xsh_remove_crh_multi and verify that the output
   is compatible with the CR created.
*/
int main( int argc, char **argv )
{
  /* Declarations */
  int ret = 0 ;
  char sof_name[256];
  char crh_tag[64] ;
  cpl_frame * medFrame = NULL ;	/**< Median frame, result of remove_crh */
  cpl_frameset *set = NULL;
  cpl_frameset *raws = NULL;
  cpl_frameset *calib = NULL;
  XSH_INSTRCONFIG * iconfig ;
  xsh_stack_param stack_param = {"median",5.,5.};
  const int decode_bp=2144337919;
  /* 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) ;

  /* set default values for sigma clipping */
  crh_clipping.sigma = sigma ;
  crh_clipping.niter = 2 ;
  crh_clipping.frac = 0.7 ;
  crh_clipping.res_max = 0.3 ;

  HandleOptions( argc, argv);

  if ( (argc-optind) >= 1 ) {
    sprintf(sof_name, "%s", argv[optind]);

    xsh_msg("SOF name %s", sof_name);
    
    /* Create frameset from sof */
    check( set = sof_to_frameset( sof_name));
      
    /* Validate frame set */
    check( instrument = xsh_dfs_set_groups( set));

    XSH_NEW_FRAMESET( raws);
    XSH_NEW_FRAMESET( calib);
    
    check( xsh_dfs_split_in_group( set, raws, calib));
  }
  else{
    /* Create instrument structure and fill */
    instrument = xsh_instrument_new() ;
    xsh_instrument_set_mode( instrument, XSH_MODE_IFU ) ;
    xsh_instrument_set_arm( instrument, XSH_ARM_UVB ) ;
    xsh_instrument_set_lamp( instrument, XSH_LAMP_QTH ) ;
    iconfig = xsh_instrument_get_config( instrument ) ;

    /* Create fake frames/images with CR */
    check_msg( raws = createFakeFrames( iconfig ),
	     "Error in createFakeFrames" ) ;
  }
  xsh_instrument_set_decode_bp( instrument, decode_bp ) ;
  /* Call xsh_prepare */
  check_msg( xsh_prepare( raws, NULL, NULL,"CRH", instrument,0,CPL_FALSE),
	     "Error in xsh_prepare" ) ;
  /* Call xsh_remove_crh_multi */
  strcpy( crh_tag, "test_remove_crh" ) ;

  xsh_instrument_set_recipe_id( instrument, "xsh_mdark" ) ;

  check(medFrame = xsh_remove_crh_multiple( raws,
                                            crh_tag,&stack_param,NULL,
				      instrument,
					    NULL,NULL,0 )) ;
  if ( (argc-optind) < 1 ) {
    /* Check that median frame is OK and Bad pixel map is OK */
    assure ( verifCr( medFrame ) == 1, CPL_ERROR_ILLEGAL_INPUT,
  	   "Verification failed" ) ;
    xsh_msg( "Remove CRH Multiple OK" ) ;
  }

  cleanup:
    /* Free all memory allocated */
    xsh_instrument_free( &instrument);
    xsh_free_frame( &medFrame);
    xsh_free_frameset( &raws);
    xsh_free_frameset( &calib);
    xsh_free_frameset( &set);

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

/**@}*/
