/*                                                                              *
 *   This file is part of the ESO UVES 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-08 13:36:07 $
 * $Revision: 1.14 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.13  2013/07/02 12:42:17  amodigli
 * Rename DEBUG to debug_mode to remove compiler error on some platforms (that name is reserved to special compiler options)
 *
 * Revision 1.12  2010/04/27 09:55:01  amodigli
 * fixed call to uves_extract error due to API change
 *
 * Revision 1.11  2009/06/05 05:49:02  amodigli
 * updated init/end to cpl5
 *
 * Revision 1.10  2007/08/30 07:56:05  amodigli
 * fixed some doxygen warnings
 *
 * Revision 1.9  2007/06/22 14:50:11  jmlarsen
 * Expanded, again, interface of uves_save_image()
 *
 * Revision 1.8  2007/06/22 09:33:21  jmlarsen
 * Changed interface of uves_save_image
 *
 * Revision 1.7  2007/06/20 15:50:20  jmlarsen
 * Decrease test images size
 *
 * Revision 1.6  2007/05/25 11:50:32  jmlarsen
 * Re-added ORDER_TRACE_TABLE
 *
 * Revision 1.5  2007/05/23 06:43:23  jmlarsen
 * Removed unused variables
 *
 * Revision 1.4  2007/05/22 14:51:02  jmlarsen
 * Removed unused variables
 *
 * Revision 1.3  2007/05/02 13:20:28  jmlarsen
 * Changed interface of uves_extract_iterate
 *
 * Revision 1.2  2007/04/24 12:50:29  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.1  2007/03/15 12:27:18  jmlarsen
 * Moved unit tests to ./uves/tests and ./flames/tests
 *
 * Revision 1.3  2007/02/27 14:04:14  jmlarsen
 * Move unit test infrastructure to IRPLIB
 *
 * Revision 1.2  2007/02/22 15:38:53  jmlarsen
 * Changed tolerance
 *
 * Revision 1.1  2007/02/21 12:37:41  jmlarsen
 * Added uves_extract test
 *
 * Revision 1.2  2007/01/29 12:17:54  jmlarsen
 * Support setting verbosity from command line
 *
 * Revision 1.1  2006/11/28 08:26:35  jmlarsen
 * Added QC log unit test
 *
 */

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

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <uves_extract.h>
#include <uves_extract_iterate.h>
#include <uves_extract_profile.h>
#include <uves_test_simulate.h>
#include <uves.h>
#include <uves_parameters.h>
#include <uves_pfits.h>
#include <uves_utils_wrappers.h>
#include <uves_utils_polynomial.h>
#include <uves_dfs.h>
#include <uves_chip.h>
#include <uves_error.h>
#include <cpl_test.h>

#include <cpl.h>
/*-----------------------------------------------------------------------------
                                Defines
 -----------------------------------------------------------------------------*/

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


/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_extract_test  UVES extraction unit tests
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*----------------------------------------------------------------------------*/
/**
   @brief test spectrum creation
 */
/*----------------------------------------------------------------------------*/

static cpl_image *
create_spectrum(int nx, int ny, int minorder, int maxorder, const polynomial *order_locations,
		slit_geometry sg, cpl_image **sky_spectrum)
{
  int norders = maxorder - minorder + 1;
  cpl_image *spectrum = cpl_image_new(nx, norders, CPL_TYPE_DOUBLE);
  cpl_image *dummy = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
  uves_iterate_position *pos = NULL;

  cpl_binary *bpm = NULL;
  bool loop_y = false;

  double tot_flux = 3000;

  pos = uves_iterate_new(nx, ny, order_locations, minorder, maxorder, sg);
  *sky_spectrum = cpl_image_new(nx, norders, CPL_TYPE_DOUBLE);

  {
    int x, order;
    for (x = 1; x <= nx; x++)
      for (order = 1; order <= norders; order++)
	{
	  cpl_image_reject(spectrum, x, order);
	  cpl_image_reject(*sky_spectrum, x, order);
	}
  }

  for (uves_iterate_set_first(pos,
			      1, nx,
			      minorder, maxorder,
			      bpm,
			      loop_y);
       !uves_iterate_finished(pos); 
       uves_iterate_increment(pos))
    {
      int spectrum_row = pos->order - minorder + 1;

      double sky = 1000+20000*(pos->order - minorder)*1.0/(maxorder - minorder);

      check_nomsg( cpl_image_set(spectrum     , pos->x, spectrum_row, tot_flux) );
      check_nomsg( cpl_image_set(*sky_spectrum, pos->x, spectrum_row, sky) );

    }
  
 cleanup:
  uves_iterate_delete(&pos);
  uves_free_image(&dummy);
  return spectrum;
}


/*----------------------------------------------------------------------------*/
/**
   @brief test extraction
 */
