/*                                                                           *
 *   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-03-15 11:50:29 $
 * $Revision: 1.26 $
 */


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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup data_handling    Data Format Handling functions
 */
/**
 * @defgroup xsh_data_star_flux  List of Standard Star Flux
 * @ingroup data_handling
 *
 */
/*---------------------------------------------------------------------------*/


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

#include <xsh_dfs.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <cpl.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <xsh_utils_table.h>
#include <xsh_data_star_flux.h>
#include <xsh_data_spectrum.h>
#include <xsh_data_spectrum1D.h>

/*----------------------------------------------------------------------------
  Function prototypes
  ----------------------------------------------------------------------------*/
#define XSH_STAR_FLUX_UVB_WAV_MIN 308
/*-----------------------------------------------------------------------------
                              Implementation
 -----------------------------------------------------------------------------*/
cpl_error_code 
xsh_star_flux_list_filter_median(xsh_star_flux_list * result, 
                                 int hsize )
{
  
   cpl_vector* vflux=NULL;
   cpl_vector* vmedian=NULL;
   double* pvmedian=NULL;
   int i=0;

   XSH_ASSURE_NOT_NULL_MSG(result, "Null input flux list table frame");
   XSH_ASSURE_NOT_ILLEGAL_MSG(result->size > 2*hsize,"size  < 2*hsize. You set a too large half window size.");

   vflux=cpl_vector_wrap(result->size,result->flux);
   vmedian=cpl_vector_filter_median_create(vflux,hsize);
   pvmedian=cpl_vector_get_data(vmedian);

   for(i=0;i<result->size;i++) {
      result->flux[i]=pvmedian[i];
   }
cleanup:

   cpl_vector_unwrap(vflux);
   xsh_free_vector(&vmedian);
   return cpl_error_get_code();
}

cpl_error_code 
xsh_star_flux_list_filter_lowpass(xsh_star_flux_list * result, 
				  cpl_lowpass        filter_type,
                                  int hsize )
{
  
   cpl_vector* vflux=NULL;
   cpl_vector* vlowpass=NULL;
   double* pvlowpass=NULL;
   int i=0;

   XSH_ASSURE_NOT_NULL_MSG(result, "Null input flux list table frame");
   XSH_ASSURE_NOT_ILLEGAL_MSG(result->size > 2*hsize,"size  < 2*hsize. You set a too large half window size.");

   vflux=cpl_vector_wrap(result->size,result->flux); 
   vlowpass=cpl_vector_filter_lowpass_create(vflux,filter_type,hsize);
   pvlowpass=cpl_vector_get_data(vlowpass);

   for(i=0;i<result->size;i++) {
      result->flux[i]=pvlowpass[i];
   }
cleanup:

   cpl_vector_unwrap(vflux);
   xsh_free_vector(&vlowpass);
   return cpl_error_get_code();
}


xsh_star_flux_list * xsh_star_flux_list_create( int size )
{
  xsh_star_flux_list * result = NULL ;

  /* Create the list */
  XSH_CALLOC( result, xsh_star_flux_list, 1 ) ;
  result->header = NULL ;
  result->size = size ;
  XSH_CALLOC( result->lambda, double, size ) ;
  XSH_CALLOC( result->flux, double, size ) ;

 cleanup:
  return result ;
}

