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

}


/**
   @brief      test cube coaddition compuatation
 */
/*---------------------------------------------------------------------------*/
#define SIZE 255
static void
test_cube_coadd_gauss2d(void)
{
  int sx=64;
  int sy=64;
  int sz=16;
  int nc=3;
  int i=0;
  int z=0;


  int onp=0;

  double xc0=sx/2;
  double yc0=sy/2;
  double xc=sx/2;
  double yc=sy/2;
  double sigx=sx/16;
  double sigy=sy/16;
  double min_noise=-1;
  double max_noise=+1;
  int z_siz=0;
  int z_stp=8;
  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;
  int off=5;
  double * times=NULL ;
  float * offx=NULL;
  float * offy=NULL;
  float * roffx=NULL;
  float * roffy=NULL;
  float shftx=0;
  float shfty=0;

  float min_offx=0;
  float min_offy=0;
  float max_offx=0;
  float max_offy=0;
  float ref_offx=0;
  float ref_offy=0;
  int rsx=0;
  int rsy=0;

  cpl_imagelist* cube_out=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];
  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!") ;


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

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

  cknull(cube_bas = (cpl_imagelist**) cpl_calloc (nc,sizeof (cpl_imagelist*)),
	 "Could not allocate memory for cube_out");
    
  sinfo_msg_warning("Prepare data cubes with 2D Gauss");
  for(i=0;i<nc;i++) {
    times[1]=1;
    offx[i]=+off*i;
    offy[i]=-off*i;
    xc=xc0+offx[i];
    yc=yc0+offy[i];

    sinfo_msg_warning("xc0= %f %f, offset = %f %f xc= %f %f",
		      xc0,yc0,offx[i],offy[i],xc,yc);
    check_nomsg(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");
    check_nomsg(cpl_imagelist_save(cube_bas[i],name,CPL_BPP_IEEE_FLOAT,
				   NULL,CPL_IO_DEFAULT));

  }



  //Get ref offset
  for ( i = 0 ; i < nc ; i++ ) {
     if(i==0) {
      min_offx=offx[i];
      min_offy=offy[i];
      max_offx=offx[i];
      max_offy=offy[i];
    } else {
      if(offx[i] > max_offx) max_offx=offx[i];
      if(offy[i] > max_offy) max_offy=offy[i];
      if(offx[i] < min_offx) min_offx=offx[i];
      if(offy[i] < min_offy) min_offy=offy[i];
    }
  }
  ref_offx=(min_offx+max_offx)/2;
  ref_offy=(min_offy+max_offy)/2;

  rsx=2*floor(max_offx-min_offx+0.5)+64;
  rsy=2*floor(max_offy-min_offy+0.5)+64;
  sinfo_msg_warning("Output cube size: %d x %d",rsx,rsy);

  //reassign offsets
  for ( i = 0 ; i < nc ; i++ ) {
    shftx=offx[i]-ref_offx;
    shfty=offy[i]-ref_offy;

    sinfo_new_array_set_value(roffx,2*shftx,i);
    sinfo_new_array_set_value(roffy,2*shfty,i);
  }





  sinfo_msg_warning("Allocate memory");

  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));
  }


  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("Coadd 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(rsx,rsy,CPL_TYPE_FLOAT));
      check_nomsg(cpl_imagelist_set(cube_out,img_j,j)); 
      check_nomsg(img_m = cpl_image_new(rsx,rsy,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,
						  roffx,
                                                  roffy,
						  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_float(&roffy);
  sinfo_free_float(&roffx);

  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);

  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);

}


/**@}*/
