/*
 * 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    :       sinfo_new_cubes_coadd.c
Author       :    J. Schreiber
Created on   :    December 3, 2003
Description  :    Creates data cubes or merges data cubes
out of jittered object-sky
nodding observations
---------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*----------------------------------------------------------------------------
  Includes
  ---------------------------------------------------------------------------*/
#include "sinfo_new_cubes_coadd.h"
#include "sinfo_pfits.h"
#include "sinfo_baryvel.h"
#include "sinfo_pro_save.h"
#include "sinfo_objnod_ini_by_cpl.h"
#include "sinfo_functions.h"
#include "sinfo_utilities_scired.h"
#include "sinfo_wave_calibration.h"
#include "sinfo_cube_construct.h"
#include "sinfo_error.h"
#include "sinfo_utils_wrappers.h"
//Used only for cpl_propertylist_has
#include "sinfo_dfs.h"
/*----------------------------------------------------------------------------
  Defines
  ---------------------------------------------------------------------------*/
#define PI_NUMB        (3.1415926535897932384626433832795) /* pi */


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

/**@{*/
/**
 * @addtogroup sinfo_rec_jitter Cube construction
 *
 * TBD
 */


/*----------------------------------------------------------------------------
  Function     : sinfo_new_cubes_coadd()
  In           : ini_file: file name of according .ini file
  Out          : integer (0 if it worked, -1 if it doesn't)
  Job          : this routine carries through the data cube creation of an
  object science observation using object-sky nodding
  and jittering. This script expects jittered frames that
  were already sky-subtracted
  averaged, flatfielded, spectral tilt corrected and
  interleaved if necessary
  ---------------------------------------------------------------------------*/