xsh_star_flux_list * xsh_star_flux_list_load( cpl_frame * star_frame )
{
  cpl_table *table = NULL ;
  const char * tablename = NULL ;
  xsh_star_flux_list * result = NULL ;
  int nentries, i ;
  double * plambda, * pflux ;

  /* verify input */
  XSH_ASSURE_NOT_NULL( star_frame);

  /* get table filename */
  check(tablename = cpl_frame_get_filename( star_frame ));

  /* Load the table */
  XSH_TABLE_LOAD( table, tablename ) ;

  /*
  cpl_table_and_selected_double(table,XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,CPL_LESS_THAN,XSH_STAR_FLUX_UVB_WAV_MIN);
  cpl_table_erase_selected(table);
  */

   check( nentries = cpl_table_get_nrow( table ) ) ;

  /* Create the list */
  check( result = xsh_star_flux_list_create( nentries ) ) ;

  plambda = result->lambda ;
  pflux = result->flux ;

  check(result->header = cpl_propertylist_load(tablename, 0));

  check(cpl_table_cast_column(table,XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,"F_WAVELENGTH",CPL_TYPE_FLOAT));
  check(cpl_table_cast_column(table,XSH_STAR_FLUX_LIST_COLNAME_FLUX,"F_FLUX",CPL_TYPE_FLOAT))
;

  /* Fill the list */
  for( i = 0 ; i< nentries ; i++, plambda++, pflux++ ) {
    float value ;

    check( xsh_get_table_value( table, "F_WAVELENGTH",
				CPL_TYPE_FLOAT, i, &value ) ) ;
    *plambda = value ;
    check( xsh_get_table_value( table, "F_FLUX",
				CPL_TYPE_FLOAT, i, &value ) ) ;
    *pflux = value ;
  }

 cleanup:
  if (cpl_error_get_code () != CPL_ERROR_NONE) {
    xsh_error_msg("can't load frame %s",cpl_frame_get_filename(star_frame));
    xsh_star_flux_list_free(&result);
  }
  XSH_TABLE_FREE( table);
  return result ;
}

cpl_error_code 
xsh_star_flux_list_to_frame( xsh_star_flux_list* list, cpl_frame* frame)
{
  xsh_spectrum *s = NULL ;
  const char * name = NULL ;
  int size=0;
  double* pflux=NULL;
  int i=0;
  const char* tag=NULL;
  cpl_frame* frm=NULL;
  /* verify input */
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( list);

  s=xsh_spectrum_load(frame);

  /* basic checks: verify list versus spectrum size and wave range */
  size=s->size;
  cpl_ensure_code(list->size == s->size, CPL_ERROR_ILLEGAL_INPUT);

  cpl_ensure_code(list->lambda[0] == s->lambda_min, CPL_ERROR_ILLEGAL_INPUT);
  cpl_ensure_code(list->lambda[size-1] == s->lambda_max, CPL_ERROR_ILLEGAL_INPUT);

  /* copy flux from list to specrum */
  pflux=xsh_spectrum_get_flux(s);
  for(i=0;i<size;i++) {
    pflux[i]=list->flux[i];
  }

  /* save result */
  name = cpl_frame_get_filename(frame);
  tag  = cpl_frame_get_tag(frame);
  frm=xsh_spectrum_save(s,name,tag);

 cleanup:
  xsh_free_frame(&frm);
  xsh_spectrum_free(&s);
  return cpl_error_get_code() ;
}



xsh_star_flux_list * xsh_star_flux_list_load_spectrum( cpl_frame * star_frame )
{

  //const char * imagename = NULL ;
  xsh_star_flux_list * result = NULL ;
  int nentries, i ;
  double * plambda, * pflux ;
  xsh_spectrum* spectrum=NULL;

  /* verify input */
  XSH_ASSURE_NOT_NULL( star_frame);

  /* get table filename */
  //check(imagename = cpl_frame_get_filename( star_frame ));

  /* Load the image */
  check(spectrum=xsh_spectrum_load(star_frame)) ;
  nentries = xsh_pfits_get_naxis1(spectrum->flux_header);

  /* Create the list */
  check( result = xsh_star_flux_list_create( nentries ) ) ;
  result->header = cpl_propertylist_duplicate(spectrum->flux_header);
  plambda = result->lambda ;
  pflux = result->flux ;

  double* pima=NULL;
  pima=cpl_image_get_data_double(spectrum->flux);

  double wmin=0;
  wmin = xsh_pfits_get_crval1(result->header);
  double wstep=0;
  wstep = xsh_pfits_get_cdelt1(result->header);

  /* Fill the list */
  for( i = 0 ; i< nentries ; i++, plambda++, pflux++ ) {

    *plambda = (float) (wmin+i*wstep) ;
    *pflux =   (float) pima[i];

  }

 cleanup:
  if (cpl_error_get_code () != CPL_ERROR_NONE) {
    xsh_error_msg("can't load frame %s",cpl_frame_get_filename(star_frame));
    xsh_star_flux_list_free(&result);
  }

  xsh_spectrum_free( &spectrum );
  return result ;
}

