/*                                                                          *
 *   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: 2011-12-02 14:13:14 $
 * $Revision: 1.9 $
 */

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

/*-------------------------------------------------------------------------*/
/**
  @defgroup test_xsh_spectrum_merge_3d  Test a cube
  @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_data_pre_3d.h>
#include <xsh_badpixelmap.h>

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

#include <getopt.h>

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

#define MODULE_ID "XSH_DATA_SPECTRUM_MERGE_3D"

static const char * Options = "" ;

enum {
  ZCHUNK_HSIZE_OPT
};

static struct option long_options[] = {
  {"lambda-chunk-hsize", required_argument, 0, ZCHUNK_HSIZE_OPT},
  {0, 0, 0, 0}
};

static void Help( void )
{
  puts( "Creates for each slitlet:");
  puts( "   - a median profile along the slit");
  puts( "   - a localization as a function of wavelength");
  
  puts( "Usage: test_xsh_data_spectrum_merge_3d [options] CUBE SKY_LINE");

  puts( "Options" ) ;
  puts( "  --lambda-chunk-hsize=<n> : Chunk half size in pixel [50]");
  
  puts( "\nInput Files" ) ;
  puts( "CUBE                : data cube (tag = *_IFU_MERGE3D_IFU_UVB)" );
  puts( "SKY_LINE [OPTIONAL] : sky lines (tag = SKY_LINE_LIST_arm)" );
  
  puts( "\nOutput Files");
  puts( "1) MEDIAN_CUBE.fits  : median profile in fits image");
  puts( "2) median_profile_slitbin_*.dat : ASCII profile along the slit direction for every slitlet by collapsing in wavelength");
  puts( "3) profile_lambda_*.dat : ASCII file containing for each chunk around lref gaussian central fitting positions");
  puts( "4) gsl_locfit_*.dat : ASCII central slit position for each slitlet");
  TEST_END();
}


static void HandleOptions( int argc, char **argv, 
  int *zchunk_hsize)
{
  int opt ;
  int option_index = 0;
      
  while (( opt = getopt_long (argc, argv, Options,
                              long_options, &option_index)) != EOF )
    switch ( opt ) {
    case ZCHUNK_HSIZE_OPT:
      *zchunk_hsize = atoi( optarg);                   
    break;
    default: Help() ; exit( 0);
  }
}
/*--------------------------------------------------------------------------
  Implementation
  --------------------------------------------------------------------------*/

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

