/*                                                                            *
 *   This file is part of the ESO SINFO 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: 2013-08-15 11:49:35 $
 * $Revision: 1.10 $
 * $Name: not supported by cvs2svn $
 */

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <cpl.h>
#include <cpl_test.h>
#include <sinfo_pro_save.h>
#include <sinfo_utilities_scired.h>
#include <sinfo_error.h>
#include <sinfo_msg.h>
#include <sinfo_new_cube_ops.h>
#include <sinfo_utils_wrappers.h>
/*-----------------------------------------------------------------------------
                                Defines
 -----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------*/
/**
 * @defgroup sinfo_cube_coadd_test  SINFO library unit tests
 */
/*----------------------------------------------------------------------------*/
/**@{*/
   

/**
   @brief      test cube coaddition compuatation
 */
/*---------------------------------------------------------------------------*/
#define SIZE 255
static cpl_imagelist*
sinfo_cube_gen_gauss2d_with_noise(const int sx, 
                                 const int sy, 
                                 const int sz, 
				 const double xc,
				 const double yc,
                                 const double sigx,
                                 const double sigy,
                                 const double min_noise,
                                 const double max_noise)
{

  int z=0;
  double a0=1;
  double a1=-2;
  double a2=1;
  double x=100;
  char name[SIZE];

  sinfo_msg_warning("Prepare data cube with 2D Gauss");
 
  cpl_imagelist* cube=cpl_imagelist_new();
  for(z=0;z<sz;z++) {
    double y =a0+a1*x*z+a2*x*z*z;
    cpl_image* img_o=cpl_image_new(sx,sy,CPL_TYPE_FLOAT);
    cpl_image* img_s=cpl_image_new(sx,sy,CPL_TYPE_FLOAT);
    cpl_image_fill_gaussian(img_o,xc,yc,y,sigx,sigy);
    cpl_image_fill_noise_uniform(img_s,min_noise,max_noise);
    cpl_image_add(img_o,img_s);
    sinfo_free_image(&img_s);
    sprintf(name,"%s%d%s","img_",z,".fits");
    cpl_image_save(img_o,name,CPL_BPP_IEEE_FLOAT,
			       NULL,CPL_IO_DEFAULT);
    cpl_imagelist_set(cube,img_o,z);
  }

  return cube;

}

static cpl_error_code
sinfo_cube_prepare_cube_aux(double* times, float* offx, float* offy,
                            const int nc)
{
    double off = 5;
    double exptime = 1;

    for(int i = 0;i < nc; i++) {
        times[i] = exptime;
        offx[i] =+ off*i;
        offy[i] =- off*i;
    }
    return cpl_error_get_code();
}


static cpl_error_code
sinfo_cube_prepare_cube_frames(cpl_imagelist** cube_bas,
                               const int sx, const int sy, const int sz,
                               const int nc,
                               double* times, float* offx, float* offy)
{

    char name[SIZE];
    cpl_propertylist* plist=NULL;
    double xc0 = 0.5 * sx;
    double yc0 = 0.5 * sy;
    double sigx = sx / 16;
    double sigy = sy / 16;
    double min_noise=-1;
    double max_noise=+1;
    double mjd_obs=56942.32674796;
    for(int i=0;i<nc;i++) {
        double xc=xc0+offx[i];
        double yc=yc0+offy[i];

        sinfo_msg_warning("xc0= %f %f, offset = %f %f xc= %f %f",
                          xc0,yc0,offx[i],offy[i],xc,yc);
        plist=cpl_propertylist_new();
        cpl_propertylist_append_double(plist,KEY_NAME_CUMOFFX,offx[i]);
        cpl_propertylist_append_double(plist,KEY_NAME_CUMOFFY,offy[i]);
        cpl_propertylist_append_double(plist,"EXPTIME",times[i]);
        cpl_propertylist_append_double(plist,KEY_NAME_MJD_OBS,mjd_obs);
        cube_bas[i]=sinfo_cube_gen_gauss2d_with_noise(sx,sy,sz,xc,yc,
                        sigx,sigy,min_noise,max_noise);
        sprintf(name,"%s%d%s","cub_bas",i,".fits");
        cpl_imagelist_save(cube_bas[i],name,CPL_BPP_IEEE_FLOAT,plist,CPL_IO_DEFAULT);
        sinfo_free_propertylist(&plist);

    }
    return cpl_error_get_code();

}

