/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 European Southern Observatory
 *
 * This program 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
 */
/*----------------------------------------------------------------------------
 
     File name    :       spiffi_north_south_test.c
   Author       :    A. Modigliani
   Created on   :    Sep 17, 2003
   Description  : 

 * nsh.h
 * Result of a north-south test exposure are 32 continuum spectra of a 
 * pinhole that means one spectrum in each slitlet at the same spatial 
 * position.
 * Each spectrum is fitted in sp[atial direction by a Gaussian to get the 
 * sub-pixel positions for each row.
 *
 * Then the distances are determined in each row and averaged
 *
 * Result: are distances of each slitlet from each other => 31 values stored 
 *  in an ASCII file this Python script needs a frame of a pinhole source with 
 *  a continuous spectrum, that is shifted exactly perpendicular to the 
 *  slitlets. It fits the spectra in spatial direction by a Gaussian fit 
 *  function and therefore determines the sub-pixel position of the source.
 *
 *  Then the distances of the slitlets from each other are determined and 
 *   saved in an ASCII list. 
 *


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

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
/*----------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
#include "sinfo_new_nst.h"
#include "sinfo_pro_save.h"
#include "sinfo_pro_types.h"
#include "sinfo_functions.h"
#include "sinfo_ns_ini_by_cpl.h"
#include "sinfo_cube_construct.h"
#include "sinfo_utilities.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_error.h"
#include "sinfo_globals.h"

/*----------------------------------------------------------------------------
                                Defines
 ---------------------------------------------------------------------------*/

/**@{*/
/**
 * @addtogroup sinfo_rec_distortion North South test
 *
 * TBD
 */

/*----------------------------------------------------------------------------
                             Function Definitions
 ---------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
   Function     :       north_south_test()
   In           :       ini_file: file name of according .ini file
   Out          :       integer (0 if it worked, -1 if it doesn't) 
   Job          : Result of a north-south test exposure are 32 continuum 
                  spectra of a pinhole    that means one spectrum in each 
                  slitlet at the same spatial position.

                  Each spectrum is fitted in sp[atial direction by a Gaussian
                  to get the sub-pixel positions for each row.

                  Then the distances are determined in each row and averaged

                  Result: are distances of each slitlet from each other =>
                          31 values stored in an ASCII file


 ---------------------------------------------------------------------------*/