/*----------------------------------------------------------------------------*/

static void
test_extract(void)
{
  polynomial *order_locations = NULL;
  int minorder = 1;
//  int maxorder = 22;
//  int nx = 3000;
//  int ny = 2000;
  int maxorder = 8;
  int nx = 500;
  int ny = 600;
  slit_geometry sg = {30.0, 0.0};
  cpl_image *in_spectrum = NULL;
  cpl_image *in_sky = NULL;
  cpl_image *image = NULL;
  cpl_image *image_noise = NULL;
  uves_propertylist *image_header = NULL;
  cpl_table *ordertable = NULL;
  cpl_parameterlist *  	parameters = NULL;
  const char *test_id = "uves_extract-test";
  bool extract_partial = false;
  bool debug_mode = true;
  bool blue = true;
  enum uves_chip chip = uves_chip_get_first(blue);
  cpl_image *cosmics = NULL;
  uves_iterate_position *pos = NULL;
  uves_extract_profile *profile = NULL;

  /* Output */
  cpl_image *out_spectrum = NULL;
  cpl_image *out_sky = NULL;
  cpl_image *out_sky_noise = NULL;
//  uves_propertylist *spectrum_header = NULL;
//  cpl_image *spectrum_noise = NULL;
  cpl_table *cosmic_mask = NULL;
  cpl_image *cosmic_image = NULL;
//  cpl_table *profile_table = NULL;
  cpl_image *weights = NULL;
  cpl_table *info_tbl = NULL; 
  cpl_table *order_trace = NULL;

  /* Build data */
  check_nomsg( create_order_table(NULL, &order_locations, NULL,
                                  minorder, maxorder, nx) );

  check_nomsg( in_spectrum = create_spectrum(nx, ny,
					     minorder, maxorder,
					     order_locations,
					     sg,
					     &in_sky) );

  pos = uves_iterate_new(nx, ny, order_locations, minorder, maxorder, sg);

  profile = uves_extract_profile_new(uves_gauss, 
                                     uves_gauss_derivative, 
                                     4, 0, 0);

  profile->y0 = uves_polynomial_new_zero(2);
  profile->sigma = uves_polynomial_new_zero(2);
  uves_polynomial_shift(profile->sigma, 0, 2.5);

  check_nomsg( image = uves_create_image(pos,
                                         chip,
                                         in_spectrum, in_sky,
                                         cosmics,
                                         profile,
                                         &image_noise,
                                         &image_header) );
  
  uves_save_image(image, "image.fits", NULL, true, true);
  uves_save_image(image_noise, "noise.fits", NULL, true, true);
  
  ordertable = cpl_table_new(2);
  cpl_table_new_column(ordertable, "Order", CPL_TYPE_INT);
  cpl_table_set_int(ordertable, "Order", 0, minorder);
  cpl_table_set_int(ordertable, "Order", 1, maxorder);

  /* Extract */
  parameters = cpl_parameterlist_new();
  check_nomsg( uves_propagate_parameters_step(UVES_EXTRACT_ID, 
					      parameters,
					      test_id,
					      NULL));

  {
    const char *value = "optimal";  //fixme should test also linear
    uves_set_parameter(parameters, test_id, UVES_EXTRACT_ID ".method", CPL_TYPE_STRING, &value);
  }

  check( out_spectrum = 
	 uves_extract(image,
		      image_noise,
		      image_header,
		      ordertable,
		      order_locations,
		      sg.length,
		      sg.offset,
		      parameters,
		      test_id,
                      "",
		      extract_partial,
                      debug_mode,
		      chip,
		      NULL,/* spectrum_header */
		      NULL, /* spectrum_noise */
		      &out_sky,
		      &out_sky_noise,
		      &cosmic_mask,
		      &cosmic_image,
		      NULL, /* profile_table */
		      &weights,
		      &info_tbl,
                      &order_trace),
	 "Error during extraction");

  uves_save_image(out_spectrum, "spectrum.fits", NULL, true, true);
  
  /* Check results */
  {
    int x, order;
    
    for (order = minorder; order <= maxorder; order++)
      {
	int spectrum_row = order - minorder + 1;
	
	for (x = 1; x <= nx; x++)
	  {
	    int in_bad, out_bad;
	    double in     = cpl_image_get( in_spectrum, x, spectrum_row, &in_bad);
	    double sky    = cpl_image_get( in_sky     , x, spectrum_row, &in_bad);
	    double out    = cpl_image_get(out_spectrum, x, spectrum_row, &out_bad); 
	    double osky   = cpl_image_get(out_sky     , x, spectrum_row, &out_bad); 
	    
#if 0  /* linear */
	    assure( out_bad || in_bad ||
		    float_equal(out, in + sky, 0.001),
		    CPL_ERROR_ILLEGAL_OUTPUT,
		    "At (x, order) = (%d, %d): In = %f  +  %f (%d); Out = %f (%d)",
		    x, order, sky, in, in_bad, out, out_bad);

#else /* optimal */

#if 0
	    assure( out_bad || in_bad ||
		    float_equal(out, in, 0.02),
		    CPL_ERROR_ILLEGAL_OUTPUT,
		    "Object spectrum differs at (x, order) = (%d, %d): In = %f (%d); Out = %f (%d)",
		    x, order, in, in_bad, out, out_bad);

	    assure( out_bad || in_bad ||
		    float_equal(osky, sky, 0.01),
		    CPL_ERROR_ILLEGAL_OUTPUT,
		    "Sky spectrum differs at (x, order) = (%d, %d), sky: In = %f (%d); Out = %f (%d)",
		    x, order, sky, in_bad, osky, out_bad);
#endif
            if (!out_bad && !in_bad)
                {
                    cpl_test_rel(out, in, 0.02);
                    cpl_test_rel(osky, sky, 0.01);
                }
#endif
	    
	  }
      }
  }
  
 cleanup:
  uves_free_image(&in_spectrum);
  uves_free_image(&in_sky);
  uves_free_image(&image);
  uves_free_image(&image_noise);
  uves_free_propertylist(&image_header);
  uves_polynomial_delete(&order_locations);
  uves_free_parameterlist(&parameters);
  uves_free_table(&ordertable);
  uves_iterate_delete(&pos);
  uves_extract_profile_delete(&profile);
  uves_free_image(&cosmics);

  uves_free_image(&out_spectrum);
  uves_free_image(&out_sky);
  uves_free_image(&out_sky_noise);
  uves_free_image(&weights);
  uves_free_table(&cosmic_mask);
  uves_free_image(&cosmic_image);
  uves_free_table(&info_tbl);
  uves_free_table(&order_trace);
  return;
}