/**
   @brief      test cube coaddition compuatation
 */
/*---------------------------------------------------------------------------*/
#define SIZE 255

cpl_imagelist*
sinfo_coadd_cubes(const int rsx, const int rsy, const int nc,
                  cpl_imagelist** cube_obj,
                  float* offx, float* offy, double* times)
{
    int onp;
    int z_stp=8;
    int z_siz=0;
    int z_min=0;
    int z_max=0;
    int kappa=2;
    char name[SIZE];
    cpl_image* img_j=NULL;
    cpl_image* img_m=NULL;
    cpl_imagelist* cube_msk = cpl_imagelist_new();
    cpl_imagelist* cube_out = cpl_imagelist_new();
    onp = cpl_imagelist_get_size(cube_obj[0]);

    sinfo_msg_warning("Coadd data");
    for (int 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 (int j = z_min; j < z_max; j++) {
            img_j = cpl_image_new(rsx, rsy, CPL_TYPE_FLOAT);
            cpl_imagelist_set(cube_out, img_j, j);
            img_m = cpl_image_new(rsx, rsy, CPL_TYPE_FLOAT);
            cpl_imagelist_set(cube_msk, img_m, j);
        }

        sinfo_new_combine_jittered_cubes_thomas_range(cube_obj, cube_out,
                        cube_msk, nc, offx, offy, times, "tanh", z_min, z_max,
                        kappa);

    }
    sinfo_msg_warning("Save final products");
    sprintf(name, "%s", "cub_pro.fits");
    cpl_imagelist_save(cube_out,name,CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
    sprintf(name, "%s", "cub_pro_msk.fits");
    cpl_imagelist_save(cube_msk,name,CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
    sinfo_free_imagelist(&cube_msk);
    sinfo_free_imagelist(&cube_out);
    return cube_msk;
}

static void
test_cube_coadd_offset_xy_dump(const float* offx,const float* offy,const int n)
{
    for(int i=0;i<n;i++){
        sinfo_msg_warning("offx=%g offy=%g",offx[i],offy[i]);
    }
    return;
}
static void
test_cube_coadd_gauss2d(void)
{
  int sx=64;
  int sy=64;
  int sz=16;
  int nc=3;
  int i=0;

  int vllx=0;
  int vlly=0;
  int vurx=0;
  int vury=0;

  double * times=NULL ;
  float * offx=NULL;
  float * offy=NULL;

  float ref_offx=0;
  float ref_offy=0;
  int rsx=sx;
  int rsy=sy;

  cpl_imagelist** cube_bas=NULL;
  cpl_imagelist** cube_obj=NULL;
  cpl_imagelist** cube_tmp=NULL;

  char name[SIZE];
 
  sinfo_msg_warning("Prepare data cubes with 2D Gauss");
  times = (double*) cpl_calloc (nc, sizeof (double));
  offx = (float*) cpl_calloc (nc, sizeof(float));
  offy = (float*) cpl_calloc (nc, sizeof(float));
  sinfo_cube_prepare_cube_aux(times, offx, offy, nc);

  cube_bas = (cpl_imagelist**) cpl_calloc (nc,sizeof (cpl_imagelist*));
  sinfo_cube_prepare_cube_frames(cube_bas, sx, sy, sz, nc, times, offx, offy);


  sinfo_auto_size_cube(offx, offy, nc, &ref_offx, &ref_offy, &rsx, &rsy);

  sinfo_msg_warning("Output cube size: %d x %d offset: %g, %g",
                    rsx,rsy,ref_offx,ref_offy);
  test_cube_coadd_offset_xy_dump(offx,offy,nc);
  /* AMO: found that this setting does not work because the correction by
   * reference offset is not proper. Need to comment out to have proper result

  for ( i = 0 ; i < nc ; i++ ) {
      sprintf(name,"%s%d%s","cub_bas",i,".fits");
      sinfo_assign_offset_usr(i,name,offx,offy,ref_offx,ref_offy);
  }
  */
  sinfo_msg_warning("after");
  test_cube_coadd_offset_xy_dump(offx,offy,nc);


  sinfo_msg_warning("Allocate memory to create coadded cube");
  cknull(cube_tmp = (cpl_imagelist**) cpl_calloc (nc, sizeof (cpl_imagelist*)),
	 "Could not allocate memory for cube_tmp");

  cknull(cube_obj = (cpl_imagelist**) cpl_calloc (nc,sizeof (cpl_imagelist*)),
	 "Could not allocate memory for cubeobject");

  sinfo_msg_warning("Reload data");

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

    sprintf(name,"%s%d%s","cub_bas",i,".fits");
    check_nomsg(cube_tmp[i] = cpl_imagelist_load(name,CPL_TYPE_FLOAT,0));
    sprintf(name,"%s%d%s","cub_load",i,".fits");
    check_nomsg(cpl_imagelist_save(cube_tmp[i],name,CPL_BPP_IEEE_FLOAT,
				   NULL,CPL_IO_DEFAULT));

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

    sprintf(name,"%s%d%s","cub_object",i,".fits");
    check_nomsg(cpl_imagelist_save(cube_obj[i],name,CPL_BPP_IEEE_FLOAT,
				   NULL,CPL_IO_DEFAULT));
  }

  sinfo_coadd_cubes(rsx, rsy, nc, cube_obj, offx, offy, times);


 cleanup:

  sinfo_free_float(&offy);
  sinfo_free_float(&offx);
  sinfo_free_double(&times);

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

     sinfo_free_imagelist(&cube_bas[i]);

  }
  sinfo_free_array_imagelist(&cube_bas);

  //Free the memory
  for ( i = 0 ; i <nc  ; i++ ) {
      sinfo_free_imagelist(&cube_obj[i]);
  }
  sinfo_free_array_imagelist(&cube_obj);

  sinfo_free_array_imagelist(&cube_tmp);

  return;

}