void xsh_star_flux_list_free( xsh_star_flux_list ** list )
{
  if ( list != NULL && *list != NULL ) {
    xsh_free_propertylist(&(*list)->header);
    cpl_free( (*list)->lambda ) ;
    cpl_free( (*list)->flux ) ;
    //check( cpl_free( (*list)->header ) );
    cpl_free( *list ) ;
     *list = NULL ;
  }


  return ;
}

cpl_error_code 
xsh_star_flux_list_dump_ascii( xsh_star_flux_list * list, const char* filename )
{
   FILE *   fout ;
   int size=0;
   int i=0;
   double * plambda=NULL;
   double * pflux=NULL;

   XSH_ASSURE_NOT_NULL_MSG(list,"Null input std star flux list!Exit");
   size=list->size;

   plambda = list->lambda ;
   pflux = list->flux ;

   if((fout=fopen(filename,"w"))==NULL) {

      return CPL_ERROR_FILE_IO ;
   } else {
      for(i=0;i<size;i++) {
	fprintf(fout, "%f %f \n", plambda[i], pflux[i]);
      }
   }
   if ( fout ) fclose( fout ) ;

 cleanup:
   return cpl_error_get_code() ;
}



cpl_frame * xsh_star_flux_list_save( xsh_star_flux_list * list,
				     const char * filename, const char * tag )
{
  cpl_table * table = NULL ;
  cpl_frame * result = NULL ;
  int size, i ;
  double * plambda, * pflux ;

  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_NULL(filename);
  
  /* create a table */
  check(table = cpl_table_new( 2 ));

  /* create column names */
  check(
    cpl_table_new_column(table, XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT));
  check(
    cpl_table_new_column(table, XSH_STAR_FLUX_LIST_COLNAME_FLUX,
      CPL_TYPE_FLOAT));

  size = list->size ;
  plambda = list->lambda ;
  pflux = list->flux ;

  check(cpl_table_set_size(table, size));

  /* insert data */
  for( i = 0 ; i<size ; i++, plambda++, pflux++ ) {
    float value ;

    value = *plambda ;
    check(cpl_table_set_float(table, XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,
			       i, value ));
    value = *pflux ;
    check(cpl_table_set_float(table, XSH_STAR_FLUX_LIST_COLNAME_FLUX,
			       i, value ));
  }

  /* create fits file */
  check(cpl_table_save( table, list->header, NULL, filename, CPL_IO_DEFAULT));
  //check( xsh_add_temporary_file( filename ));

  /* Create the frame */
  check(result=xsh_frame_product( filename, tag,
				  CPL_FRAME_TYPE_TABLE,
				  CPL_FRAME_GROUP_PRODUCT,
				  CPL_FRAME_LEVEL_TEMPORARY));

  xsh_msg_dbg_low( "Star Flux Frame Saved" ) ;

 cleanup:
  XSH_TABLE_FREE( table);
  return result ;
}




cpl_frame * 
xsh_star_flux_list_save_order( xsh_star_flux_list * list,
                               const char * filename, 
                               const char * tag,
                               const int order )
{
  cpl_table * table = NULL ;
  cpl_frame * result = NULL ;
  int size, i ;
  double * plambda, * pflux ;

  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_NULL(filename);
  
  /* create a table */
  check(table = cpl_table_new( 2 ));

  /* create column names */
  check(
    cpl_table_new_column(table, XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT));
  check(
    cpl_table_new_column(table, XSH_STAR_FLUX_LIST_COLNAME_FLUX,
      CPL_TYPE_FLOAT));

  size = list->size ;
  plambda = list->lambda ;
  pflux = list->flux ;

  check(cpl_table_set_size(table, size));

  /* insert data */
  for( i = 0 ; i<size ; i++, plambda++, pflux++ ) {
    float value ;

    value = *plambda ;
    check(cpl_table_set_float(table, XSH_STAR_FLUX_LIST_COLNAME_WAVELENGTH,
			       i, value ));
    value = *pflux ;
    check(cpl_table_set_float(table, XSH_STAR_FLUX_LIST_COLNAME_FLUX,
			       i, value ));
  }

  /* create fits file */
  if(order==0) {
     check(cpl_table_save( table,list->header,NULL,filename,CPL_IO_DEFAULT));
  } else {
     check(cpl_table_save( table,list->header,NULL,filename,CPL_IO_EXTEND));
  }
  //check( xsh_add_temporary_file( filename ));

  /* Create the frame */
  check(result=xsh_frame_product( filename, tag,
				  CPL_FRAME_TYPE_TABLE,
				  CPL_FRAME_GROUP_PRODUCT,
				  CPL_FRAME_LEVEL_TEMPORARY));

  xsh_msg_dbg_low( "Star Flux Frame Saved" ) ;

 cleanup:
  XSH_TABLE_FREE( table);
  return result ;
}




