/*                                                                           a
 *   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-08-09 15:08:55 $
 * $Revision: 1.4 $
 * $Name: not supported by cvs2svn $
 */
/*----------------------------------------------------------------------------
  Includes
  ----------------------------------------------------------------------------*/
#include <math.h>
#include <string.h>
#include <xsh_error.h>
#include <xsh_utils_ifu.h>
#include <xsh_utils_image.h>
#include <xsh_utils_wrappers.h>
#include <xsh_utils_table.h>
#include <xsh_pfits.h>
#include <xsh_model_io.h>
#include <xsh_data_rec.h>
#include <xsh_data_spectrum.h>
#include <xsh_dfs.h>
#include <xsh_drl.h>
#include <xsh_blaze.h>

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_blaze Blaze handling
 * @ingroup drl_functions
 */

/*----------------------------------------------------------------------------*/
/**@{*/

/*---------------------------------------------------------------------------*/

/*****************************************************************************/
/** 
  @brief
    Divide a pre image by the blaze image 

  @param[in] pre_frame
    The PRE frame
  @param[in] blaze_frame
    The blaze image frame
  @param[in] instrument
    The instrument structure
  @return
    The normalized master flat frame
*/
/*****************************************************************************/
cpl_frame *xsh_divide_by_blaze( cpl_frame *pre_frame, cpl_frame *blaze_frame,
  xsh_instrument *instrument)
{
  xsh_pre *pre = NULL;
  const char* blaze_name = NULL;
  cpl_image *blaze_img = NULL;
  cpl_frame *result_frame = NULL;
  float *data1 = NULL;
  float *data2 = NULL;
  float *errs1 = NULL;
  int i;
  const char* tag = NULL;
  
  XSH_ASSURE_NOT_NULL( pre_frame);
  XSH_ASSURE_NOT_NULL( blaze_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  
  check( pre = xsh_pre_load( pre_frame, instrument));
  check( tag = cpl_frame_get_tag( pre_frame));
  check( blaze_name = cpl_frame_get_filename( blaze_frame));
  check( blaze_img = cpl_image_load( blaze_name, CPL_TYPE_FLOAT,0,0));

  check( data1 = cpl_image_get_data_float( pre->data));
  check( errs1 = cpl_image_get_data_float( pre->errs));
  check( data2 = cpl_image_get_data_float( blaze_img));
  
  for(i=0;i< pre->nx*pre->ny; i++){

    if ( data2[i] == 0){
      data1[i] = 0;
    }
    else {
      double d1 = 0.0, d2 = 0.0;
      double e1 = 0.0;
      d1 = data1[i];
      d2 = data2[i];
      e1 = errs1[i];
      data1[i] = (float)(d1/d2);
      errs1[i] = (float)(e1/d2);
    }
  }

  check( result_frame = xsh_pre_save ( pre, "DIV_BY_BLAZE.fits", tag, 1));
  check( cpl_frame_set_tag ( result_frame, tag));
  
  cleanup:
    xsh_free_image( &blaze_img);
    xsh_pre_free( &pre);
    
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &result_frame);
    }
    return result_frame;
}
/*****************************************************************************/

/*****************************************************************************/
/** 
  @brief
    Normalize a master flat frame order by order

  @param[in] masterflat_frame
    The master flat frame
  @param[in] order_frame
    The order list frame
  @param[in] instrument
    The instrument structure
  @return
    The normalized master flat frame
*/
/*****************************************************************************/

cpl_frame *xsh_blaze_image( cpl_frame* masterflat_frame,
   cpl_frame *order_frame, xsh_instrument *instrument)
{
  cpl_image *result_img = NULL;
  cpl_frame *result_frame = NULL;
  xsh_order_list *order_list = NULL;
  char blaze_name[256];
  char blaze_tag[16];
  
  XSH_ASSURE_NOT_NULL( masterflat_frame);
  XSH_ASSURE_NOT_NULL( order_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  
  
  sprintf( blaze_name, "%s", "BLAZE_IMAGE.fits");
  sprintf(blaze_tag, "%s", "BLAZE");
  
  check( order_list = xsh_order_list_load( order_frame, instrument));
  check( result_img = xsh_create_blaze( masterflat_frame, order_list, 
    instrument));
  
  check( cpl_image_save( result_img, blaze_name,
      CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT));
  
  check( result_frame = cpl_frame_new());
  check( cpl_frame_set_filename( result_frame, blaze_name));
  check( cpl_frame_set_tag( result_frame, blaze_tag));

  cleanup:
    xsh_free_image( &result_img);
    xsh_order_list_free( &order_list);
    
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &result_frame);
    }
    return result_frame;
}
/*****************************************************************************/


