/*                                                                            *
 *   This file is part of the ESO UVES Pipeline                               *
 *   Copyright (C) 2004,2005 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: 2010-09-24 09:32:07 $
 * $Revision: 1.17 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.15  2007/12/17 07:56:36  amodigli
 * we now compute a clean median as in MIDAS
 *
 * Revision 1.14  2007/12/03 17:00:14  amodigli
 * removed dependency from flames and fixed a warning
 *
 * Revision 1.13  2007/12/03 10:39:36  amodigli
 * added uves_physmod_align_tables flames_align_table_column
 *
 * Revision 1.12  2007/06/26 15:08:42  amodigli
 * commented table dumping
 *
 * Revision 1.11  2007/06/25 16:34:52  amodigli
 * cleaned
 *
 * Revision 1.10  2007/06/06 08:17:33  amodigli
 * replace tab with 4 spaces
 *
 * Revision 1.9  2006/11/06 15:19:41  jmlarsen
 * Removed unused include directives
 *
 * Revision 1.8  2006/08/24 06:36:10  amodigli
 * fixed doxygen warning
 *
 * Revision 1.7  2006/08/22 14:17:00  amodigli
 * fixed bug on computing MedDY
 *
 * Revision 1.6  2006/08/08 15:41:19  amodigli
 * fixed bug in aligning actual and ref tables
 *
 * Revision 1.5  2006/08/02 07:02:05  amodigli
 * stability check more robust: size of actual and ref table may differ
 *
 * Revision 1.4  2006/07/31 06:29:26  amodigli
 * added QC on stability test
 *
 * Revision 1.3  2006/07/28 14:51:26  amodigli
 * fixed some bugs on improper table selection
 *
 * Revision 1.2  2006/06/28 13:28:29  amodigli
 * improved output
 *
 * Revision 1.1  2006/02/03 07:46:30  jmlarsen
 * Moved recipe implementations to ./uves directory
 *
 * Revision 1.4  2006/01/20 10:36:25  amodigli
 *
 * Fixed warings from doxigen
 *
 * Revision 1.3  2006/01/19 08:47:24  jmlarsen
 * Inserted missing doxygen end tag
 *
 * Revision 1.2  2006/01/16 13:52:58  jmlarsen
 * Removed memory leak
 *
 * Revision 1.1  2006/01/16 08:02:50  amodigli
 *
 * Added
 *
 * Revision 1.4  2006/01/09 14:05:42  amodigli
 * Fixed doxigen warnings
 *
 * Revision 1.3  2005/12/20 08:11:44  jmlarsen
 * Added CVS  entry
 *
 */

/*----------------------------------------------------------------------------*/
/**
 * @addtogroup uves_physmod
 */
/*---------------------------------------------------------------------------*/
/**@{*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif


/*-----------------------------------------------------------------------------
                                Includes
 ----------------------------------------------------------------------------*/
#include <uves_physmod_stability_check.h>
#include <uves_utils_wrappers.h>
#include <uves_msg.h>
#include <uves_error.h>

/*-----------------------------------------------------------------------------
                                Defines
 ----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
                            Functions prototypes
 ----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
                            Static variables
 ----------------------------------------------------------------------------*/

static int 
uves_physmod_align_tables(cpl_table** m_tbl, 
			  cpl_table** r_tbl);


static int
flames_align_table_column(cpl_table** m_tbl,cpl_table** r_tbl,const char* col);

/*-----------------------------------------------------------------------------
                            Functions code
 ----------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief    This procedure run a stability check
  @param    m_tbl  model table on actual frame. 
  @param    r_tbl model table on reference frame
  @param    med_dx median shift on X axis
  @param    med_dy median shift on Y axis
  @param    avg_dx mean shift on X axis
  @param    avg_dy mean shift on Y axis

  @return   0 if everything is ok, -1 otherwise


 This procedure compares values of XMES and YMES columns from check and 
 reference frames and computes the median of their difference.
 */
/*---------------------------------------------------------------------------*/

int uves_physmod_stability_check(cpl_table* m_tbl, 
                                 cpl_table* r_tbl, 
                                 double* med_dx,
                                 double* med_dy,
                                 double* avg_dx,
                                 double* avg_dy)

