/*                                                                           *
 *   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-06-21 07:40:22 $
 * $Revision: 1.25 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup data_handling    Data Format Handling functions
 */
/**
 * @defgroup xsh_data_arclist  List of Arclines
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


/*-----------------------------------------------------------------------------
                                 Includes
 ----------------------------------------------------------------------------*/
#include <xsh_data_arclist.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <cpl.h>
#include <xsh_utils_table.h>
#include <math.h>
#include <xsh_drl.h>
/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/
static int xsh_arclist_lambda_compare(const void* one, const void* two){
  xsh_arcline** a = NULL;
  xsh_arcline** b = NULL;
  float la, lb;

  a = (xsh_arcline**) one;
  b = (xsh_arcline**) two;
  
  la = (*a)->wavelength;
  lb = (*b)->wavelength;
 
  if (la <= lb)
    return -1;
  else
    return 1; 


}


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

 * @brief sort arcline list by increasing lambda
 * 
 * @param list pointer to arcline_list
 */
/*---------------------------------------------------------------------------*/
void xsh_arclist_lambda_sort(xsh_arclist* list){
  qsort(list->list,list->size,sizeof(xsh_arcline*),
    xsh_arclist_lambda_compare);
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief get size of arcline list
 * 
 * @param list pointer to arcline_list
 * @return the size
 */
/*---------------------------------------------------------------------------*/
int xsh_arclist_get_size(xsh_arclist* list){
  int i=0;

  XSH_ASSURE_NOT_NULL(list);
  i = list->size;

  cleanup:
    return i;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief get nb lines rejected in arcline list
 *  
 * @param list pointer to arcline_list
 * @return the number of rejected lines
 */
/*---------------------------------------------------------------------------*/
int xsh_arclist_get_nbrejected(xsh_arclist* list){
  int i=0;

  XSH_ASSURE_NOT_NULL(list);
  i = list->nbrejected;

  cleanup:
    return i;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief get wavelength of a line in the arcline list
 *  
 * @param list pointer to arcline_list
 * @param idx index in the list
 * @return the wavelength of the arcline
 */
/*---------------------------------------------------------------------------*/
float xsh_arclist_get_wavelength(xsh_arclist* list, int idx)
{
  float f = 0.0;

  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(idx >= 0 && idx < list->size);
  f = list->list[idx]->wavelength;

  cleanup:
    return f;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief reject a line from the list
 *  
 * @param list pointer to arcline_list
 * @param idx index of the line to reject
 */
/*---------------------------------------------------------------------------*/
void xsh_arclist_reject(xsh_arclist* list, int idx)
{
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(idx >= 0 && idx < list->size);
  
  if (list->rejected[idx] == 0){
    list->rejected[idx] = 1;
    list->nbrejected++;
  }

  cleanup:
    return;

}

/*---------------------------------------------------------------------------*/
/**
 * @brief restore a line from the list
 *
 * @param list pointer to arcline_list
 * @param idx index of the line to reject
 */
/*---------------------------------------------------------------------------*/
void xsh_arclist_restore(xsh_arclist* list, int idx)
{
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(idx >= 0 && idx < list->size);

  if (list->rejected[idx] == 1){
    list->rejected[idx] = 0;
    list->nbrejected--;
  }

  cleanup:
    return;

}
/*---------------------------------------------------------------------------*/
/** 
 * @brief give if a line is rejected
 *  
 * @param list pointer to arcline_list
 * @param idx Index of the arc in the list

 * @return 1 if the line is rejected else 0
 */
/*---------------------------------------------------------------------------*/
int xsh_arclist_is_rejected(xsh_arclist* list, int idx)
{
  int res = 0;

  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(idx >= 0 && idx < list->size);

  res = list->rejected[idx];
  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/**
  @brief get header of the table
  @param list the arclist
  @return the header associated to the table
 */
/*---------------------------------------------------------------------------*/
cpl_propertylist* xsh_arclist_get_header(xsh_arclist* list)
{
  cpl_propertylist * res = NULL;

  XSH_ASSURE_NOT_NULL(list);
  res = list->header;
  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Dump main info about an arcline_list
 * 
 * @param list pointer to arcline_list
 */
/*---------------------------------------------------------------------------*/
void xsh_dump_arclist( xsh_arclist* list)
{
  int i = 0;

  XSH_ASSURE_NOT_NULL(list);

  xsh_msg( "ARCLINE_LIST Dump %d lines",list->size);  

  for(i=0; i< list->size; i++) {
    const char* name = list->list[i]->name;
    const char* comment = list->list[i]->comment;
    if (name == NULL) name ="";
    if (comment == NULL) comment ="";
    xsh_msg("  Wavelength %f name %s flux %d comment %s",
      list->list[i]->wavelength, name,
      list->list[i]->flux, comment);
  }
  xsh_msg( "END ARCLINE_LIST");

  cleanup:
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief load an arcline list frame in arclist structure
  @param frame the table frame ARCLINE_LIST
  @return the arclist allocated structure

 */
/*---------------------------------------------------------------------------*/
xsh_arclist* xsh_arclist_load(cpl_frame* frame){
  cpl_table* table = NULL; 
  const char* tablename = NULL;
  xsh_arclist* result = NULL;
  int i = 0;

  /* verify input */
  XSH_ASSURE_NOT_NULL( frame);

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

  XSH_TABLE_LOAD( table, tablename);

  /* allocate memory */
  XSH_CALLOC(result,xsh_arclist,1);
   
  /* init structure */
  check(result->size = cpl_table_get_nrow(table));
  XSH_CALLOC(result->list, xsh_arcline*, result->size);
  XSH_CALLOC(result->rejected, int, result->size);
  result->nbrejected = 0;

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

  /* load table data */
  for(i=0;i<result->size;i++){
    const char* name ="";
    const char* comment ="";
    xsh_arcline* arc = (xsh_arcline*)cpl_malloc(sizeof(xsh_arcline));

    check(xsh_get_table_value(table, XSH_ARCLIST_TABLE_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT, i, &(arc->wavelength)));
    check(xsh_get_table_value(table, XSH_ARCLIST_TABLE_COLNAME_NAME,
      CPL_TYPE_STRING, i, &name));
    check(xsh_get_table_value(table, XSH_ARCLIST_TABLE_COLNAME_FLUX,
      CPL_TYPE_INT, i, &(arc->flux)));
    check(xsh_get_table_value(table, XSH_ARCLIST_TABLE_COLNAME_COMMENT,
      CPL_TYPE_STRING, i, &comment));
    if (name != NULL) {
      arc->name = xsh_stringdup(name);
    }
    else {
      arc->name = NULL;
    }
    if (comment != NULL) {
      arc->comment = xsh_stringdup(comment);
    }
    else {
      arc->comment = NULL;
    }
    result->list[i] = arc;
  }

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

/*---------------------------------------------------------------------------*/
/**
  @brief free memory associated to a arcline
  @param arc the arcline to free
 */
/*---------------------------------------------------------------------------*/
void xsh_arcline_free(xsh_arcline** arc)
{
  if (arc && (*arc)){
    if ((*arc)->name != NULL) {
      cpl_free((*arc)->name);
    }
    if ((*arc)->comment != NULL) {
      cpl_free((*arc)->comment);
    }
    cpl_free(*arc);
    *arc = NULL;
  }
}

/*---------------------------------------------------------------------------*/
/**
  @brief free memory associated to a arclist
  @param list the arclist to free
 */
/*---------------------------------------------------------------------------*/
void xsh_arclist_free(xsh_arclist** list)
{
  int i;
 
  if (list && *list) {
    if ((*list)->list) {
      for (i=0; i< (*list)->size; i++) {
        xsh_arcline* arc = (*list)->list[i];
        xsh_arcline_free(&arc);
      }
      cpl_free((*list)->list);
      xsh_free_propertylist(&((*list)->header));
    }
    XSH_FREE((*list)->rejected);
    cpl_free(*list);
    *list = NULL;
  }
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Clean an arclist according to a list of valid lambda
  @param[in] list
    The arclist (order by increasing wavelength)
  @param[in] lambda
    The lambda array (order by increasing wavelength)
  @param[in] size
    The size of lambda array
  
 */
/*---------------------------------------------------------------------------*/
void xsh_arclist_clean_from_list( xsh_arclist* list, double* lambda, int size)
{
  int i=0, j=0;
  int nrejected=0;
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( lambda);

  for( i=0; i< list->size; i++){
    int find = 0;
    double arc_lambda = 0.0;

    check( arc_lambda = xsh_arclist_get_wavelength( list, i));

    for ( j = 0 ; j<size ; j++ ) {
      if ( fabs( arc_lambda-lambda[j]) <= WAVELENGTH_PRECISION ) {
	find = 1 ;
	break ;
      }
    }
    if ( find == 0 ) {
       check( xsh_arclist_reject( list, i));
       nrejected++;
    }


  }
  XSH_REGDEBUG("cleanarclines list size %d rejected %d (%d)", list->size,
	  list->nbrejected, size );

  check( xsh_arclist_clean( list)); 
  cleanup:
    return;
}


void xsh_arclist_clean_from_list_not_flagged( xsh_arclist* list, double* lambda, int* flag, int size)
{
  int i=0, j=0;
  int nrejected=0;
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( lambda);

  for( i=0; i< list->size; i++){
    int find = 0;
    double arc_lambda = 0.0;

    check( arc_lambda = xsh_arclist_get_wavelength( list, i));
    for ( j = 0 ; j<size ; j++ ) {
      if ( fabs( arc_lambda-lambda[j]) <= WAVELENGTH_PRECISION && 
	   (flag[j] == 0)  ) {
	find = 1 ;
 	break ;
      }
    }
    if ( find == 0 ) {
       check( xsh_arclist_reject( list, i));
       //xsh_msg("reject lambda[%d]=%g",i,arc_lambda);
       nrejected++;
    }

  }

  XSH_REGDEBUG("cleanarclines list size %d rejected %d (%d)", list->size,
    list->nbrejected, size );
  check( xsh_arclist_clean( list));
 

  cleanup:
    return;
}


void xsh_arclist_clean(xsh_arclist* list)
{
  int i, j;

  XSH_ASSURE_NOT_NULL(list);

  j = 0;
  for(i=0;i<list->size;i++)
  {
    if(xsh_arclist_is_rejected(list,i)){
      xsh_arcline_free(&list->list[i]);
    }
    else{
      list->list[j] = list->list[i];
      list->rejected[j] = 0;
      j++;
    }
  }
  list->size = j;
  list->nbrejected = 0;
  
  cleanup:
    return; 
}
/*---------------------------------------------------------------------------*/
/**
  @brief save a arclist to a frame
  @param list the arclist structure to save
  @param filename the name of the save file on disk
  @param tag  the frame tag
  @return a newly allocated frame

 */
/*---------------------------------------------------------------------------*/
cpl_frame* 
xsh_arclist_save(xsh_arclist* list,const char* filename,const char* tag)
{
  cpl_table* table = NULL;
  cpl_frame * result = NULL ;
  int i=0;

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

  /* create column names */
  check(
    cpl_table_new_column(table,XSH_ARCLIST_TABLE_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT));
  check(
    cpl_table_set_column_unit ( table, XSH_ARCLIST_TABLE_COLNAME_WAVELENGTH,
      XSH_ARCLIST_TABLE_UNIT_WAVELENGTH));
  check(
    cpl_table_new_column(table,XSH_ARCLIST_TABLE_COLNAME_NAME,
     CPL_TYPE_STRING));
  check(
    cpl_table_set_column_unit ( table, XSH_ARCLIST_TABLE_COLNAME_NAME,
      XSH_ARCLIST_TABLE_UNIT_NAME));

  check(
    cpl_table_new_column(table,XSH_ARCLIST_TABLE_COLNAME_FLUX,
     CPL_TYPE_INT));
  check(
    cpl_table_set_column_unit ( table, XSH_ARCLIST_TABLE_COLNAME_FLUX,
      XSH_ARCLIST_TABLE_UNIT_FLUX));

  check(
    cpl_table_new_column(table,XSH_ARCLIST_TABLE_COLNAME_COMMENT,
     CPL_TYPE_STRING));
  check(
    cpl_table_set_column_unit ( table, XSH_ARCLIST_TABLE_COLNAME_COMMENT,
      XSH_ARCLIST_TABLE_UNIT_COMMENT));

  check(cpl_table_set_size(table,list->size));

  /* insert data */
  for(i=0;i<list->size;i++){
      check(cpl_table_set_float(table,XSH_ARCLIST_TABLE_COLNAME_WAVELENGTH,
        i,list->list[i]->wavelength));
      check(cpl_table_set_string(table,XSH_ARCLIST_TABLE_COLNAME_NAME,
        i,list->list[i]->name));
      check(cpl_table_set_int(table,XSH_ARCLIST_TABLE_COLNAME_FLUX,
        i,list->list[i]->flux));
      check(cpl_table_set_string(table,XSH_ARCLIST_TABLE_COLNAME_COMMENT,
        i,list->list[i]->comment));
  }

  /* 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));

  cleanup:
    XSH_TABLE_FREE( table);
    return result ;
}

/**@}*/