/*****************************************************************************/
/** 
  @brief
    Normalize a master flat frame order by order
  @param[in] masterflat_frame
    The master flat frame
  @param[in] order_list
    The order list given order position
  @param[in] instrument
    The instrument structure
  @return
    The normalized master flat frame
*/
/*****************************************************************************/
cpl_image* xsh_create_blaze( cpl_frame* masterflat_frame, 
  xsh_order_list* order_list, xsh_instrument *instrument)
{
  xsh_pre *masterflat_pre = NULL;
  float *masterflat_data = NULL;
  cpl_image *result = NULL;
  float *result_data = NULL;
  int i, iorder, y, nx, ny;
  cpl_vector *cen_flux_vect = NULL, *cen_flux_pos_vect = NULL;
  double *cen_flux_pos = NULL, *cen_flux = NULL;
  int deg_poly = 18;
  double mse =0.0;

  XSH_ASSURE_NOT_NULL( masterflat_frame);
  XSH_ASSURE_NOT_NULL( order_list);
  XSH_ASSURE_NOT_NULL( instrument);


  check( masterflat_pre = xsh_pre_load( masterflat_frame, instrument));
  XSH_CMP_INT( order_list->bin_x, ==, masterflat_pre->binx, "binning in x",);
  XSH_CMP_INT( order_list->bin_y, ==, masterflat_pre->biny, "binning in y",);

  check( nx = cpl_image_get_size_x( masterflat_pre->data));
  check( ny = cpl_image_get_size_y( masterflat_pre->data));
  check( masterflat_data = cpl_image_get_data_float( masterflat_pre->data));
  XSH_MALLOC( cen_flux, double, ny);
  XSH_MALLOC( cen_flux_pos, double, ny);

  check( result = cpl_image_new( nx,ny, CPL_TYPE_FLOAT));
  check( result_data = cpl_image_get_data_float( result));

  /* fit the central flux */
  for( iorder= 0; iorder < order_list->size; iorder++) {
    int x_cen;
    int start_y, end_y;
    int size_y=0;
    int absorder;

    check( start_y = xsh_order_list_get_starty( order_list, iorder));
    check( end_y = xsh_order_list_get_endy( order_list, iorder));
    absorder = order_list->list[iorder].absorder;

    for(y=start_y; y <= end_y; y++){
      check( x_cen = xsh_order_list_eval_int( order_list, 
        order_list->list[iorder].cenpoly, y));
      cen_flux[y-start_y] = masterflat_data[(x_cen-1)+(y-1)*nx];
      cen_flux_pos[y-start_y] = (double)y;
      size_y++;
    }

    check( cen_flux_vect = cpl_vector_wrap( size_y, cen_flux));
    check( cen_flux_pos_vect = cpl_vector_wrap( size_y, cen_flux_pos));

    check( order_list->list[iorder].blazepoly = 
      xsh_polynomial_fit_1d_create( cen_flux_pos_vect, 
        cen_flux_vect, deg_poly, &mse));
    xsh_msg_dbg_medium("Polynomial blaze fitting for order %d with mse %f", absorder, mse);

    while( mse >= MSE_LIMIT){
      deg_poly--;
      xsh_msg( "Not good mse %f Decrease deg poly to %d",mse, deg_poly);
      xsh_free_polynomial( &(order_list->list[iorder].blazepoly));
      check( order_list->list[iorder].blazepoly = xsh_polynomial_fit_1d_create( 
        cen_flux_pos_vect,
        cen_flux_vect, deg_poly, &mse));
    }
#if REGDEBUG_BLAZE
    /* REGDEBUG */
    {
      FILE* regdebug = NULL;
      char filename[256];

      sprintf(filename, "blaze_%d.dat",absorder);
      regdebug = fopen(filename,"w");

      fprintf( regdebug, "# y flux poly\n");

      for( y=0; y< size_y; y++){
        double flux_cen_val;
        
        check( flux_cen_val = cpl_polynomial_eval_1d(
          order_list->list[iorder].blazepoly, cen_flux_pos[y], NULL));
        fprintf(regdebug, "%f %f %f\n", cen_flux_pos[y], cen_flux[y], flux_cen_val);
      }
      fclose(regdebug);
    }
#endif

    /* normalize the order */
    for(y=start_y; y <= end_y; y++){
      double flux_cen_val;
      int x_min, x_max;

      check( x_min = xsh_order_list_eval_int( order_list,
        order_list->list[iorder].edglopoly, y));
      check( x_max = xsh_order_list_eval_int( order_list,
        order_list->list[iorder].edguppoly, y));      
      check( flux_cen_val = cpl_polynomial_eval_1d(
         order_list->list[iorder].blazepoly, y, NULL));

      for( i=x_min; i<= x_max; i++){
        result_data[i-1+(y-1)*nx] = flux_cen_val;
      }
    }
    xsh_unwrap_vector( &cen_flux_vect);
    xsh_unwrap_vector( &cen_flux_pos_vect);
  } /* end loop over orders */

#if REGDEBUG_BLAZE
  { 
    
    check( cpl_image_save( result, "BLAZE_IMAGE.fits",
      CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT));
  }
#endif

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_unwrap_vector( &cen_flux_vect);
      xsh_unwrap_vector( &cen_flux_pos_vect);
      xsh_free_image( &result);
    }
    xsh_pre_free( &masterflat_pre);
    XSH_FREE( cen_flux);
    XSH_FREE( cen_flux_pos);
    return result;
}
/*****************************************************************************/

/**@}*/