int
sinfo_new_cubes_coadd (const char* plugin_id,cpl_parameterlist* config,
                       cpl_frameset* sof, const char* procatg)
{

    object_config * cfg=NULL ;
    cpl_image * im=NULL ;
    cpl_image* jitter_image=NULL;
    cpl_imagelist  ** cube_tmp=NULL ;

    cpl_imagelist  ** cubeobject=NULL ;
    cpl_imagelist  * jittercube=NULL ;
    cpl_imagelist  * maskcube=NULL ;
    cpl_propertylist* plist=NULL;
    int sky_cor=0;
    int pdensity=0;

    int i=0;
    int n=0 ;
    /* int partind = 0 ; */
    int centralpix=0 ;
    int z_siz=0;
    int z_min=0;
    int z_max=0;
    int z=0;
    int z_stp=100;
    int scales_sky=0;
    int ks_clip=0;
    double kappa=2.0;

    float ref_offx=0;
    float ref_offy=0;
    float newcenter_x=0 ;
    float newcenter_y=0 ;
    /*
   float cd1_1=0;
   float cd1_2=0;
   float cd2_1=0;
   float cd2_2=0;
   double pixelscale=0;
     */
    //double angle=0;
    //float radangle=0 ;
    double exptime=0 ;

    double * times=NULL ;
    float * offsetx=NULL;
    float * offsety=NULL;
    float offx_min=1.e10;
    float offy_min=1.e10;
    float offx_max=-1.e10;
    float offy_max=-1.e10;

    double dis=0 ;
    double centralLambda=0 ;
    double mjd_obs=0;

    char name_jitter[MAX_NAME_SIZE] ;
    char pro_mjit[MAX_NAME_SIZE];
    char pro_obs[MAX_NAME_SIZE];
    char pro_med[MAX_NAME_SIZE];


    char * name=NULL ;
    char * partname=NULL;
    /* char * partname2=NULL ; */
    char file_name[MAX_NAME_SIZE];
    int vllx=0;
    int vlly=0;
    int vurx=0;
    int vury=0;

    int onp=0;
    int j=0;
    cpl_image* j_img=NULL;
    cpl_image* m_img=NULL;
    cpl_table* qclog_tbl=NULL;
    cpl_image* ill_cor=NULL;
    cpl_frame* frame=NULL;
    cpl_frameset* stk=NULL;
    cpl_parameter* p=NULL;
    int mosaic_max_size=0;
    double barycor=0;
    double helicor=0;
    cpl_table* qclog=NULL;

    check_nomsg(p=cpl_parameterlist_find(config,
                    "sinfoni.objnod.mosaic_max_size"));
    check_nomsg(mosaic_max_size=cpl_parameter_get_int(p));
    check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.product.density"));
    check_nomsg(pdensity=cpl_parameter_get_int(p));

    if (strcmp(procatg,PRO_COADD_STD) == 0) {
        strcpy(pro_mjit,PRO_MASK_COADD_STD);
        strcpy(pro_obs,PRO_OBS_STD);
        strcpy(pro_med,PRO_MED_COADD_STD);

    } else if (strcmp(procatg,PRO_COADD_PSF) == 0) {
        strcpy(pro_mjit,PRO_MASK_COADD_PSF);
        strcpy(pro_obs,PRO_OBS_PSF);
        strcpy(pro_med,PRO_MED_COADD_PSF);
    } else {
        strcpy(pro_mjit,PRO_MASK_COADD_OBJ);
        strcpy(pro_obs,PRO_OBS_OBJ);
        strcpy(pro_med,PRO_MED_COADD_OBJ);
    }

    /*----parse input data and parameters to set cube_config cfg---*/
    check_nomsg(stk = cpl_frameset_new());

    cknull(cfg = sinfo_parse_cpl_input_objnod(config,sof,&stk),
           "Error setting parameter configuration");

    ck0(sinfo_check_input_data(cfg),"error checking input");

    if ( cfg->jitterind == 1 )
    {
        cknull(times = (double*) cpl_calloc (cfg->nframes, sizeof (double)),
                        " could not allocate memory!") ;

        cknull(offsetx = (float*) cpl_calloc (cfg->nframes, sizeof(float)),
               " could not allocate memory!") ;

        cknull(offsety = (float*) cpl_calloc (cfg->nframes, sizeof(float)),
               " could not allocate memory!") ;
    }

    if (cfg->jitterind == 0)
    {
        if ( NULL != (partname = strtok(cfg->outName, ".")))
        {
            /* partname2 = strtok (NULL, ".") ; */
            /* partind = 1 ; */
        }
    }

    ck0(sinfo_auto_size_cube_new(cfg,&ref_offx,&ref_offy,
                    &offx_min,&offy_min,
                    &offx_max,&offy_max),
        "Error resizing cube");

    if(NULL != cpl_frameset_find(sof,PRO_ILL_COR)) {
        frame = cpl_frameset_find(sof,PRO_ILL_COR);
        ill_cor=cpl_image_load(cpl_frame_get_filename(frame),CPL_TYPE_FLOAT,0,0);
    } else {
        sinfo_msg("Illumination correction image not provided");
        cpl_error_reset();
    }

    for ( n = 0 ; n < cfg->nframes ; n++ )
    {

        sinfo_msg_debug("Read FITS information");
        name = cfg->framelist[n] ;
        if (n == 0)
        {
            strcpy (name_jitter, name) ;
        }
        if( sinfo_is_fits_file(name) != 1) {
            sinfo_msg_error("Input file %s is not FITS",name);
            goto cleanup;
        }


        /* get some header values and compute the CD-sinfo_matrix */
        plist=cpl_propertylist_load(name,0);

        if (cpl_propertylist_has(plist, KEY_NAME_MJD_OBS)) {
            mjd_obs=cpl_propertylist_get_double(plist, KEY_NAME_MJD_OBS);
        } else {
            sinfo_msg_error("keyword %s does not exist",KEY_NAME_MJD_OBS);
            cpl_propertylist_delete(plist) ;
            return -1;
        }


        //pixelscale = sinfo_pfits_get_pixscale(plist) /2;
        //angle = sinfo_pfits_get_posangle(plist) ;
        /* in PUPIL data there is not posangle info: we reset the error */
        if(cpl_error_get_code() != CPL_ERROR_NONE) {
            cpl_error_reset();
        }

        sinfo_free_propertylist(&plist);
        // The following 5 lines are not really used here!
        /*
      radangle = angle * PI_NUMB / 180. ;
      cd1_1 = cos(radangle) ;
      cd1_2 = sin(radangle) ;
      cd2_1 = -sin(radangle) ;
      cd2_2 = cos(radangle) ;
         */

        sinfo_msg_debug("frame no.: %d, name: %s\n", n, name) ;
        cknull(im = cpl_image_load(name,CPL_TYPE_FLOAT,0,0),
               " could not load frame %s!",name) ;

        if (cfg->jitterind == 1)
        {
            exptime = sinfo_pfits_get_ditndit(name) ;

            if (exptime == FLAG)
            {
                sinfo_msg_error("could not read fits header keyword exptime!");
                goto cleanup;
            }
            times[n] = exptime ;

            ck0(sinfo_assign_offset_from_fits_header(n,name,offsetx,offsety,
                            ref_offx,ref_offy),
                "Error assigning offsets");
            //sinfo_msg("assigned offset: %g %g",offsetx[n],offsety[n]);
        }
        sinfo_free_image(&im);

    } /* end loop over n (nframes) */


    /* leak free */
    if(cfg->jitterind == 0) {
        goto exit;
    }

    /* Here in case of autojitter we estimate the sky */
    if( (cfg->size_x*cfg->size_y*cfg->nframes) > (100*mosaic_max_size) ) {
        sinfo_msg_warning("Coadd cube size:%d,%d. N frames: %d",
                        cfg->size_x,cfg->size_y,cfg->nframes);
        sinfo_msg_warning("Max allowed should be such "
                        "that sixeX*sixeY*Nframes < 100*%d",mosaic_max_size);
        goto exit;
    }

    if ( cfg->jitterind == 1 )
    {
        check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.objnod.vllx"));
        check_nomsg(vllx = cpl_parameter_get_int(p));
        check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.objnod.vlly"));
        check_nomsg(vlly = cpl_parameter_get_int(p));
        check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.objnod.vurx"));
        check_nomsg(vurx = cpl_parameter_get_int(p));
        check_nomsg(p = cpl_parameterlist_find(config, "sinfoni.objnod.vury"));
        check_nomsg(vury = cpl_parameter_get_int(p));
        cknull(cube_tmp = (cpl_imagelist**) cpl_calloc (cfg->nframes,
                        sizeof (cpl_imagelist*)),
                        "Could not allocate memory for cube_tmp");


        cknull(cubeobject = (cpl_imagelist**) cpl_calloc (cfg->nframes,
                        sizeof (cpl_imagelist*)),
                        "Could not allocate memory for cubeobject");
        check_nomsg(p=cpl_parameterlist_find(config,
                        "sinfoni.objnod.sky_cor"));
        check_nomsg(sky_cor=cpl_parameter_get_bool(p));


        for ( n = 0 ; n < cfg->nframes ; n++ ) {
            if(sky_cor == 1 && (pdensity == 1 || pdensity == 3) &&
                            strcmp(cfg->sky_dist,"no_sky")!=0) {
                sinfo_msg("load sky corrected cubes");

                snprintf(file_name,MAX_NAME_SIZE-1,"%s%2.2d%s","out_cube_obj_cor",
                         n,".fits");

            } else {
                snprintf(file_name,MAX_NAME_SIZE-1,"%s%2.2d%s","out_cube_obj",n,
                                ".fits");
            }
            check_nomsg(cube_tmp[n] = cpl_imagelist_load(file_name,
                            CPL_TYPE_FLOAT,0));


            check_nomsg(cubeobject[n] = sinfo_new_cube_getvig(
                            cube_tmp[n],
                            1+vllx,1+vlly,
                            64 - vurx, 64 - vury));
            check_nomsg(sinfo_free_imagelist(&cube_tmp[n]));

        }
        sinfo_free_array_imagelist(&cube_tmp);


    }


    /*
     ---------------------------------------------------------------------
     ------------------------JITTERING------------------------------------
     ---------------------------------------------------------------------
     */

    if (cfg->jitterind == 1)
    {
        sinfo_msg("Jittering...");

        sinfo_msg("Coadded cube size. x: %d y: %d",
                  cfg->size_x,cfg->size_y);
        check_nomsg(jittercube = cpl_imagelist_new()) ;


        /*
         ---------------------------------------------------------------------
         -------------------THOMAS ALGORITHM----------------------------------
         ---------------------------------------------------------------------
         */

        check_nomsg(p=cpl_parameterlist_find(config,
                        "sinfoni.objnod.scales_sky"));
        check_nomsg(scales_sky=cpl_parameter_get_bool(p));
        check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.objnod.ks_clip"));
        check_nomsg(ks_clip = cpl_parameter_get_bool(p));
        check_nomsg(p=cpl_parameterlist_find(config,"sinfoni.objnod.kappa"));
        check_nomsg(kappa = cpl_parameter_get_double(p));


        if(scales_sky == 1) {
            sinfo_msg("Subtract spatial sinfo_median to each cube plane");
            for(n=0;n<cfg->nframes;n++) {
                sinfo_msg("Process cube %d\n",n);
                sinfo_new_sinfoni_correct_median_it(&(cubeobject[n]));
            }
        }


        /* AMO CHECK */

        cknull(maskcube=cpl_imagelist_new(),"could not allocate cube!");

        /* Illumination correction */
        if(ill_cor != NULL) {
            for(n=0;n<cfg->nframes;n++) {
                cpl_imagelist_divide_image(cubeobject[n],ill_cor);
            }
        }
        sinfo_free_image(&ill_cor);

        sinfo_msg("Combine jittered cubes");


        if(ks_clip == 1) {
            sinfo_msg("Cube coaddition with kappa-sigma");
        }
        check_nomsg(onp=cpl_imagelist_get_size(cubeobject[0]));
        for(z=0;z<onp;z+=z_stp) {
            z_siz=(z_stp < (onp-z)) ? z_stp : (onp-z);
            z_min=z;
            z_max=z_min+z_siz;
            sinfo_msg("Coadding cubes: range [%4.4d,%4.4d) of 0-%d\n",
                      z_min,z_max,onp);

            for(j=z_min;j<z_max;j++) {
                check_nomsg(j_img=cpl_image_new(cfg->size_x,
                                cfg->size_y,CPL_TYPE_FLOAT));
                check_nomsg(cpl_imagelist_set(jittercube,j_img,j));
                check_nomsg(m_img = cpl_image_new(cfg->size_x,
                                cfg->size_y,CPL_TYPE_FLOAT));
                check_nomsg(cpl_imagelist_set(maskcube,m_img,j));
            }
            if(ks_clip == 1) {
                sinfo_new_combine_jittered_cubes_thomas_range(cubeobject,
                                jittercube,
                                maskcube,
                                cfg->nframes,
                                offsetx,offsety,
                                times,
                                cfg->kernel_type,
                                z_min,
                                z_max,
                                kappa);

            } else {
                sinfo_new_combine_jittered_cubes_range(cubeobject,
                                jittercube,
                                maskcube,
                                cfg->nframes,
                                offsetx,
                                offsety,
                                times,
                                cfg->kernel_type,
                                z_min,
                                z_max) ;
            }
        }
        sinfo_new_convert_0_to_ZERO_for_cubes(jittercube) ;

        if (jittercube == NULL)
        {
            sinfo_msg_error(" could not allocate new data cube!") ;
            goto cleanup;
        }

        if (maskcube == NULL)
        {
            sinfo_msg_error(" could not merge the jittered data cubes\n") ;
            goto cleanup;
        }

        for ( i = 0 ; i <cfg->nframes  ; i++ ) {
            sinfo_free_imagelist(&cubeobject[i]);
            //sinfo_msg("offx[%d]=%f,offy[%d]=%f",i,offsetx[i],i,offsety[i]);
        }
        sinfo_free_array_imagelist(&cubeobject);

        //Old setting
        //newcenter_x = cfg->size_x / 2. + 0.5 ;
        //newcenter_y = cfg->size_y / 2. + 0.5 ;


        /* new setting */
        /*
        sinfo_msg("offx_min=%f,offy_min=%f",offx_min,offy_min);
        sinfo_msg("offx_max=%f,offy_max=%f",offx_max,offy_max);
        sinfo_msg("ref_offx=%f,ref_offy=%f",ref_offx,ref_offy);

        newcenter_x=2*floor(fabs(offx_min)+0.5)+32+8;
        newcenter_y=-2*floor(fabs(offy_max)+0.5)+32-2;


        newcenter_x=cfg->size_x / 2. -offsetx[0];
        newcenter_y=cfg->size_y / 2. -offsety[0];
         */



        if (mjd_obs > 53825. ) {
            /* April 1st 2006 */
            newcenter_x=cfg->size_x / 2. +2*ref_offx;
            newcenter_y=cfg->size_y / 2. +2*ref_offy;
        } else if ((mjd_obs > 53421.58210082 ) && (mjd_obs <= 53825.)){
            /* after detector's upgrade */
            newcenter_x=cfg->size_x / 2. -2*ref_offx;
            newcenter_y=cfg->size_y / 2. +2*ref_offy;
        } else {
            /* before detector's upgrade */
            newcenter_x=cfg->size_x / 2. +2*ref_offx;
            newcenter_y=cfg->size_y / 2. -2*ref_offy;
        }


        //sinfo_msg("CRPIX=%f,%f", newcenter_x, newcenter_y);
        //newcenter_x=2*floor(fabs(ref_offx)+0.5);
        //newcenter_y=-2*floor(fabs(ref_offy)+0.5);
        //sinfo_msg_warning("Center image: %f %f",newcenter_x,newcenter_y);
        cknull_nomsg(qclog = sinfo_qclog_init());
        plist=cpl_propertylist_load(file_name,0);
        if(sinfo_baryvel(plist, &barycor,&helicor) != CPL_ERROR_NONE) {
            sinfo_msg_warning("Could not compute velocity corrections");
            cpl_error_reset();
        } else {

            check_nomsg(sinfo_qclog_add_double(qclog,
                            "QC VRAD BARYCOR",
                            barycor,
                            "Barycentric radial velocity correction ",
                            "%13.6f"));

            check_nomsg(sinfo_qclog_add_double(qclog,
                            "QC VRAD HELICOR",
                            helicor,
                            "Heliocentric radial velocity correction ",
                            "%13.6f"));
            sinfo_msg("Barycor=%g Helicor=%g",barycor,helicor);

            sinfo_free_propertylist(&plist);

        }

        ck0(sinfo_pro_save_ims(jittercube,sof,sof,cfg->outName,
                        procatg,qclog,plugin_id,config),
            "cannot save cube %s", cfg->outName);

        sinfo_free_table(&qclog);

        /* we need to set again the following 3 */
        plist=cpl_propertylist_load(file_name,0);
        dis=sinfo_pfits_get_cdelt3(plist);
        centralLambda=sinfo_pfits_get_crval3(plist);
        centralpix=sinfo_pfits_get_crpix3(plist);
        sinfo_free_propertylist(&plist);
        sinfo_new_set_wcs_cube(jittercube, cfg->outName,
                               centralLambda, dis,
                               centralpix, newcenter_x, newcenter_y);

        cknull(jitter_image = sinfo_new_median_cube(jittercube),
               " could not do sinfo_medianCube()");
        cknull_nomsg(qclog = sinfo_qclog_init());
        update_bad_pixel_map(jitter_image);
        check_nomsg(sinfo_qclog_add_double(qclog,
                        "QC FRMDIF MEANSTD",
                        cpl_image_get_mean(jitter_image),
                        "mean of the collapesd cube",
                        "%13.6f"));
        check_nomsg(sinfo_qclog_add_double(qclog,
                        "QC FRMDIF STDEV",
                        cpl_image_get_stdev(jitter_image),
                        "standard deviation of the collapesd cube",
                        "%13.6f"));
        ck0(sinfo_pro_save_ima(jitter_image,sof,sof,cfg->med_cube_name,
                        pro_med,qclog,plugin_id,config),
            "cannot save ima %s", cfg->outName);
        sinfo_free_table(&qclog);

        sinfo_new_set_wcs_image(jitter_image, cfg->med_cube_name,
                                newcenter_x,newcenter_y);

        sinfo_free_image(&jitter_image);

        ck0(sinfo_pro_save_ims(maskcube,sof,sof,cfg->maskname,
                        pro_mjit,NULL,plugin_id,config),
            "cannot save cube %s", cfg->maskname);

        sinfo_new_set_wcs_cube(maskcube, cfg->maskname,
                               centralLambda, dis, centralpix,
                               newcenter_x, newcenter_y);

        sinfo_free_double(&times) ;
        sinfo_free_float(&offsetx) ;
        sinfo_free_float(&offsety) ;
        sinfo_free_imagelist(&maskcube) ;
        sinfo_free_imagelist(&jittercube) ;

    } /* end of jittering */

    exit:

    /* free memory */
    sinfo_objnod_free(&cfg);
    sinfo_free_frameset(&stk);
    return 0;

    cleanup:

    sinfo_free_image(&jitter_image);
    sinfo_free_imagelist(&jittercube) ;
    sinfo_free_imagelist(&maskcube) ;
    sinfo_free_table(&qclog);

    if(cfg != NULL) {
        if(cube_tmp != NULL) {
            for ( n = 0 ; n < cfg->nframes ; n++ ) {
                sinfo_free_imagelist(&(cube_tmp[n]));
            }
            sinfo_free_array_imagelist(&cube_tmp);
        }
        if(cubeobject != NULL) {
            for ( n = 0 ; n < cfg->nframes ; n++ ) {
                sinfo_free_imagelist(&(cubeobject[n]));
            }
            sinfo_free_array_imagelist(&cubeobject);
        }

    }
    sinfo_free_table(&qclog_tbl);
    sinfo_free_image(&im);
    sinfo_free_image(&ill_cor);
    sinfo_free_float(&offsety);
    sinfo_free_float(&offsetx);
    sinfo_free_double(&times);
    sinfo_objnod_free(&cfg);
    sinfo_free_frameset(&stk);

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        sinfo_check_rec_status(0);
    }
    return -1;



}

/**@}*/