/**
   @brief      test cube coaddition compuatation
 */
/*---------------------------------------------------------------------------*/
/* TODO: temporarily commented out before we find a way to generate a cube i
   with the unit test itself

static void
test_cube_coadd_data(void)
{
  int sx=64;
  int sy=64;
  int nc=1;
  int i=0;
  int z=0;


  int onp=0;


  int z_siz=0;
  int z_stp=100;
  int z_min=0;
  int z_max=0;
  int kappa=2;
  int j=0;

  int vllx=0;
  int vlly=0;
  int vurx=0;
  int vury=0;

  double * times=NULL ;
  float * offx=NULL;
  float * offy=NULL;

  cpl_imagelist* cube_out=NULL;
  cpl_imagelist* cube_tst=NULL;

  cpl_imagelist** cube_bas=NULL;
  cpl_imagelist* cube_msk=NULL;

  cpl_imagelist** cube_obj=NULL;
  cpl_imagelist** cube_tmp=NULL;


  cpl_image* img_o=NULL;
  cpl_image* img_s=NULL;
  cpl_image* img_m=NULL;
  cpl_image* img_j=NULL;

  char name[SIZE];
  //char* src_data="/data2/sinfoni/sinfo_demo/pro/sinfo_rec_jitter_objnod_358/";
  char* src_data="/data2/sinfoni/sinfo_demo/test/ref/";


  sinfo_msg_warning("allocate memory");
  cknull(times = (double*) cpl_calloc (nc, sizeof (double)),
	 " could not allocate memory!") ;
 
  cknull(offx = (float*) cpl_calloc (nc, sizeof(float)),
	 " could not allocate memory!") ;

  cknull(offy = (float*) cpl_calloc (nc, sizeof(float)),
	 " could not allocate memory!") ;


  //We use input data generated by the pipeline
  sprintf(name,"%s%s%2.2d%s",src_data,"out_cube_obj",i,"_cpl40.fits");
  //sprintf(name,"%s%s%4.4d%s",src_data,"out_",12,".fits");
  sinfo_msg_warning("name=%s",name);

  check_nomsg(cube_tst = cpl_imagelist_load(name,CPL_TYPE_FLOAT,0));
  sprintf(name,"%s","cub_tst.fits");
  check_nomsg(cpl_imagelist_save(cube_tst,name,CPL_BPP_IEEE_FLOAT,
				 NULL,CPL_IO_DEFAULT));

  cknull(cube_tmp = (cpl_imagelist**) cpl_calloc (nc, sizeof (cpl_imagelist*)),
	 "Could not allocate memory for cube_tmp");

  cknull(cube_obj = (cpl_imagelist**) cpl_calloc (nc,sizeof (cpl_imagelist*)),
	 "Could not allocate memory for cubeobject");

  sinfo_msg_warning("Load pipe data");

  for(i=0;i<nc;i++) {
    times[i]=600;
    offx[i]=0;
    offy[i]=0;

    sprintf(name,"%s%s%2.2d%s",src_data,"out_cube_obj",i,"_cpl40.fits");
    //sprintf(name,"%s%s%4.4d%s",src_data,"out_",12,".fits");
    sinfo_msg("name=%s",name);
    check_nomsg(cube_tmp[i] = cpl_imagelist_load(name,CPL_TYPE_FLOAT,0));


    sprintf(name,"%s%d%s","cub_load",i,".fits");
    check_nomsg(cpl_imagelist_save(cube_tmp[i],name,CPL_BPP_IEEE_FLOAT,
				   NULL,CPL_IO_DEFAULT));

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

    sprintf(name,"%s%d%s","cub_object",i,".fits");
    check_nomsg(cpl_imagelist_save(cube_obj[i],name,CPL_BPP_IEEE_FLOAT,
                                   NULL,CPL_IO_DEFAULT));
  }


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

  check_nomsg(onp=cpl_imagelist_get_size(cube_obj[0]));

  sinfo_msg_warning("Shift pipe data");

  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(img_j=cpl_image_new(sx,sy,CPL_TYPE_FLOAT));
      check_nomsg(cpl_imagelist_set(cube_out,img_j,j)); 
      check_nomsg(img_m = cpl_image_new(sx,sy,CPL_TYPE_FLOAT));
      check_nomsg(cpl_imagelist_set(cube_msk,img_m,j));
    }

    sinfo_new_combine_jittered_cubes_thomas_range(cube_obj,
						  cube_out,
						  cube_msk,
						  nc,
						  offx,
                                                  offy,
						  times,
						  "tanh",
						  z_min,
						  z_max,
						  kappa);



  }

  sinfo_msg_warning("Save final products");

  sprintf(name,"%s","cub_pro.fits");
  check_nomsg(cpl_imagelist_save(cube_out,name,CPL_BPP_IEEE_FLOAT,
				 NULL,CPL_IO_DEFAULT));

  sprintf(name,"%s","cub_pro_msk.fits");
  check_nomsg(cpl_imagelist_save(cube_msk,name,CPL_BPP_IEEE_FLOAT,
				 NULL,CPL_IO_DEFAULT));


 cleanup:

  sinfo_free_float(&offy);
  sinfo_free_float(&offx);
  sinfo_free_double(&times);

  sinfo_free_image(&img_s);
  sinfo_free_image(&img_o);
  sinfo_free_imagelist(&cube_out);
  sinfo_free_imagelist(&cube_msk);

  for ( i = 0 ; i <nc  ; i++ ) {
      sinfo_free_imagelist(&cube_bas[i]);
  }
  sinfo_free_array_imagelist(&cube_bas);


  //Free the memory
  for ( i = 0 ; i <nc  ; i++ ) {
      sinfo_free_imagelist(&cube_obj[i]);
  }
  sinfo_free_array_imagelist(&cube_obj);

  sinfo_free_array_imagelist(&cube_tmp);
  sinfo_free_imagelist(&cube_tst);

  return;

}
*/

/*----------------------------------------------------------------------------*/
/**
  @brief   SINFONI pipeline unit test for cube coadd

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

int main(void)
{
  cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_WARNING);

  /* Initialize CPL + SINFO messaging */
  check(test_cube_coadd_gauss2d(),"Fail testing cube_gauss2d");
  /* TODO: find a way to generate a cube with the UNIT test so that
           we can test the following
  check(test_cube_coadd_data(),"Fail testing cube_gauss2d");
  */
 cleanup:
  return cpl_test_end(0);

}


/**@}*/