{


  cpl_table* tmp_tbl1=NULL;
  cpl_table* tmp_tbl2=NULL;
  cpl_table* tmp_tbl3=NULL;
  cpl_table* tmp_tbl4=NULL;

  cpl_table* tmp_tbl5=NULL;


  double std_dx=0;
  double std_dy=0;

  int msz=0;
  int rsz=0;
  int ref=0;
  int status=0;  /* note those are swapped */
  check_nomsg(cpl_table_duplicate_column(m_tbl,"X",m_tbl,"XMES"));
  check_nomsg(cpl_table_duplicate_column(m_tbl,"Y",m_tbl,"YMES"));
  check_nomsg(cpl_table_duplicate_column(m_tbl,"S",m_tbl,"STATUS"));
  check_nomsg(cpl_table_duplicate_column(m_tbl,"O",m_tbl,"ORDER"));
  check_nomsg(cpl_table_duplicate_column(m_tbl,"ID",m_tbl,"IDENT"));

  check_nomsg(cpl_table_duplicate_column(r_tbl,"X",r_tbl,"XMES"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"Y",r_tbl,"YMES"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"S",r_tbl,"STATUS"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"O",r_tbl,"ORDER"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"ID",r_tbl,"IDENT"));

  check_nomsg(msz=cpl_table_get_nrow(m_tbl));
  check_nomsg(rsz=cpl_table_get_nrow(r_tbl));

  cpl_table_save(m_tbl, NULL, NULL, "m_tbl.fits", CPL_IO_DEFAULT);
  cpl_table_save(r_tbl, NULL, NULL, "r_tbl.fits", CPL_IO_DEFAULT);
  if(msz > rsz) {
    check_nomsg(cpl_table_set_size(r_tbl,msz));
    uves_msg_warning("Actual and reference tables have different sizes: act=%d ref=%d",msz,rsz);
    uves_physmod_align_tables(&m_tbl,&r_tbl);

    check_nomsg(msz=cpl_table_get_nrow(m_tbl));
    check_nomsg(rsz=cpl_table_get_nrow(r_tbl));
    uves_msg_warning("After correction actual and reference tables have different sizes: act=%d ref=%d",msz,rsz);

  } else if (msz < rsz) {
    check_nomsg(cpl_table_set_size(m_tbl,rsz));
    uves_msg_warning("Actual and reference tables have different sizes: act=%d ref=%d",msz,rsz);
    uves_physmod_align_tables(&m_tbl,&r_tbl);
    check_nomsg(msz=cpl_table_get_nrow(m_tbl));
    check_nomsg(rsz=cpl_table_get_nrow(r_tbl));
    uves_msg_warning("After correction actual and reference tables have different sizes: act=%d ref=%d",msz,rsz);

  } 
  cpl_table_save(m_tbl, NULL, NULL, "after_m_tbl.fits", CPL_IO_DEFAULT);
  cpl_table_save(r_tbl, NULL, NULL, "after_r_tbl.fits", CPL_IO_DEFAULT);

  /* copy values measured on actual frame into table from reference frame */
  check_nomsg(cpl_table_duplicate_column(r_tbl,"Xm",m_tbl,"X"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"Ym",m_tbl,"Y"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"Sm",m_tbl,"S"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"Om",m_tbl,"O"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"IDm",m_tbl,"ID"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"WAVEm",m_tbl,"WAVE"));

  /* initialize difference columns with values from actual frame measures */
  check_nomsg(cpl_table_duplicate_column(r_tbl,"DX",r_tbl,"Xm"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"DY",r_tbl,"Ym"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"DO",r_tbl,"Om"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"DID",r_tbl,"IDm"));
  check_nomsg(cpl_table_duplicate_column(r_tbl,"DW",r_tbl,"WAVEm"));

  /* subtract values measured on reference frame */
  check_nomsg(cpl_table_subtract_columns(r_tbl,"DX","X"));
  check_nomsg(cpl_table_subtract_columns(r_tbl,"DY","Y"));
  check_nomsg(cpl_table_subtract_columns(r_tbl,"DO","O"));
  check_nomsg(cpl_table_subtract_columns(r_tbl,"DID","ID"));
  check_nomsg(cpl_table_subtract_columns(r_tbl,"DW","WAVE"));

  /* WE GET THE ABSOLUTE VALUE: sqrt(X^2)*/
  check(cpl_table_power_column(r_tbl,"DW",2.),"Error computing power column");
  check(cpl_table_power_column(r_tbl,"DW",0.5),"Error computing power column");

  /* select proper values */
  check(tmp_tbl1=uves_extract_table_rows(r_tbl,"DO",CPL_EQUAL_TO,
          0),"Error selecting DO");
  check(tmp_tbl2=uves_extract_table_rows(tmp_tbl1,"DW",CPL_LESS_THAN,
          0.001),"Error selecting DW");
  check(tmp_tbl3=uves_extract_table_rows(tmp_tbl2,"DID",CPL_LESS_THAN,
          0.001),"Error selecting DID");
  check(tmp_tbl4=uves_extract_table_rows(tmp_tbl3,"S",CPL_EQUAL_TO,
          0),"Error selecting S");
  check(tmp_tbl5=uves_extract_table_rows(tmp_tbl4,"Sm",CPL_EQUAL_TO,
          0),"Error selecting Sm");
  check_nomsg(ref=cpl_table_get_nrow(tmp_tbl5)/2);
  //cpl_table_dump(tmp_tbl5,1,2,stdout);
  check_nomsg(*med_dx=cpl_table_get_column_median(tmp_tbl5,"DX"));
  check_nomsg(*med_dy=cpl_table_get_column_median(tmp_tbl5,"DY"));
  check_nomsg(*avg_dx=cpl_table_get_column_mean(tmp_tbl5,"DX"));
  check_nomsg(*avg_dy=cpl_table_get_column_mean(tmp_tbl5,"DY"));
  check_nomsg(std_dx=cpl_table_get_column_stdev(tmp_tbl5,"DX"));
  check_nomsg(std_dy=cpl_table_get_column_stdev(tmp_tbl5,"DY"));
  /*
  uves_msg("Stability check results: Median DX = %5.3f Median DY = %5.3f",
           *med_dx,*med_dy);
       */       
  uves_msg("Stability check results: Mean DX = %5.3f Mean DY = %5.3f",
           *avg_dx,*avg_dy);
  check_nomsg(uves_sort_table_1(tmp_tbl5,"DX",0));
  check_nomsg(*med_dx=cpl_table_get_double(tmp_tbl5,"DX",ref,&status));

  uves_free_table(&tmp_tbl1);
  uves_free_table(&tmp_tbl2);

  if(std_dx > 0) {
    check(tmp_tbl1=uves_extract_table_rows(tmp_tbl5,"DX",CPL_GREATER_THAN,
					   *avg_dx-3*std_dx),"Error selecting DO");

    check(tmp_tbl2=uves_extract_table_rows(tmp_tbl1,"DX",CPL_LESS_THAN,
					   *avg_dx+3*std_dx),"Error selecting DO");

    check_nomsg(*med_dx=cpl_table_get_column_median(tmp_tbl2,"DX"));

    uves_free_table(&tmp_tbl1);
    uves_free_table(&tmp_tbl2);
  }



  check_nomsg(uves_sort_table_1(tmp_tbl5,"DY",0));
  check_nomsg(*med_dy=cpl_table_get_double(tmp_tbl5,"DY",ref,&status));




  if(std_dy > 0) {
    check(tmp_tbl1=uves_extract_table_rows(tmp_tbl5,"DY",CPL_GREATER_THAN,
					   *avg_dy-3*std_dy),"Error selecting DO");

    check(tmp_tbl2=uves_extract_table_rows(tmp_tbl1,"DY",CPL_LESS_THAN,
					   *avg_dy+3*std_dy),"Error selecting DO");

    check_nomsg(*med_dy=cpl_table_get_column_median(tmp_tbl2,"DY"));

    uves_free_table(&tmp_tbl1);
    uves_free_table(&tmp_tbl2);
  }
  /* median computed as in UVES-MIDAS */
  uves_msg("Stability check results: Median DX = %5.3f Median DY = %5.3f",
           *med_dx,*med_dy);

 cleanup:
  uves_free_table(&tmp_tbl1);
  uves_free_table(&tmp_tbl2);
  uves_free_table(&tmp_tbl3);
  uves_free_table(&tmp_tbl4);
  uves_free_table(&tmp_tbl5);

 return 0;


}