*/
int main( int argc, char **argv)
{
  int ret;
  /* Declarations */
  const char* sci_name = NULL;
  cpl_frame *sci_frame = NULL;
  
  const char* sky_name = NULL;
  cpl_frame *sky_frame = NULL;  
  
  xsh_pre_3d *cube = NULL;
  xsh_image_3d *cube_data_img = NULL;
  int cube_x, cube_y, cube_z;
  float *cube_data = NULL;
  double *median = NULL;
  int median_size =0;
  double *coadd =NULL;
  int zchunk_hsize = 100;
  cpl_vector *med_vect = NULL;
  cpl_vector *coadd_vect = NULL;
  cpl_vector *positions = NULL;
  int x,y,z;
  double med_z;  
  cpl_image *med_img = NULL;
  double *med_img_data = NULL;
    FILE *profil_file = NULL;
    char filename[256];
  int zmed;
  cpl_propertylist *header = NULL;
  double crval1, crval2, crval3;
  double crpix1, crpix2, crpix3;
  double cdelt1, cdelt2, cdelt3;
  double x0= 0.0,sigma =0.0, area=0.0, offset=0.0;
  int bad_fit;
  xsh_instrument *instrument = NULL;

  /* Initialize libraries */
  TESTS_INIT( MODULE_ID);
  cpl_msg_set_level( CPL_MSG_DEBUG);
  xsh_debug_level_set( XSH_DEBUG_LEVEL_MEDIUM);

  HandleOptions( argc, argv, &zchunk_hsize);

  /* Analyse parameters */
  if (argc-optind >= 1 ) {
    sci_name = argv[optind];
    if (argc-optind >= 2 ) {
      sky_name = argv[optind+1];
    }
  }
  else{
    Help();
    exit(0);
  }

  TESTS_XSH_FRAME_CREATE( sci_frame, "CUBE_arm", sci_name);
  if ( sky_name != NULL){
    TESTS_XSH_FRAME_CREATE( sky_frame, "SKY_LINE_LIST_arm", sky_name);
  }

  /* USE load function */
  xsh_msg("CUBE            : %s",
    cpl_frame_get_filename( sci_frame));
  if ( sky_frame != NULL){
     xsh_msg("SKY LINE LIST         : %s",
    cpl_frame_get_filename( sky_frame));
  }
  else{
    xsh_msg("Don't use sky line list!!");
  }
  
  check( instrument = create_instrument( sci_name));
  xsh_instrument_set_mode( instrument, XSH_MODE_IFU);
  
  check( cube = xsh_pre_3d_load( sci_frame));
  header = cube->data_header;

  check( cube_data_img = xsh_pre_3d_get_data( cube));
  check( cube_x = xsh_image_3d_get_size_x( cube_data_img));
  check( cube_y = xsh_image_3d_get_size_y( cube_data_img));
  check( cube_z = xsh_image_3d_get_size_z( cube_data_img));

  xsh_msg(" Cube SLITLET*SLIT*LAMBDAS (%dx%dx%d)", cube_x, cube_y, cube_z);
  
  check( crpix1 = xsh_pfits_get_crpix1( header));
  check( crval1 = xsh_pfits_get_crval1( header));
  check( cdelt1 = xsh_pfits_get_cdelt1( header));

  check( crpix2 = xsh_pfits_get_crpix2( header));
  check( crval2 = xsh_pfits_get_crval2( header));
  check( cdelt2 = xsh_pfits_get_cdelt2( header));

  check( crpix3 = xsh_pfits_get_crpix3( header));
  check( crval3 = xsh_pfits_get_crval3( header));
  check( cdelt3 = xsh_pfits_get_cdelt3( header));
  
  xsh_msg(" SLITLET pix %f ref %f with step %f", crpix1, crval1, cdelt1);
  xsh_msg(" SLIT    pix %f ref %f with step %f", crpix2, crval2, cdelt2);
  xsh_msg(" LAMBDAS pix %f ref %f with step %f", crpix3, crval3, cdelt3); 

  check( cube_data = (float*)xsh_image_3d_get_data( cube_data_img));
  
  /* test1 */
  XSH_MALLOC( median, double, cube_z);
  check( med_vect = cpl_vector_wrap( cube_z, median));
  check( med_img = cpl_image_new( cube_x, cube_y, CPL_TYPE_DOUBLE));
  check( med_img_data = cpl_image_get_data_double( med_img));
 
  for( y=0; y<cube_y; y++){
    for( x=0; x<cube_x; x++){
      for(z=0; z< cube_z; z++){
        median[z] = cube_data[cube_x*cube_y*z+cube_x*y+x];
      }
      check (med_z = cpl_vector_get_median( med_vect));
      med_img_data[y*cube_x+x] = med_z;
    }
  }
  check( cpl_image_save( med_img, "MEDIAN_CUBE.fits", CPL_BPP_IEEE_DOUBLE, 
    NULL, CPL_IO_CREATE));

  sprintf( filename, "median_profile_slitbin_%.3f.dat", cdelt2);
  profil_file = fopen( filename, "w");
  fprintf( profil_file, "#slit UP CEN LO\n");
 
  for( y=0; y<cube_y; y++){
    fprintf( profil_file, "%f ", crval2+y*cdelt2);
    for( x=0; x<cube_x; x++){
      fprintf( profil_file, "%f ", med_img_data[y*cube_x+x]);
    }
    fprintf( profil_file, "\n");
  }
  fclose( profil_file);

  XSH_MALLOC( coadd, double, cube_y);
  check( coadd_vect = cpl_vector_wrap( cube_y, coadd));
  check( positions = cpl_vector_new( cube_y));

  for( y=0; y<cube_y; y++){
    cpl_vector_set( positions, y, y);
    cpl_vector_set( coadd_vect, y, med_img_data[y*cube_x+1]);
  }
  cpl_vector_fit_gaussian( positions, NULL, coadd_vect, NULL,CPL_FIT_ALL,&x0,&sigma,&area,&offset,NULL,NULL,NULL);

  if (cpl_error_get_code() != CPL_ERROR_NONE){
    xsh_error_reset();
  }
  else{
    sprintf( filename, "gaussian_fit.dat");
    profil_file = fopen( filename, "w");
    fprintf( profil_file, "#slit cen gauss_cen\n");
    
    for( y=0; y<cube_y; y++){
      double sval = 0.0;
      double cen_val = 0.0;
      double gauss_val = 0.0;
      double z;
      
      z = (y-x0)/(sigma*XSH_MATH_SQRT_2);
      gauss_val = area / sqrt(2 *M_PI*sigma*sigma)*exp(-(z*z))+offset;
      sval = crval2+y*cdelt2;
      cen_val = cpl_vector_get( coadd_vect, y);
      
      fprintf( profil_file, "%f %f %f\n", sval, cen_val, gauss_val);
    }
    
    fclose( profil_file);
    xsh_msg(" Median cube central slitlet gaussian fit %f arcsec\n", crval2+x0*cdelt2);
  }
  
  /* test 2 */
  sprintf( filename, "profile_lambda_%d.dat", zchunk_hsize);
  
  profil_file = fopen( filename, "w");

  median_size = 0;
  bad_fit = 0;
  xsh_unwrap_vector( &med_vect);
  
  fprintf( profil_file, "#lref up cen low\n");
  for(z=zchunk_hsize; z< cube_z; z+=zchunk_hsize*2){
    double cen[3];

    for( x=0; x<cube_x; x++){
      
      for( y=0; y<cube_y; y++){
        coadd[y]=0;
      }
      for(zmed=z-zchunk_hsize; zmed< z+zchunk_hsize; zmed++){
        for( y=0; y<cube_y; y++){        
          coadd[y] += cube_data[cube_x*cube_y*zmed+cube_x*y+x];
        }
      }
      cpl_vector_fit_gaussian( positions, NULL, coadd_vect, NULL,CPL_FIT_ALL,&x0,&sigma,&area,&offset,NULL,NULL,NULL);
      if (cpl_error_get_code() != CPL_ERROR_NONE){
        xsh_error_reset();
        cen[x]=-1;
	if (x == 1){
	  bad_fit++;
	}
      }
      else{
        cen[x] = crval2+x0*cdelt2;
	if (x == 1){
	  median[median_size] = cen[1];
	  median_size++;
	}
      } 
    }
    fprintf( profil_file, "%f %f %f %f\n", crval3+z*cdelt3, cen[0], cen[1], cen[2]);
  }
  fclose( profil_file);
  check( med_vect = cpl_vector_wrap( median_size, median));
  med_z = cpl_vector_get_median( med_vect);
  xsh_msg(" Statistics of gaussian fit bad %d lines, good %d lines",
    bad_fit, median_size);
  xsh_msg(" Median from gaussian fit %f arcsec\n", med_z);
  
  /* GSL loc fit */
  for( x=0; x< cube_x; x++){ 
    double init_par[6];
    double fit_errs[6];
    int status;
    float slitcen, fit_err;
    
    sprintf( filename, "gsl_localize_pos%d.dat", x);
    profil_file = fopen( filename, "w");
    fprintf( profil_file, "#wavelength slit_fit fit_err\n");
   
    for( z=0; z < cube_z; z++){   
      for( y=0; y < cube_y; y++){
        cpl_vector_set( coadd_vect, y, cube_data[cube_x*cube_y*z+cube_x*y+x]);
      }
      xsh_gsl_init_gaussian_fit( positions, coadd_vect, init_par);
      xsh_gsl_fit_gaussian( positions, coadd_vect, 0, init_par, fit_errs, &status);
      slitcen = crval2+init_par[4]*cdelt2;
      fit_err = fit_errs[4]*cdelt2;
      fprintf( profil_file, "%f %f %f\n", crval3+z*cdelt3, slitcen, fit_err);
    }
    fclose( profil_file); 
  }
  
  check( xsh_center_cube( sci_frame, sky_frame, zchunk_hsize*2,
    instrument));

  cleanup:
   if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_error_dump(CPL_MSG_ERROR);
      ret = 1;
    }
    xsh_instrument_free( &instrument);
    xsh_free_frame( &sci_frame);
    xsh_pre_3d_free( &cube);
    XSH_FREE( median);
    XSH_FREE( coadd);
    xsh_unwrap_vector( &coadd_vect);
    xsh_unwrap_vector( &med_vect);
    xsh_free_image( &med_img);
    TEST_END();
    return ret;
}

/**@}*/