double * xsh_star_flux_list_get_lambda( xsh_star_flux_list * list )
{
  XSH_ASSURE_NOT_NULL( list ) ;

 cleanup:
  return list->lambda ;
}

double * xsh_star_flux_list_get_flux( xsh_star_flux_list * list )
{
  XSH_ASSURE_NOT_NULL( list ) ;

 cleanup:
  return list->flux ;
}


xsh_star_flux_list* xsh_star_flux_list_duplicate( xsh_star_flux_list * list )
{
  XSH_ASSURE_NOT_NULL( list ) ;

  xsh_star_flux_list* result=NULL;
  int size=list->size;
  /* Create the list */
  result=xsh_star_flux_list_create(size );

  //check(result->header = cpl_propertylist_duplicate(list->header));

  
  memcpy(result->lambda,list->lambda,size*sizeof(double));
  memcpy(result->flux,list->flux,size*sizeof(double));

 cleanup:
  return result;
}

void xsh_star_flux_list_extrapolate_wave_end( xsh_star_flux_list * list , const double wmax)
{
  XSH_ASSURE_NOT_NULL( list ) ;
  int size=list->size;
  int i=0;
  int k=0;
  int off=10;
  double f1=0;
  double f2=0;
  double w1=0;
  double w2=0;
  double x1=0;
  double x2=0;
  double m=0;
  double x=0;
  double w=0;
  int computed=0;


  /* Rayleigh-Jeans extrapolation of null flux values:
   * we can linearize the formula to have a quick fit
   * flux=A/lambda^4+B. X=1/lambda^4, Y=f==>
   * Y=A*X+B==> we can linearize */
  for (i = 0; i < size; i++) {
    if (list->lambda[i]<wmax) {
      k++;
    } else {

      if (computed == 0) {
        f2 = list->flux[k];
        f1 = list->flux[k - off];
        w2 = 1./list->lambda[k];
        x2 = w2*w2*w2*w2;
        w1 = 1./list->lambda[k - off];
        x1 = w1*w1*w1*w1;
        m = (f2 - f1) / (x2 - x1);
        computed=1;

      } else {
         w=1./list->lambda[i];
         x=w*w*w*w;

         list->flux[i]=f1+m*(x-x1);

      }
    }
  }

 cleanup:
  return;
}

cpl_error_code 
xsh_star_flux_list_divide( xsh_star_flux_list * result,  xsh_star_flux_list * factor)
{

  XSH_ASSURE_NOT_NULL( result ) ;
  XSH_ASSURE_NOT_NULL( factor ) ;
  /* Create the list */

  XSH_ASSURE_NOT_ILLEGAL_MSG(result->size==factor->size,"List of different sizes");
  int size=result->size;
  //XSH_ASSURE_NOT_ILLEGAL_MSG(result->lambda[0]==factor->lambda[0],"List of different wave start");
  //XSH_ASSURE_NOT_ILLEGAL_MSG(result->lambda[size-1]==factor->lambda[size-1],"List of different wave end");

  int i=0;
  for(i=0;i<size;i++) {
    result->flux[i] /= factor->flux[i];
  }

 cleanup:
  return cpl_error_get_code();
}

/**@}*/