int 
sinfo_new_nst(const char* plugin_id, 
              cpl_parameterlist* config, 
              cpl_frameset* sof,
              cpl_frameset* ref_set)
{
  ns_config * cfg=NULL ;
  cpl_imagelist * list_object=NULL ;
  cpl_imagelist * list_off=NULL;
  cpl_image * im_on=NULL;
  cpl_image * im_on_sub=NULL ;
  cpl_image * im_on_ind=NULL ;
  cpl_image * im_on_gauss=NULL ;
  cpl_image * im_mask=NULL ;
  cpl_image * im_off=NULL ;

  char* name=NULL;
  char tbl_name[MAX_NAME_SIZE];
  int typ=0;
  int i = 0;

  int nob =0;
  int nof =0;

  float* distances=NULL;
  cpl_table* tbl_dist=NULL;

  cpl_vector* qc_dist=NULL;

  double  qc_dist_mean=0;
  double  qc_dist_stdev=0;
  cpl_frameset* raw=NULL;

  cpl_table* qclog_tbl=NULL;
  char key_value[MAX_NAME_SIZE];
  char key_name[MAX_NAME_SIZE];
  int  no=0;
  double lo_cut=0.;
  double hi_cut=0.;
  int pdensity=0;

  /* 
       -----------------------------------------------------------------
       1) parse the file names and parameters to the ns_config data 
          structure cfg
       -----------------------------------------------------------------
  */

  /* 
      parse the file names and parameters to the ns_config data structure cfg 
  */

  sinfo_msg("Parse cpl input");
  check_nomsg(raw=cpl_frameset_new());
  cknull(cfg = sinfo_parse_cpl_input_ns(config,sof,&raw),
     "could not parse cpl input!") ;

  if (cfg->maskInd == 1) {
    if(sinfo_is_fits_file(cfg->mask) != 1) {
      sinfo_msg_error("Input file %s is not FITS",cfg->mask); 
      goto cleanup;
    }
  }
  /*
     --------------------------------------------------------------------
     stack the frames in data cubes and take the clean mean of all frames
     --------------------------------------------------------------------
  */
  /* allocate memory for lists of object and off-frames */
  sinfo_msg("stack the frames in data cubes");
  check(list_object = cpl_imagelist_new(),
          "could not allocate memory");

  if (cfg->noff > 0 ) {
    check(list_off = cpl_imagelist_new(),
      "could not allocate memory");
  }
 
  /*
      #build different image lists for the different cases----
  */
  sinfo_msg("build different image lists for the different cases");
  nob = 0;
  nof = 0;

  for (i=0; i< cfg->nframes; i++){
    name = cfg->framelist[i];
    if(sinfo_is_fits_file(name) != 1) {
      sinfo_msg_error("Input file %s is not FITS",name);
      goto cleanup;
    } else {
      typ = sinfo_new_intarray_get_value( cfg->frametype, i );
      if (typ == 1) {
    cpl_imagelist_set(list_object,
                          cpl_image_load(name,CPL_TYPE_FLOAT,0,0),nob);
    nob = nob + 1;
      } else {
    cpl_imagelist_set(list_off,
                          cpl_image_load(name,CPL_TYPE_FLOAT,0,0),nof);
    nof = nof + 1;
      }
    }
  }

  if (cfg->noff != nof || cfg->nobj != nob ){       
      sinfo_msg_error("something wrong with the number of the "
                      "different types of frames");
      goto cleanup;
  }
  
  /*
  #---take the average of the different cubes -------------
  */
  sinfo_msg("take the average of the different cubes");

  check_nomsg(no=cpl_imagelist_get_size(list_object));
  lo_cut=(floor)(cfg->loReject*no+0.5);
  hi_cut=(floor)(cfg->hiReject*no+0.5);
  check(im_on=cpl_imagelist_collapse_minmax_create(list_object,lo_cut,hi_cut),
              "sinfo_average_with_rejection failed" );

  if (cfg->noff != 0) {
    /*
      im_off = sinfo_average_with_rejection( cube_off, 
                                       cfg->loReject, cfg->hiReject );
    */
    check_nomsg(no=cpl_imagelist_get_size(list_off));
    lo_cut=(floor)(cfg->loReject*no+0.5);
    hi_cut=(floor)(cfg->hiReject*no+0.5);
    check(im_off=cpl_imagelist_collapse_minmax_create(list_off,lo_cut,hi_cut),
      "sinfo_average_with_rejection failed" );
      sinfo_free_imagelist(&list_off);
  }
  sinfo_free_imagelist(&list_object);

  /*
  #finally, subtract off from on frames and store the result in the object cube
  */

  if (cfg->noff != 0) {
    sinfo_msg("subtract off from on frames");
    check(im_on_sub = cpl_image_subtract_create(im_on, im_off),
      "sinfo_sub_image failed" );

    sinfo_free_image(&im_on);
    sinfo_free_image(&im_off);
  } else {
    check_nomsg(im_on_sub = cpl_image_duplicate(im_on));
    sinfo_free_image(&im_on);
  }

  /*
  #---------------------------------------------------------
  # convolution with Gaussian if recommended
  #---------------------------------------------------------
  */
  if (cfg->gaussInd == 1) {
    sinfo_msg("convolution with Gaussian");
    cknull(im_on_gauss = sinfo_new_convolve_ns_image_by_gauss(im_on_sub, 
                                                              cfg->hw),
                     "could not carry out sinfo_convolveNSImageByGauss" );

    sinfo_free_image(&im_on_sub);
    check_nomsg(im_on_sub = cpl_image_duplicate(im_on_gauss));
    sinfo_free_image(&im_on_gauss);
  }

  /*
  #---------------------------------------------------------
  # static bad pixel indication
  #---------------------------------------------------------
  */

   if (cfg->maskInd == 1) {
     sinfo_msg("static bad pixel indication");
     check(im_mask = cpl_image_load(cfg->mask,CPL_TYPE_FLOAT,0,0),
       "could not load static bad pixel mask" );
     cknull(im_on_ind = sinfo_new_mult_image_by_mask(im_on_sub, im_mask),
        "could not carry out sinfo_multImageByMask" );
     sinfo_free_image(&im_mask);
     sinfo_free_image(&im_on_sub);
  
   } else {
      check_nomsg(im_on_ind = cpl_image_duplicate(im_on_sub));
      sinfo_free_image(&im_on_sub);
   }

   if(pdensity > 1 && strcmp(plugin_id,"sinfo_rec_distortion")!=0) {
     ck0(sinfo_pro_save_ima(im_on_ind,ref_set,sof,cfg->fitsname,
			    PRO_MASTER_SLIT,NULL,plugin_id,config),
	 "cannot save ima %s", cfg->fitsname);
   }
 
   /*
   #---------------------------------------------------------
   # do the north - south - test
   #---------------------------------------------------------
   */
   sinfo_msg("Do the north - south - test");

   cknull(distances = sinfo_north_south_test(im_on_ind, 
                       cfg->nslits,
                       cfg->halfWidth,
                       cfg->fwhm ,
                       cfg->minDiff,
                       cfg->estimated_dist,
                                           cfg->devtol, 
                                           IMA_PIX_START, 
                                           IMA_PIX_END ),
      "North South Test distance determination failed");
   sinfo_free_image(&im_on_ind);
    /*
   sinfo_new_parameter_to_ascii(distances, cfg->nslits - 1, cfg->outName);
   */
   check_nomsg(tbl_dist = cpl_table_new(cfg->nslits - 1));
   check_nomsg(cpl_table_new_column(tbl_dist,"slitlet_distance",
				    CPL_TYPE_FLOAT));
   check_nomsg(cpl_table_copy_data_float(tbl_dist,"slitlet_distance",
					 distances));

   strcpy(tbl_name,cfg->outName);

   check_nomsg(qclog_tbl = cpl_table_new(cfg->nslits + 1));
   check_nomsg(cpl_table_new_column(qclog_tbl,"key_name", CPL_TYPE_STRING));
   check_nomsg(cpl_table_new_column(qclog_tbl,"key_type", CPL_TYPE_STRING));
   check_nomsg(cpl_table_new_column(qclog_tbl,"key_value", CPL_TYPE_STRING));
   check_nomsg(cpl_table_new_column(qclog_tbl,"key_help", CPL_TYPE_STRING));

   check_nomsg(qc_dist=cpl_vector_new(cfg->nslits - 1));
 
   for(i=0;i<cfg->nslits - 1;i++) {
         snprintf(key_name,MAX_NAME_SIZE-1,"%s%i","QC SL DIST",i);
         cpl_table_set_string(qclog_tbl,"key_name",i,key_name);
         cpl_table_set_string(qclog_tbl,"key_type",i,"CPL_TYPE_DOUBLE");
         snprintf(key_value,MAX_NAME_SIZE-1,"%g",distances[i]);
         cpl_table_set_string(qclog_tbl,"key_value",i,key_value);
         cpl_table_set_string(qclog_tbl,"key_help",i,"Slitlet distance");

         cpl_vector_set(qc_dist,i,distances[i]);
   }
   check_nomsg(qc_dist_mean=cpl_vector_get_mean(qc_dist));
   check_nomsg(qc_dist_stdev=cpl_vector_get_stdev(qc_dist));

   cpl_table_set_string(qclog_tbl,"key_name",cfg->nslits-1,"QC SL DISTAVG");
   cpl_table_set_string(qclog_tbl,"key_type",cfg->nslits-1,"CPL_TYPE_DOUBLE");
   snprintf(key_value,MAX_NAME_SIZE-1,"%g",cpl_vector_get_mean(qc_dist));
   cpl_table_set_string(qclog_tbl,"key_value",cfg->nslits-1,key_value);
   cpl_table_set_string(qclog_tbl,"key_help",cfg->nslits-1,
                                  "Average Slitlet distance");

   cpl_table_set_string(qclog_tbl,"key_name",cfg->nslits,"QC SL DISTRMS");
   cpl_table_set_string(qclog_tbl,"key_type",cfg->nslits,"CPL_TYPE_DOUBLE");
   snprintf(key_value,MAX_NAME_SIZE-1,"%g",qc_dist_stdev);
   cpl_table_set_string(qclog_tbl,"key_value",cfg->nslits,key_value);
   cpl_table_set_string(qclog_tbl,"key_help",cfg->nslits,
                        "RMS Slitlet distance");

   ck0(sinfo_pro_save_tbl(tbl_dist,ref_set,sof,tbl_name,
           PRO_SLITLETS_DISTANCE,qclog_tbl,plugin_id,config),
       "cannot dump tbl %s", tbl_name);

   sinfo_free_my_vector(&qc_dist);
   sinfo_free_table(&tbl_dist);
   sinfo_free_table(&qclog_tbl);
   sinfo_free_float(&distances);
   sinfo_ns_free (&cfg);
   sinfo_free_frameset(&raw);

  return 0;

  cleanup:
  sinfo_free_my_vector(&qc_dist);
  sinfo_free_table(&tbl_dist);
  sinfo_free_table(&qclog_tbl);
  sinfo_free_float(&distances);
  sinfo_free_table(&tbl_dist);
  sinfo_free_table(&qclog_tbl);
  sinfo_free_imagelist(&list_object);
  sinfo_free_imagelist(&list_off);
  sinfo_free_image(&im_on);
  sinfo_free_image(&im_mask);
  sinfo_free_image(&im_on_gauss);
  sinfo_free_image(&im_on_sub);
  sinfo_free_image(&im_off);
  sinfo_free_image(&im_on_ind);
  sinfo_ns_free (&cfg);
  sinfo_free_frameset(&raw);
  return -1;


}

/**@}*/