/*----------------------------------------------------------------------------*/
/**
   @brief test iteration
 */
/*----------------------------------------------------------------------------*/

static void
test_iterate(void)
{
  polynomial *order_locations;
  uves_iterate_position *pos = NULL;
  cpl_binary *bpm = NULL;
  bool loop_y = true;
  int nx = 2000;
  int ny = 1000;
  cpl_image *image = cpl_image_new(2000, 1000, CPL_TYPE_DOUBLE);
  int minorder = 3;
  int maxorder = 15;
  slit_geometry sg = {30.0, 0.0};
  
  check_nomsg( create_order_table(NULL, &order_locations, NULL,
                                  minorder, maxorder, nx) );


  pos = uves_iterate_new(nx, ny,
			 order_locations,
			 minorder, 
			 maxorder, 
			 sg);


  check( uves_iterate_set_first(pos,
				1, nx,
				minorder, maxorder,
				bpm,
				loop_y),
	 "Set first position failed");

  assure( pos->x == 1 && pos->order == minorder,
	  CPL_ERROR_ILLEGAL_OUTPUT,
	  "Set first position failed: x, order, minorder = %d %d %d",
	  pos->x, pos->order, minorder);

  {
    int y = pos->y;

    assure_nomsg( !uves_iterate_finished(pos), CPL_ERROR_ILLEGAL_OUTPUT );

    check( uves_iterate_increment(pos), "Increment failed");
    check( uves_iterate_increment(pos), "Increment failed");
    check( uves_iterate_increment(pos), "Increment failed");
    check( uves_iterate_increment(pos), "Increment failed");

    /* Must hold for the polynomial used */
    assure( pos->x == 1 && pos->y == y+4 &&
	    pos->order == minorder, CPL_ERROR_ILLEGAL_OUTPUT,
	    "Increment failed: x, y, order = %d, %d (%d), %d",
	    pos->x, pos->y, y+1, pos->order);
  }

  /* Go to next order */
  while(pos->x < nx)
    {
      uves_iterate_increment(pos);
    }
  while(pos->x != 1)
    {
      uves_iterate_increment(pos);
    }

  {
    int y = pos->y;
    uves_iterate_increment(pos);

    cpl_test_eq( pos->x, 1 );
    cpl_test_eq( pos->y, y+1 );
    cpl_test_eq( pos->order, minorder+1 );
    /* Here was a bug... */
    
  }

  

 cleanup:
  uves_free_image(&image);
  uves_polynomial_delete(&order_locations);
  uves_iterate_delete(&pos);
  return;
}
  
/*----------------------------------------------------------------------------*/
/**
  @brief   Test of uves_extract
**/
/*----------------------------------------------------------------------------*/

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

//  cpl_msg_set_level(CPL_MSG_DEBUG);

  test_iterate();

  test_extract();

  return cpl_test_end(0);
}


/**@}*/
