/*                                                                           *
 *   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:56:39 $
 * $Revision: 1.13 $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_resid_order Residual table orderpos
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/
#define XSH_RESIDX_THRESHOLD 0.1
/**@{*/


/*-----------------------------------------------------------------------------
                                 Includes
 ----------------------------------------------------------------------------*/
#include <xsh_data_instrument.h>
#include <xsh_data_order_resid_tab.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <cpl.h>
#include <xsh_drl.h>
#include <xsh_utils_table.h>

/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/


/**
  @brief 
    Create a residual tab structure.

  @param[in] size
    The numbers of rows in residual tab
  @param[in] orders
    Array of orders
  @param[in] posx
    Array of X central position fitted
  @param[in] posy
    Array of y position
  @param[in] resx
    Array of difference with polynomial fitted by xsh_orderpos
  @param[in] polx
    Array of polynomial fitted X position
  @return 
    The resid_tab allocated structure
*/
/*---------------------------------------------------------------------------*/
xsh_resid_order_tab * xsh_resid_order_create(int size, int * orders,
					     double * posx, double * posy,
					     double * resx, double* polx )
{
  xsh_resid_order_tab* result = NULL;
  int i;

  /* verify input */
  XSH_ASSURE_NOT_ILLEGAL( size>=0);
  XSH_ASSURE_NOT_NULL( orders);
  XSH_ASSURE_NOT_NULL( posx);
  XSH_ASSURE_NOT_NULL( posy);
  XSH_ASSURE_NOT_NULL( resx);
  XSH_ASSURE_NOT_NULL( polx);

  XSH_CALLOC( result, xsh_resid_order_tab, 1);

  XSH_CALLOC( result->order, int, size);
  XSH_CALLOC( result->pos_x, double, size);
  XSH_CALLOC( result->pos_y, double, size);
  XSH_CALLOC( result->res_x, double, size);
  XSH_CALLOC( result->pol_x, double, size);


  check (result->header = cpl_propertylist_new());

  result->size = size;
  xsh_msg_dbg_low( " xsh_resid_order_create( %d )", result->size ) ;

  for( i=0; i<size; i++) {
    result->order[i] = orders[i];
    result->pos_x[i] = posx[i];
    result->pos_y[i] = posy[i];
    result->res_x[i] = resx[i];
    result->pol_x[i] = polx[i];
  }

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_resid_order_free( &result);
    }
    return result;  
}

/*---------------------------------------------------------------------------*/
/**
 *  @brief 
 *    Load a residual tab from a frame
 *
 *  @param[in] resid_tab_frame
 *    The residual tab to load
 *  @return
 *    a new allocated resid_tab structure
 */
/*---------------------------------------------------------------------------*/

