/*                                                                           *
 *   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-12-17 16:57:15 $
 * $Revision: 1.27 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_the_map Theoretical Map
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


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

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

  a = (xsh_the_arcline**) one;
  b = (xsh_the_arcline**) two;

  la = (*a)->wavelength;
  lb = (*b)->wavelength;

  if (la <= lb)
    return -1;
  else
    return 1;


}

static int xsh_the_map_lambda_order_slit_compare(const void* one, 
  const void* two){

  xsh_the_arcline** a = NULL;
  xsh_the_arcline** b = NULL;
  float la, lb;
  int oa, ob;
  float sa, sb;

  a = (xsh_the_arcline**) one;
  b = (xsh_the_arcline**) two;

  la = (*a)->wavelength;
  lb = (*b)->wavelength;

  oa = (*a)->order;
  ob = (*b)->order;

  sa = (*a)->slit_position;
  sb = (*b)->slit_position;

  if ( (lb - la) > WAVELENGTH_PRECISION ){
    return -1;
  }
  else if ((lb - la) < WAVELENGTH_PRECISION ){ 
    return 1;
  }
  else {
    if (oa < ob) {
      return -1;
    }
    else if ( oa > ob) {
      return 1;
    }
    else{
      if (sa <= sb){
        return -1;
      }
      else{
        return 1;
      }
    }
  }
}
/*---------------------------------------------------------------------------*/
/** 
 * @brief sort the_map arcline list by increasing lambda
 * 
 * @param list pointer to arcline_list
 */
/*---------------------------------------------------------------------------*/
void xsh_the_map_lambda_sort(xsh_the_map* list)
{
  qsort(list->list,list->size,sizeof(xsh_the_arcline*),
    xsh_the_map_lambda_compare);
}

void xsh_the_map_lambda_order_slit_sort(xsh_the_map* list)
{
    qsort(list->list,list->size,sizeof(xsh_the_arcline*),
    xsh_the_map_lambda_order_slit_compare);
}
/*---------------------------------------------------------------------------*/
/** 
 * @brief get size of the map list
 * 
 * @param list pointer to arcline_list
 * @return the size
 */
/*---------------------------------------------------------------------------*/
int xsh_the_map_get_size(xsh_the_map* list)
{
  int i=0;

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

  cleanup:
    return i;
}
/*---------------------------------------------------------------------------*/
/** 
 * @brief get detx of the map list
 * 
 * @param list pointer to arcline_list
 *  @param idx index in the arcline list
 * @return the detector_x
 */
/*---------------------------------------------------------------------------*/
double xsh_the_map_get_detx(xsh_the_map* list, int idx)
{
  double res = 0.0;

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

  cleanup:
    return res;
}
/*---------------------------------------------------------------------------*/
/** 
 * @brief get dety of the map list
 * 
 * @param list pointer to arcline_list
 *  @param idx index in the arcline list
 * @return the detector_y
 */