/*---------------------------------------------------------------------------*/
/**
  @brief    This procedure compares two input tables and eventually alignes 
            them
  @param    m_tbl  model table on actual frame. 
  @param    r_tbl model table on reference frame
  @param    col   reference column
  @return   0 if everything is ok, -1 otherwise


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


static int
flames_align_table_column(cpl_table** m_tbl,cpl_table** r_tbl,const char* col)
{

  double* pmw=NULL;
  double* prw=NULL;
  int* pmc=NULL;
  int* prc=NULL;
  int* pmo=NULL;
  int* pro=NULL;
  int nm=0;
  int nr=0;
  int i=0;
  int j=0;

 
  check_nomsg(pmw=cpl_table_get_data_double(*m_tbl,col));
  check_nomsg(prw=cpl_table_get_data_double(*r_tbl,col));
  check_nomsg(pmc=cpl_table_get_data_int(*m_tbl,"CHECK"));
  check_nomsg(prc=cpl_table_get_data_int(*r_tbl,"CHECK"));
  check_nomsg(pmo=cpl_table_get_data_int(*m_tbl,"CHECK"));
  check_nomsg(pro=cpl_table_get_data_int(*r_tbl,"CHECK"));
  check_nomsg(nm=cpl_table_get_nrow(*m_tbl));
  check_nomsg(nr=cpl_table_get_nrow(*r_tbl));
  for(i=0;i<nm;i++) {
    for(j=0;j<nr;j++) {

      if((pmw[i]==prw[j]) && (pmo[i]==pro[j])) {
	pmc[i]=1;
	prc[j]=1;
      }

    }
  }
 cleanup:

  return 0;

}

/*---------------------------------------------------------------------------*/
/**
  @brief    This procedure compares two input tables and eventually alignes 
            them
  @param    m_tbl  model table on actual frame. 
  @param    r_tbl model table on reference frame
  @return   0 if everything is ok, -1 otherwise


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

static int 
uves_physmod_align_tables(cpl_table** m_tbl, 
			  cpl_table** r_tbl)
{
  cpl_table* tmp=NULL;

  uves_propertylist* plist=NULL;
  int ord_min=0;
  int ord_max=0;
  double wav_min=0;
  double wav_max=0;
  int i=0;
  int nm=0;
  int nr=0;
  int nsel=0;


  check_nomsg(nm=cpl_table_get_nrow(*m_tbl));
  check_nomsg(nr=cpl_table_get_nrow(*r_tbl));

  check_nomsg(plist=uves_propertylist_new());
  check_nomsg(uves_propertylist_append_bool(plist,"ORDER",0)); /* 1 for descending order */
  check_nomsg(uves_propertylist_append_bool(plist,"WAVE",0));  /* 0 for ascending order */
  check_nomsg(uves_table_sort(*m_tbl,plist));
  check_nomsg(uves_table_sort(*r_tbl,plist));
  uves_free_propertylist(&plist);


  ord_min=(cpl_table_get_column_min(*m_tbl,"ORDER")>
           cpl_table_get_column_min(*r_tbl,"ORDER")) ?  
    cpl_table_get_column_min(*m_tbl,"ORDER") :
    cpl_table_get_column_min(*r_tbl,"ORDER");



  ord_max=(cpl_table_get_column_max(*m_tbl,"ORDER")<
           cpl_table_get_column_max(*r_tbl,"ORDER")) ?  
    cpl_table_get_column_max(*m_tbl,"ORDER") :
    cpl_table_get_column_max(*r_tbl,"ORDER");


    

  uves_msg_warning("ord_min=%d",ord_min);
  uves_msg_warning("ord_max=%d",ord_max);


 wav_min=(cpl_table_get_column_min(*m_tbl,"WAVE")>
           cpl_table_get_column_min(*r_tbl,"WAVE")) ?  
    cpl_table_get_column_min(*m_tbl,"WAVE") :
    cpl_table_get_column_min(*r_tbl,"WAVE");



  wav_max=(cpl_table_get_column_max(*m_tbl,"WAVE")<
           cpl_table_get_column_max(*r_tbl,"WAVE")) ?  
    cpl_table_get_column_max(*m_tbl,"WAVE") :
    cpl_table_get_column_max(*r_tbl,"WAVE");


  uves_msg_warning("wav_min=%g",wav_min);
  uves_msg_warning("wav_max=%g",wav_max);


  check_nomsg(nsel=cpl_table_and_selected_int(*m_tbl,"ORDER",CPL_NOT_LESS_THAN,ord_min));
  check_nomsg(nsel=cpl_table_and_selected_int(*m_tbl,"ORDER",CPL_NOT_GREATER_THAN,ord_max));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_LESS_THAN,wav_min));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_GREATER_THAN,wav_max));
  uves_msg_warning("nsel=%d",nsel);
  check_nomsg(tmp=cpl_table_extract_selected(*m_tbl));
  uves_free_table(m_tbl);
  check_nomsg(*m_tbl=cpl_table_duplicate(tmp));




  check_nomsg(nsel=cpl_table_and_selected_int(*r_tbl,"ORDER",CPL_NOT_LESS_THAN,ord_min));
  check_nomsg(nsel=cpl_table_and_selected_int(*r_tbl,"ORDER",CPL_NOT_GREATER_THAN,ord_max));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_LESS_THAN,wav_min));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_GREATER_THAN,wav_max));
  uves_msg_warning("nsel=%d",nsel);
  check_nomsg(tmp=cpl_table_extract_selected(*r_tbl));
  uves_free_table(r_tbl);
  check_nomsg(*r_tbl=cpl_table_duplicate(tmp));



  cpl_table_new_column(*m_tbl,"CHECK",CPL_TYPE_INT);
  cpl_table_new_column(*r_tbl,"CHECK",CPL_TYPE_INT);
  cpl_table_fill_column_window_int(*r_tbl,"CHECK",0,nr,0);
  cpl_table_fill_column_window_int(*m_tbl,"CHECK",0,nm,0);


  for(i=ord_min;i<=ord_max;i++) {

    cpl_table_and_selected_int(*m_tbl,"ORDER",CPL_EQUAL_TO,i);
    cpl_table_and_selected_int(*r_tbl,"ORDER",CPL_EQUAL_TO,i);
    flames_align_table_column(m_tbl,r_tbl,"WAVE");


  }
  cpl_table_select_all(*m_tbl);
  cpl_table_select_all(*r_tbl);


 wav_min=(cpl_table_get_column_min(*m_tbl,"WAVE")>
           cpl_table_get_column_min(*r_tbl,"WAVE")) ?  
    cpl_table_get_column_min(*m_tbl,"WAVE") :
    cpl_table_get_column_min(*r_tbl,"WAVE");



  wav_max=(cpl_table_get_column_max(*m_tbl,"WAVE")<
           cpl_table_get_column_max(*r_tbl,"WAVE")) ?  
    cpl_table_get_column_max(*m_tbl,"WAVE") :
    cpl_table_get_column_max(*r_tbl,"WAVE");


  uves_msg_warning("wav_min=%g",wav_min);
  uves_msg_warning("wav_max=%g",wav_max);

  check_nomsg(nsel=cpl_table_and_selected_int(*m_tbl,"ORDER",CPL_NOT_LESS_THAN,ord_min));
  check_nomsg(nsel=cpl_table_and_selected_int(*m_tbl,"ORDER",CPL_NOT_GREATER_THAN,ord_max));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_LESS_THAN,wav_min));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_GREATER_THAN,wav_max));
  check_nomsg(nsel=cpl_table_and_selected_int(*m_tbl,"CHECK",CPL_EQUAL_TO,1));
  uves_msg_warning("nsel=%d",nsel);
  check_nomsg(tmp=cpl_table_extract_selected(*m_tbl));
  uves_free_table(m_tbl);
  check_nomsg(*m_tbl=cpl_table_duplicate(tmp));

  check_nomsg(nsel=cpl_table_and_selected_int(*r_tbl,"ORDER",CPL_NOT_LESS_THAN,ord_min));
  check_nomsg(nsel=cpl_table_and_selected_int(*r_tbl,"ORDER",CPL_NOT_GREATER_THAN,ord_max));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_LESS_THAN,wav_min));
  check_nomsg(nsel=cpl_table_and_selected_double(*m_tbl,"WAVE",CPL_NOT_GREATER_THAN,wav_max));
  check_nomsg(nsel=cpl_table_and_selected_int(*r_tbl,"CHECK",CPL_EQUAL_TO,1));
  uves_msg_warning("nsel=%d",nsel);
  check_nomsg(tmp=cpl_table_extract_selected(*r_tbl));
  uves_free_table(r_tbl);
  check_nomsg(*r_tbl=cpl_table_duplicate(tmp));
 
  

 cleanup:



  //uves_free_string_const(&rout);
  //uves_free_string_const(&mout);

  return 0;
}


/**@}*/