xsh_resid_order_tab * xsh_resid_order_load( cpl_frame * resid_tab_frame)
{
  xsh_resid_order_tab * result = NULL;
  cpl_table * table = NULL;
  const char * tablename = NULL;
  int i = 0;
  int size = 0;

  /* verify input */
  XSH_ASSURE_NOT_NULL( resid_tab_frame);

  /* get table */
  check( tablename = cpl_frame_get_filename( resid_tab_frame));

  XSH_TABLE_LOAD( table, tablename);

  check( size = cpl_table_get_nrow(table));

  XSH_CALLOC( result, xsh_resid_order_tab, 1);
  check (result->header = cpl_propertylist_load( tablename, 0));
  result->size = size;

  XSH_CALLOC( result->order, int, size);
  XSH_CALLOC( result->pos_x, double, size);
  XSH_CALLOC( result->pos_y, double, size);
  XSH_CALLOC( result->res_x, double, size);
  XSH_CALLOC( result->pol_x, double, size);

  for(i=0; i<size;i++){

    check( xsh_get_table_value( table, 
      XSH_RESID_ORDER_TABLE_COLNAME_ORDER, 
      CPL_TYPE_INT, i, &result->order[i]));

    check( xsh_get_table_value( table,
      XSH_RESID_ORDER_TABLE_COLNAME_POSX,
      CPL_TYPE_DOUBLE, i, &result->pos_x[i]));

    check( xsh_get_table_value( table,
      XSH_RESID_ORDER_TABLE_COLNAME_POSY,
      CPL_TYPE_DOUBLE, i, &result->pos_y[i]));

    check( xsh_get_table_value( table,
      XSH_RESID_ORDER_TABLE_COLNAME_RESX,
      CPL_TYPE_DOUBLE, i, &result->res_x[i]));

    check( xsh_get_table_value( table,
      XSH_RESID_ORDER_TABLE_COLNAME_POLX,
      CPL_TYPE_DOUBLE, i, &result->pol_x[i]));
  }

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

/*---------------------------------------------------------------------------*/
/**
  @brief 
    Free memory associated to a resid_tab

  @param[in,out] resid 
    The resid_tab to free
*/
/*---------------------------------------------------------------------------*/
void xsh_resid_order_free( xsh_resid_order_tab** resid) {
  if ( resid && *resid) {
    XSH_FREE(  (*resid)->order);
    XSH_FREE(  (*resid)->pos_x);
    XSH_FREE(  (*resid)->pos_y);
    XSH_FREE(  (*resid)->res_x);
    XSH_FREE(  (*resid)->pol_x);

    xsh_free_propertylist( &(*resid)->header);

    cpl_free(*resid);
    *resid = NULL;
  }

}


/*---------------------------------------------------------------------------*/
/**
  @brief Save a residual tab to a frame

  @param resid
    The resid_tab structure to save
  @param[in] filename
    The name of the save file on disk
  @param instrument
    The instrument arm setting 
  @param ord_qc_param
    The qc parameter structure 
  @param tag order list pro catg

  @return 
    A newly allocated residual tab frame

*/
/*---------------------------------------------------------------------------*/
cpl_frame * xsh_resid_order_save( xsh_resid_order_tab * resid,
				  const char* filename,
				  xsh_instrument * instrument, 
                                  ORDERPOS_QC_PARAM* ord_qc_param,
				  const char* tag)
{
  cpl_frame *result = NULL ;
  cpl_table *table = NULL;
  cpl_propertylist *header = NULL;
  int i = 0;
  //int ord_min=0;
  //int ord_max=0;
  cpl_table* sel=NULL;

  XSH_ASSURE_NOT_NULL( resid);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_NULL( instrument);

  xsh_msg_dbg_high( "  xsh_resid_order_save, size = %d", resid->size ) ;

  /* create a table */
  check( table = cpl_table_new( XSH_RESID_ORDER_TABLE_NB_COL));
  header = resid->header;
  /* copy some QC parameters */

  // To Be Done !

  check( xsh_pfits_set_qc( header, &resid->residmin,
			   QC_ORD_ORDERPOS_RESIDMIN,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residmax,
			   QC_ORD_ORDERPOS_RESIDMAX,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residavg,
			   QC_ORD_ORDERPOS_RESIDAVG,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residrms,
			   QC_ORD_ORDERPOS_RESIDRMS,
			   instrument ) ) ;

  /* create columns */ 
  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_ORDER,
    XSH_RESID_ORDER_TABLE_UNIT_ORDER, CPL_TYPE_INT);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POSX,
    XSH_RESID_ORDER_TABLE_UNIT_POSX, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POSY,
    XSH_RESID_ORDER_TABLE_UNIT_POSY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_RESX,
    XSH_RESID_ORDER_TABLE_UNIT_RESX, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POLX,
    XSH_RESID_ORDER_TABLE_UNIT_POLX, CPL_TYPE_DOUBLE);

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

  /* insert data */
  for (i=0; i<resid->size; i++) {
    check(cpl_table_set_int(table, XSH_RESID_ORDER_TABLE_COLNAME_ORDER,
			i, resid->order[i]));
    check(cpl_table_set_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POSX,
      i, resid->pos_x[i]));
    check(cpl_table_set_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POSY,
      i, resid->pos_y[i]));
    check(cpl_table_set_double(table,XSH_RESID_ORDER_TABLE_COLNAME_RESX,
      i, resid->res_x[i]));
    check(cpl_table_set_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POLX,
      i, resid->pol_x[i]));
  }


  /*
    Setup more QC parameters
  */
  //check(ord_max=cpl_table_get_column_max(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER));
  //check(ord_min=cpl_table_get_column_min(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER));

  ord_qc_param->nposall = resid->size ;
  
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_LESS_THAN,XSH_RESIDX_THRESHOLD));
  
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_GREATER_THAN,-XSH_RESIDX_THRESHOLD));
  
  
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_LESS_THAN,2*resid->residrms));
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_GREATER_THAN,-2*resid->residrms));
  check(sel=cpl_table_extract_selected(table));

 
  check(resid->residavg_sel = 
	cpl_table_get_column_mean(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residmax_sel = 
	cpl_table_get_column_max(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residmin_sel = 
	cpl_table_get_column_min(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residrms_sel = 
	cpl_table_get_column_stdev(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  xsh_msg_dbg_high("after selection: avg=%g med=%g rms=%g min=%g max=%g",
	  resid->residavg_sel,
          cpl_table_get_column_median(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX),
	  resid->residrms_sel,
	  resid->residmin_sel,
	  resid->residmax_sel);
  

  check( xsh_pfits_set_qc( header, &resid->residmin_sel,
			   QC_ORD_ORDERPOS_RESIDMIN_SEL,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residmax_sel,
			   QC_ORD_ORDERPOS_RESIDMAX_SEL,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residavg_sel,
			   QC_ORD_ORDERPOS_RESIDAVG_SEL,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( header, &resid->residrms_sel,
			   QC_ORD_ORDERPOS_RESIDRMS_SEL,
			   instrument ) ) ;
   

  /* create fits file */
  check( xsh_pfits_set_pcatg(header, tag));
  check( cpl_table_save(table, header,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));
  //check (xsh_add_temporary_file( filename));

  cleanup:
    
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame(&result);
    }
    XSH_TABLE_FREE( table);
    XSH_TABLE_FREE( sel);
    return result ;
}









/*---------------------------------------------------------------------------*/
/**
  @brief convert a residual tab structure to a cpl_table 

  @param resid
    The resid_tab structure to save

  @param instrument
    The instrument arm setting 

  @param ord_qc_param
    The qc parameter structure 


  @return 
    A newly allocated residual cpl_table

*/
/*---------------------------------------------------------------------------*/
cpl_table * 
xsh_resid_order_2tab( xsh_resid_order_tab * resid,
				  xsh_instrument * instrument, 
                                  ORDERPOS_QC_PARAM* ord_qc_param)
{
  cpl_table *table = NULL;
  int i = 0;
  //int ord_min=0;
  //int ord_max=0;
  cpl_table* sel=NULL;
  int* porder=NULL;
  double* pposx=NULL;
  double* pposy=NULL;
  double* presx=NULL;
  double* ppolx=NULL;

  XSH_ASSURE_NOT_NULL( resid);
  XSH_ASSURE_NOT_NULL( instrument);

  xsh_msg( "  xsh_resid_order_save, size = %d", resid->size ) ;

  /* create a table */
  check( table = cpl_table_new( XSH_RESID_ORDER_TABLE_NB_COL));

  /* create columns */ 
  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_ORDER,
    XSH_RESID_ORDER_TABLE_UNIT_ORDER, CPL_TYPE_INT);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POSX,
    XSH_RESID_ORDER_TABLE_UNIT_POSX, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POSY,
    XSH_RESID_ORDER_TABLE_UNIT_POSY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_RESX,
    XSH_RESID_ORDER_TABLE_UNIT_RESX, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_ORDER_TABLE_COLNAME_POLX,
    XSH_RESID_ORDER_TABLE_UNIT_POLX, CPL_TYPE_DOUBLE);

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


  // insert data: 
  // using pointers is faster, but remember to initialize columns! 
  cpl_table_fill_column_window(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER,
			       0,resid->size,-1);

  cpl_table_fill_column_window(table,XSH_RESID_ORDER_TABLE_COLNAME_POSX,
			       0,resid->size,-1);

  cpl_table_fill_column_window(table,XSH_RESID_ORDER_TABLE_COLNAME_POSY,
			       0,resid->size,-1);

  cpl_table_fill_column_window(table,XSH_RESID_ORDER_TABLE_COLNAME_RESX,
			       0,resid->size,-1);

  cpl_table_fill_column_window(table,XSH_RESID_ORDER_TABLE_COLNAME_POLX,
			       0,resid->size,-1);
  porder=cpl_table_get_data_int(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER);
  pposx=cpl_table_get_data_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POSX);
  pposy=cpl_table_get_data_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POSY);
  presx=cpl_table_get_data_double(table,XSH_RESID_ORDER_TABLE_COLNAME_RESX);
  ppolx=cpl_table_get_data_double(table,XSH_RESID_ORDER_TABLE_COLNAME_POLX);



  for (i=0; i<resid->size; i++) {
    check(porder[i]=resid->order[i]);
    check(pposx[i]=resid->pos_x[i]);
    check(pposy[i]=resid->pos_y[i]);
    check(presx[i]=resid->res_x[i]);
    check(ppolx[i]=resid->pol_x[i]);
  }


  /*
    Setup more QC parameters
  */
  //check(ord_max=cpl_table_get_column_max(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER));
  //check(ord_min=cpl_table_get_column_min(table,XSH_RESID_ORDER_TABLE_COLNAME_ORDER));

  ord_qc_param->nposall = resid->size ;
  
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_LESS_THAN,XSH_RESIDX_THRESHOLD));
  
  
  check(ord_qc_param->npossel=cpl_table_and_selected_double(table,
							    XSH_RESID_ORDER_TABLE_COLNAME_RESX,CPL_LESS_THAN,2*resid->residrms));
  check(sel=cpl_table_extract_selected(table));

 
  check(resid->residavg_sel = 
	cpl_table_get_column_mean(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residmax_sel = 
	cpl_table_get_column_max(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residmin_sel = 
	cpl_table_get_column_min(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  check(resid->residrms_sel = 
	cpl_table_get_column_stdev(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX));

  xsh_msg("after selection: avg=%g med=%g rms=%g min=%g max=%g",
	  resid->residavg_sel,
          cpl_table_get_column_median(sel,XSH_RESID_ORDER_TABLE_COLNAME_RESX),
	  resid->residrms_sel,
	  resid->residmin_sel,
	  resid->residmax_sel);
  

  cleanup:
 
    XSH_TABLE_FREE( sel);
    return table ;
}








/**@}*/