/*---------------------------------------------------------------------------*/
double xsh_the_map_get_dety(xsh_the_map* list, int idx)
{
  double res = 0.0;

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

  res = list->list[idx]->detector_y;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief get wavelength of the map list
 * 
 * @param list pointer to arcline_list
 *  @param idx index in the arcline list
 * @return the wavelength
 */
/*---------------------------------------------------------------------------*/
float xsh_the_map_get_wavelength(xsh_the_map* list, int idx)
{
  float res = 0.0;

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

  res = list->list[idx]->wavelength;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief get order of the map list
 * 
 * @param list pointer to arcline_list
 *  @param idx index in the arcline list
 * @return the order
 */
int xsh_the_map_get_order(xsh_the_map* list, int idx)
{
  int res = 0;

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

  res = list->list[idx]->order;

  cleanup:
    return res;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief get slit position of the map list
 * 
 * @param list pointer to arcline_list
 * @param idx index in the arcline list
 * @return the slit position
 */
int xsh_the_map_get_slit_index(xsh_the_map* list, int idx)
{
  int res = 0;

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

  res = list->list[idx]->slit_index;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief get slit position of the map list
 * 
 * @param list pointer to arcline_list
 * @param idx index in the arcline list
 * @return the slit position
 */
float xsh_the_map_get_slit_position( xsh_the_map* list, int idx)
{
  float res = 0;

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

  res = list->list[idx]->slit_position;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Dump main info about the_map
 * 
 * @param the Pointer to the theoretical map structure
 */
/*---------------------------------------------------------------------------*/
void xsh_dump_the_map( xsh_the_map* the)
{
  int i = 0;

  assure(the != NULL,CPL_ERROR_NULL_INPUT,"Null the map");

  xsh_msg( "THE_MAP Dump %d lines",the->size);  
  for(i=0; i< the->size; i++) {
    xsh_msg("  Wavelength %f order %d slit_position %f detector_x %f \
      detector_y %f", the->list[i]->wavelength, the->list[i]->order,
      the->list[i]->slit_position, the->list[i]->detector_x,
      the->list[i]->detector_y);
  }
  xsh_msg( "END THE_MAP");

  cleanup:
    return;
}

void xsh_the_map_set_arcline( xsh_the_map* list, int idx, float wavelength,
  int order, int slit_index, float slit_position, double detx, double dety)
{
  XSH_ASSURE_NOT_NULL( list);

  XSH_ASSURE_NOT_ILLEGAL( idx >= 0);  
  XSH_ASSURE_NOT_ILLEGAL( list->size > idx);

  list->list[idx]->wavelength = wavelength;
  list->list[idx]->order = order;
  list->list[idx]->slit_index = slit_index;  
  list->list[idx]->slit_position = slit_position;
  list->list[idx]->detector_x = detx;
  list->list[idx]->detector_y = dety;

  cleanup :
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief 
    Create an empty theoretical map
  @param size
    The size of the theoretical map
  @return 
    the the_map allocated structure

 */
/*---------------------------------------------------------------------------*/
xsh_the_map* xsh_the_map_create( int size)
{
  xsh_the_map* result = NULL;
  int i;

  XSH_ASSURE_NOT_ILLEGAL( size > 0);

  /* allocate memory */
  XSH_CALLOC( result, xsh_the_map, 1);

  result->size = size ;

  XSH_CALLOC( result->list, xsh_the_arcline*, result->size);

  for( i=0; i<size; i++){
    XSH_CALLOC( result->list[i], xsh_the_arcline,1);
  }

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_the_map_free( &result);
    }
    return result;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief 
    load a theoretical map frame in the_map structure. Suppress spurious
    entries in the THE MAP (marked with wavelength = 0).

  @param frame 
    The Theoretical Map frame
  @return 
    the the_map allocated structure

 */
/*---------------------------------------------------------------------------*/
xsh_the_map* xsh_the_map_load( cpl_frame* frame)
{
  cpl_table* table = NULL; 
  const char* tablename = NULL;
  xsh_the_map* result = NULL;
  int i, k ;
  cpl_propertylist * header = NULL ;
  int full_size ;
  int real_size = 0 ;

  /* verify input */
  XSH_ASSURE_NOT_NULL( frame);

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

  XSH_TABLE_LOAD( table, tablename);

  check_msg( header = cpl_propertylist_load( tablename, 1 ),
    "Can't load header from %s", tablename ) ;

  xsh_msg_dbg_medium("Loading %s", tablename);

  /* allocate memory */
  XSH_CALLOC( result, xsh_the_map, 1);

  /*
    First round to get the real number of good lines (wavelenght != 0.
  */
  check (full_size = cpl_table_get_nrow( table));

  xsh_msg_dbg_medium("size %d",full_size);

  for( i = 0; i<full_size ; i++) {
    float lambda ;
    
    check (xsh_get_table_value( table, XSH_THE_MAP_TABLE_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT, i, &lambda));
    if ( lambda > WAVELENGTH_PRECISION ) real_size++ ;
  }
  /* Check */
  xsh_msg_dbg_medium("real size %d",real_size);

  assure(real_size > 0, CPL_ERROR_ILLEGAL_INPUT, 
    "no valid lambda found in the_map"); 

  /* init structure */
  result->size = real_size ;
  
  XSH_CALLOC( result->list, xsh_the_arcline*, result->size);

  for(i=0, k = 0 ; i<full_size; i++) {
    xsh_the_arcline * arc = NULL;
    float lambda = 0.0;
  
    check(xsh_get_table_value( table, XSH_THE_MAP_TABLE_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT, i, &lambda ));
    /* for incorrect line detection (nan) and 0. wavelength */
    if ( fabs(lambda) < WAVELENGTH_PRECISION || (lambda != lambda) ) continue;
    XSH_CALLOC( arc, xsh_the_arcline,1);
    arc->wavelength = lambda ;
    check(xsh_get_table_value(table, XSH_THE_MAP_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT, i, &(arc->order)));
    check(xsh_get_table_value(table, XSH_THE_MAP_TABLE_COLNAME_SLITPOSITION,
      CPL_TYPE_FLOAT, i, &(arc->slit_position)));
    check(xsh_get_table_value(table, XSH_THE_MAP_TABLE_COLNAME_SLITINDEX,
      CPL_TYPE_INT, i, &(arc->slit_index)));
    check(xsh_get_table_value(table, XSH_THE_MAP_TABLE_COLNAME_DETECTORX,
      CPL_TYPE_DOUBLE, i, &(arc->detector_x)));
    check(xsh_get_table_value(table, XSH_THE_MAP_TABLE_COLNAME_DETECTORY,
      CPL_TYPE_DOUBLE, i, &(arc->detector_y)));
    result->list[k] = arc;
    k++ ;
  }
  check( result->header = cpl_propertylist_duplicate( header ) ) ;
  cleanup:
    XSH_TABLE_FREE( table);
    xsh_free_propertylist( &header ) ;
    return result;  
}


/*---------------------------------------------------------------------------*/
/**
  @brief free memory associated to a the_arcline
  @param arc the the_arcline to free
 */
/*---------------------------------------------------------------------------*/
void xsh_the_arcline_free(xsh_the_arcline** arc) {
  if( arc && *arc) {
    cpl_free(*arc);
    *arc = NULL;
  }

}
/*---------------------------------------------------------------------------*/
/**
  @brief free memory associated to a the_map
  @param list the the_map to free
 */
/*---------------------------------------------------------------------------*/
void xsh_the_map_free(xsh_the_map** list)
{
  int i = 0;

  if (list && *list){
    if ((*list)->list){
      for(i=0; i < (*list)->size; i++) {
        xsh_the_arcline* arc =  (*list)->list[i];
        xsh_the_arcline_free(&arc);
      }
      cpl_free((*list)->list);
      xsh_free_propertylist( &((*list)->header) ) ;
      (*list)->list = NULL;
    }
    cpl_free(*list);
    *list = NULL;
  }
}
/*---------------------------------------------------------------------------*/
/**
  @brief save a the_map to a frame
  @param list the the_map structure to save
  @param filename the name of the save file on disk
  @return a newly allocated frame

 */
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_the_map_save(xsh_the_map* list,const char* filename)
{
  cpl_table* table = NULL;
  cpl_frame * result = NULL ;
  int i=0;
  
  XSH_ASSURE_NOT_NULL( list);
  
  /* create a table */
  check(table = cpl_table_new(XSH_THE_MAP_TABLE_NB_COL));

  /* create column names */
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_WAVELENGTH,
      CPL_TYPE_FLOAT));
  check(
    cpl_table_set_column_unit ( table, XSH_THE_MAP_TABLE_COLNAME_WAVELENGTH,
      XSH_THE_MAP_TABLE_UNIT_WAVELENGTH));  
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_ORDER,
     CPL_TYPE_INT));
  check(
    cpl_table_set_column_unit ( table, XSH_THE_MAP_TABLE_COLNAME_ORDER,
      XSH_THE_MAP_TABLE_UNIT_ORDER));
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_SLITPOSITION,
     CPL_TYPE_FLOAT));
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_SLITINDEX,
     CPL_TYPE_INT));
  check(
    cpl_table_set_column_unit ( table, XSH_THE_MAP_TABLE_COLNAME_SLITPOSITION,
      XSH_THE_MAP_TABLE_UNIT_SLITPOSITION));
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_DETECTORX,
     CPL_TYPE_DOUBLE));
  check(
    cpl_table_set_column_unit ( table, XSH_THE_MAP_TABLE_COLNAME_DETECTORX,
      XSH_THE_MAP_TABLE_UNIT_DETECTORX));
  check(
    cpl_table_new_column(table,XSH_THE_MAP_TABLE_COLNAME_DETECTORY,
     CPL_TYPE_DOUBLE));
  check(
    cpl_table_set_column_unit ( table, XSH_THE_MAP_TABLE_COLNAME_DETECTORY,
      XSH_THE_MAP_TABLE_UNIT_DETECTORY));

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

  /* insert data */
  for(i=0;i<list->size;i++){
    check(cpl_table_set_float(table,XSH_THE_MAP_TABLE_COLNAME_WAVELENGTH,
      i,list->list[i]->wavelength));
    check(cpl_table_set_int(table,XSH_THE_MAP_TABLE_COLNAME_ORDER,
      i,list->list[i]->order));
    check(cpl_table_set_float(table,XSH_THE_MAP_TABLE_COLNAME_SLITPOSITION,
      i,list->list[i]->slit_position));
    check(cpl_table_set_int(table,XSH_THE_MAP_TABLE_COLNAME_SLITINDEX,
      i,list->list[i]->slit_index));
    check(cpl_table_set_double(table,XSH_THE_MAP_TABLE_COLNAME_DETECTORX,
      i,list->list[i]->detector_x));
    check(cpl_table_set_double(table,XSH_THE_MAP_TABLE_COLNAME_DETECTORY,
      i,list->list[i]->detector_y));
  }

  /* create fits file */
  check(cpl_table_save(table, NULL,NULL,filename, CPL_IO_DEFAULT));

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

/**@}*/
