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


}

/**@}*/


