/*                                                                              *
 *   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: 2009-06-05 05:49:02 $
 * $Revision: 1.20 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.19  2007/09/11 17:09:45  amodigli
 * added parse_midas_poly
 *
 * Revision 1.18  2007/08/21 13:14:29  jmlarsen
 * Update to changed functionality of uves_read_midas_array
 *
 * Revision 1.17  2007/08/13 12:15:43  amodigli
 * support of CPL4
 *
 * Revision 1.16  2007/06/26 14:51:00  jmlarsen
 * Fixed bug in load_linetable
 *
 * Revision 1.15  2007/06/22 14:50:11  jmlarsen
 * Expanded, again, interface of uves_save_image()
 *
 * Revision 1.14  2007/06/22 09:33:21  jmlarsen
 * Changed interface of uves_save_image
 *
 * Revision 1.13  2007/06/20 15:55:37  jmlarsen
 * Parametrized assumption about MIDAS format
 *
 * Revision 1.12  2007/06/20 15:50:44  jmlarsen
 * Expanded test
 *
 * Revision 1.11  2007/05/23 06:43:23  jmlarsen
 * Removed unused variables
 *
 * Revision 1.10  2007/05/22 14:51:02  jmlarsen
 * Removed unused variables
 *
 * Revision 1.9  2007/04/24 12:50:29  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.8  2007/04/24 08:07:11  jmlarsen
 * Shortened line
 *
 * Revision 1.7  2007/04/24 08:04:29  jmlarsen
 * Added performance test
 *
 * Revision 1.6  2007/04/10 12:51:43  jmlarsen
 * Fixed wrong reference value
 *
 * Revision 1.5  2007/04/10 12:50:56  jmlarsen
 * Added check of float values
 *
 * Revision 1.4  2007/04/10 11:35:06  jmlarsen
 * Added error checking
 *
 * Revision 1.3  2007/04/03 11:02:30  jmlarsen
 * Support reading float MIDAS arrays
 *
 * Revision 1.2  2007/03/15 15:04:14  jmlarsen
 * Allow spaces in HISTORY keyword string values
 *
 * Revision 1.1  2007/03/15 12:27:18  jmlarsen
 * Moved unit tests to ./uves/tests and ./flames/tests
 *
 * Revision 1.6  2007/02/27 14:04:14  jmlarsen
 * Move unit test infrastructure to IRPLIB
 *
 * Revision 1.5  2007/01/31 15:11:09  jmlarsen
 * Test of inf+nan when saving FITS files
 *
 * Revision 1.4  2007/01/29 12:17:54  jmlarsen
 * Support setting verbosity from command line
 *
 * Revision 1.3  2006/11/28 08:26:23  jmlarsen
 * Changed message
 *
 * Revision 1.2  2006/11/24 09:39:35  jmlarsen
 * Factored out termination code
 *
 * Revision 1.1  2006/11/22 08:04:59  jmlarsen
 * Added uves_dfs unit test module
 *
 * Revision 1.20  2006/11/16 09:49:25  jmlarsen
 * Fixed doxygen bug
 *
 * Revision 1.19  2006/11/08 14:04:03  jmlarsen
 * Doxybugfix
 *
 * Revision 1.18  2006/11/06 15:30:54  jmlarsen
 * Added missing includes
 *
 * Revision 1.17  2006/11/03 15:02:06  jmlarsen
 * Added test of uves_align
 *
 * Revision 1.16  2006/09/11 13:59:01  jmlarsen
 * Renamed identifier reserved by POSIX
 *
 * Revision 1.15  2006/08/24 11:46:18  jmlarsen
 * Fixed typo
 *
 * Revision 1.14  2006/08/24 07:18:17  amodigli
 * fixed doxygen warnings
 *
 * Revision 1.13  2006/08/24 06:39:57  jmlarsen
 * Reduced maximum line length
 *
 * Revision 1.12  2006/08/17 14:11:25  jmlarsen
 * Use assure_mem macro to check for memory allocation failure
 *
 * Revision 1.11  2006/08/14 12:13:27  jmlarsen
 * Reset irplib error handler
 *
 * Revision 1.10  2006/08/14 07:45:41  amodigli
 * doxigen doc
 *
 * Revision 1.9  2006/03/03 13:54:11  jmlarsen
 * Changed syntax of check macro
 *
 * Revision 1.8  2006/02/03 07:47:53  jmlarsen
 * Moved recipe implementations to ./uves directory
 *
 * Revision 1.7  2006/01/31 08:26:56  jmlarsen
 * Disabled recipe run tests
 *
 * Revision 1.6  2006/01/25 16:14:14  jmlarsen
 * Changed interface of gauss.fitting routine
 *
 * Revision 1.5  2005/12/16 14:22:22  jmlarsen
 * Removed midas test data; Added sof files
 *
 * Revision 1.4  2005/11/18 10:54:43  jmlarsen
 * Minor changes
 *
 * Revision 1.3  2005/11/14 13:18:44  jmlarsen
 * Minor update
 *
 * Revision 1.2  2005/11/11 13:18:54  jmlarsen
 * Reorganized code, renamed source files
 *
 * Revision 1.1  2005/11/10 16:33:41  jmlarsen
 * Added weighted extraction, test of gauss. fit
 *
 */

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

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

#include <uves_dfs.h>
#include <uves_parameters.h>
#include <uves_utils_wrappers.h>
#include <uves_test_simulate.h>
#include <uves_pfits.h>
#include <uves_error.h>

#include <cpl_test.h>
#include <cpl.h>

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

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


/*----------------------------------------------------------------------------*/
/**
 * @defgroup uves_dfs  UVES dfs unit tests
 */
/*----------------------------------------------------------------------------*/
/**@{*/


/*----------------------------------------------------------------------------*/
/**
   @brief  This function tests reading MIDAS polynomials
 */
/*----------------------------------------------------------------------------*/
static void
parse_midas_poly(void)
{
    uves_propertylist *header = uves_propertylist_new();
    polynomial *p = NULL;

    const char *data[] = {"",
		    "'COEFFI','I*4',1,7,'7I10'",
		    "     53889         2         3         2         1         4         5",
		    "",
		    "'COEFFR','R*4',1,5,'5E14.7'",
		    " 4.3300000E+02 4.0880000E+03 1.0000000E+00 2.1000000E+01 0.0000000E+00",
		    "",
		    "'COEFFD','R*8',1,30,'3E23.15'",
		    " -7.097005629698889E+01  4.050908371864904E-02 -2.886756545398909E-06",
		    "  5.504345508879626E-10 -5.583004967206025E-14  7.624532125635992E+01",
		    " -2.428213567964009E-03  1.819158447566360E-06 -5.090366383338846E-10",
		    "  5.198098506055602E-14  3.513177145982783E-01  5.570332137951829E-04",
		    " -3.876157463910250E-07  1.113253735718822E-10 -1.132455173423791E-14",
		    "  2.977232589499959E-02 -5.389240622889887E-05  3.777456726044612E-08",
		    " -1.083863050648735E-11  1.098450510939580E-15 -1.093309039442914E-03",		    
		    "  2.402609262989674E-06 -1.688416547941747E-09  4.839101712729582E-13",
		    " -4.884504488944702E-17  1.919853952642526E-05 -4.004133160220927E-08",
		    "  2.816206503824200E-11 -8.051313882805877E-15  8.090579180112579E-19",
		    " ",
		    "'TAB_IN_OUT_YSHIFT','R*8',1,1,'3E23.15'",
		    "  4.180818583555659E+01		    ",
		    " "};
    unsigned int i;
    
    /* Create typical FLAMES ordef table header */
    for (i = 0; i < 8000; i++)
	{
	    uves_propertylist_append_string(
		header, "HISTORY",
		" 35834     35835     35836     35837     35838     35839     35840");
	}

    for (i = 0; i < sizeof(data)/sizeof(char *); i++)
	{
	    uves_propertylist_append_string(
		header, "HISTORY",
		data[i]);
	}

    check_nomsg( p = uves_polynomial_convert_from_plist_midas(header, 
							      "COEFF",-1));
    
    assure( uves_polynomial_get_dimension(p) == 2, CPL_ERROR_ILLEGAL_OUTPUT,
	    "Dimension is %d, 2 expected", uves_polynomial_get_dimension(p));

  cleanup:
    uves_free_propertylist(&header);
    uves_polynomial_delete(&p);

    return;
}

/*----------------------------------------------------------------------------*/
/**
   @brief line table input
 */
/*----------------------------------------------------------------------------*/
static void
test_load_linetable(void)
{
    const char * const filename = "linetable.fits";
    cpl_table *linetable_in = NULL;
    polynomial *dispersion = NULL;
    polynomial *absorder = NULL;
    uves_propertylist *header = uves_propertylist_new();
    uves_propertylist *eheader = uves_propertylist_new();

    cpl_frame *f = cpl_frame_new();
    cpl_frameset *frames = cpl_frameset_new();
    bool flames = false;
    const char *const chip_id = "CCD42";
    cpl_table *ordertable = NULL;
    cpl_table *tracetable = NULL;
    polynomial *order_locations = NULL;
    int firstabs, lastabs;
    enum uves_chip chip = UVES_CHIP_BLUE;
    int minorder = 1;
    int maxorder = 5;
    int nx = 150;
    /*int ny = 100;*/
    int trace_id = 0;
    int window = 1;

    /* output */
    const char *linetable_filename;
    cpl_table *linetable_out = NULL;
    uves_propertylist *header_out = NULL;
    polynomial *dispersion_out = NULL;
    polynomial *absorder_out = NULL;

    /* build data */
    check_nomsg( create_order_table(&ordertable, &order_locations, &tracetable,
                                    minorder, maxorder, nx));
    check_nomsg( create_line_table(&linetable_in, &dispersion, &absorder, 
                                   &firstabs, &lastabs,
                                   minorder, maxorder, nx));
    check_nomsg( uves_propertylist_append_string(header, UVES_CHIP_ID(chip), chip_id));
    check_nomsg( uves_propertylist_append_string(header, UVES_DRS_ID, "CPL"));
    
    check_nomsg( uves_pfits_set_firstabsorder(eheader, firstabs) );
    check_nomsg( uves_pfits_set_lastabsorder(eheader, lastabs) );
    
    check_nomsg( uves_pfits_set_traceid(eheader, trace_id) );
    check_nomsg( uves_pfits_set_windownumber(eheader, window) );

    check_nomsg( uves_table_save(linetable_in, header, eheader, filename, CPL_IO_DEFAULT) );
    check_nomsg( uves_save_polynomial(dispersion, filename, eheader) );
    check_nomsg( uves_save_polynomial(absorder, filename, eheader) );

    cpl_test_eq(cpl_error_get_code(), CPL_ERROR_NONE);

    cpl_frame_set_filename(f, filename);
    cpl_frame_set_tag(f, "LINE_TABLE_BLUE");
    cpl_frameset_insert(frames, f);

    check_nomsg( uves_load_linetable(frames,
                                     flames,
                                     chip_id,
                                     order_locations, minorder, maxorder,
                                     &linetable_filename,
                                     &linetable_out,
                                     &header_out,
                                     &dispersion_out,
                                     &absorder_out,
                                     chip, trace_id, window));

    cpl_test_eq(cpl_error_get_code(), CPL_ERROR_NONE);

    cpl_test( linetable_out != NULL );
    cpl_test( header_out != NULL );
    cpl_test( dispersion_out != NULL );
    cpl_test( absorder_out != NULL );

    cpl_test_eq( cpl_table_get_nrow(linetable_in),
                    cpl_table_get_nrow(linetable_out) );

    /* not required: cpl_test_eq( cpl_table_get_ncol(linetable_in),
       cpl_table_get_ncol(linetable_out) );
    */

    cpl_test_eq_string( filename, linetable_filename );

    {
        int order;
        int x;
        
        for (order = minorder; order <= maxorder; order++) {
            for (x = 1; x <= nx; x += nx/6) {
                cpl_test_rel( 
                    uves_polynomial_evaluate_2d(absorder    , x, order),
                    uves_polynomial_evaluate_2d(absorder_out, x, order), 0.001);
                
                cpl_test_rel( 
                    uves_polynomial_evaluate_2d(dispersion  , x, order),
                    uves_polynomial_evaluate_2d(dispersion_out, x, order), 0.001);
            }
        }
    }
                
  cleanup:
    uves_free_frameset(&frames);
    uves_free_table(&linetable_in);
    uves_free_table(&linetable_out);
    uves_polynomial_delete(&dispersion);
    uves_polynomial_delete(&absorder);
    uves_polynomial_delete(&dispersion_out);
    uves_polynomial_delete(&absorder_out);
    uves_free_propertylist(&header_out);
    uves_free_propertylist(&header);
    uves_free_propertylist(&eheader);
    uves_free_table(&ordertable);
    uves_free_table(&tracetable);
    uves_polynomial_delete(&order_locations);

    return;
}


/*----------------------------------------------------------------------------*/
/**
   @brief this function tests reading MIDAS arrays
 */
/*----------------------------------------------------------------------------*/
static void
convert_midas_array(void)
{
    const char *values[] = {"HISTORY", "", 
			    "HISTORY", "'FIBREPOS','R*8',1,9,'3E23.15'",
			    "HISTORY", " -3.243571124678650E+01 -2.309646501161805E+01 -1.402902770375962E+01",
			    "HISTORY", " -4.772375924542811E+00  4.827040349175236E+00  1.378761244187003E+01",
			    "HISTORY", "  2.321337764943556E+01  3.243571124678650E+01 -3.552713678800501E-15",
			    "HISTORY", "",
                            "HISTORY", "'COEFFR','R*4',1,20,'5E14.7'",
                            "HISTORY", "9.4893160E+00 4.0716226E+03 0.0000000E+00 2.3000000E+01 1.8538159E-04",
                            "HISTORY", "0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00",
			    "HISTORY", "",
			    "HISTORY", "'INTVAL','I",
			    "HISTORY", "1 2 3 4 5 6",
			    "HISTORY", "7",
			    "HISTORY", "",
			    "HISTORY", "'LEGAL','C",
			    "HISTORY", " a sdfasdf",
			    "HISTORY", "",
			    "HISTORY", "'ILLEGAL','C",
			    "HISTORY", "1bsdf",
			    "HISTORY", "bsdf",
			    "HISTORY", "",
			    "HISTORY", "'CHIPCHOICE','C",
			    "HISTORY", "abcd",
			    "HISTORY", ""};

    int N = sizeof(values) / sizeof(const char *) / 2;

    uves_propertylist *header = NULL;
    double *resultd = NULL;
    int *resulti = NULL;
    float *resultf = NULL;
    const char *results = NULL;
    int result_length, i;
    int nkeys;
    cpl_type result_type;

    header = uves_propertylist_new();
    for (i = 0; i < N; i++)
	{
	    uves_propertylist_append_string(header, values[i*2], values[i*2+1]);
	}

    check_nomsg(resultd = uves_read_midas_array(header, "FIBREPOS", &result_length,
                                               &result_type, &nkeys));

    cpl_test_eq(result_type, CPL_TYPE_DOUBLE);
    cpl_test_eq(result_length, 9);
    cpl_test_eq(nkeys, 5);

    /* Check numbers, see above */
    cpl_test_rel(resultd[0], -32, 0.10);
    cpl_test_rel(resultd[3], -4.7, 0.10);
    cpl_test_rel(resultd[6], 23, 0.10);
    cpl_test( fabs(resultd[8]) < 0.001);

    /* float */
    check_nomsg(resultf = uves_read_midas_array(header, "COEFFR", &result_length,
                                                &result_type, &nkeys));
    cpl_test_eq(result_type, CPL_TYPE_FLOAT);
    cpl_test_eq(result_length, 10);
    cpl_test_eq(nkeys, 4);

    cpl_test_rel(resultf[0], 9.489, 0.01);
    cpl_test_rel(resultf[1], 4071,  0.01);
    cpl_test_abs(resultf[2], 0.000, 0.01);
    cpl_test_rel(resultf[3], 23.00, 0.01);

    /* integer */
    check_nomsg(resulti = uves_read_midas_array(header, "INTVAL", &result_length,
                                               &result_type, &nkeys));

    cpl_test_eq(result_type, CPL_TYPE_INT);
    cpl_test_eq(result_length, 7);
    cpl_test_eq(nkeys, 4);
    for (i = 1; i <= 7; i++)
        {
            cpl_test_eq(resulti[i-1], i);
        }


    /* string */
    check_nomsg( results = uves_read_midas_array(header, "LEGAL", &result_length,
                                       &result_type, &nkeys) );

    cpl_test_eq(result_type, CPL_TYPE_STRING);
    cpl_test_eq(result_length, 10);
    cpl_test_eq(nkeys, 3);
    cpl_test_eq_string(results, " a sdfasdf");

    cpl_test(uves_read_midas_array(header, "ILLEGAL2", &result_length,
                                      &result_type, &nkeys) == NULL);
    uves_error_reset();

    uves_free_string_const(&results);
    check_nomsg(results = uves_read_midas_array(header, "CHIPCHOICE", &result_length,
                                               &result_type, &nkeys));

    cpl_test_eq(result_type, CPL_TYPE_STRING);
    cpl_test_eq(result_length, 4);
    cpl_test_eq(nkeys, 3);
    cpl_test_eq_string(results, "abcd");

    /* Performance test (relevant for long FLAMES FITS headers) */
    N = 9000;
    uves_free_propertylist(&header);
    header = uves_propertylist_new();
    uves_propertylist_append_string(header, "HISTORY", "'SELIDX','I*4',1,48389,'7I10'");
    for (i = 0; i < N; i++)
	{
	    uves_propertylist_append_string(
                header, "HISTORY", 
                "  64605     64606     64607     64608     64609     64610     64611");
	}
    uves_propertylist_append_string(header, "HISTORY", "");
    
    uves_free_int(&resulti);
    check_nomsg( resulti = uves_read_midas_array(header, "SELIDX", &result_length,
                                                 &result_type, &nkeys));

    cpl_test_eq(result_type, CPL_TYPE_INT);
    cpl_test_eq(result_length, N*7);
    cpl_test_eq(nkeys, 1+N+1);

  cleanup:
    uves_free_propertylist(&header);
    uves_free_double(&resultd);
    uves_free_int(&resulti);
    uves_free_float(&resultf);
    uves_free_string_const(&results);
    return;
}
   
/*----------------------------------------------------------------------------*/
/**
   @brief test image output
 */
/*----------------------------------------------------------------------------*/
static void
test_save_image(void)
{
    const int N = 100;
    int i;

    cpl_image *image = cpl_image_new(N, 1, CPL_TYPE_DOUBLE);

    double inf = DBL_MAX;
    for (i = 1; i <= N; i++)
	{
	    cpl_image_set(image, i, 1, -FLT_MAX*200);
	    inf *= 10;
	}


    cpl_image_set(image, 1, 1, inf);
    cpl_image_set(image, 2, 1, inf/inf);

    uves_save_image(image, "dfs.fits", NULL, true, true);
    uves_free_image(&image);

    return;
}

/*----------------------------------------------------------------------------*/
/**
   @brief test pipeline product creation
 */
/*----------------------------------------------------------------------------*/
static void
test_save_frame(void)
{
    cpl_frameset *frames = cpl_frameset_new();
    cpl_parameterlist *parameters = cpl_parameterlist_new();
    int nx = 1500;
    int ny = 1024;
    int nkey = 360;
    cpl_image *image = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
    uves_propertylist *raw_header = uves_propertylist_new();
    uves_propertylist *product_header = uves_propertylist_new();
    const char *starttime;
    const char *recipe_id = "uves_cal_phony";
    const char *tag = "PHONY_TAG";
    const char *raw_filename = "raw_file.fits";

    uves_define_global_parameters(parameters);
    cpl_test_eq( cpl_error_get_code(), CPL_ERROR_NONE );

    /* Create raw image */
    {
        cpl_image *raw_image = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
        cpl_frame *raw_frame = cpl_frame_new();

        {
            int i;
            for (i = 0; i < nkey; i++)
                {
                    const char *key_name = uves_sprintf("KEY%d", i);
                    uves_propertylist_append_int(raw_header, key_name, i);
                    uves_free_string_const(&key_name);
                }
            uves_propertylist_append_string(raw_header, "ORIGIN", "unknown...");
        }

        uves_image_save(raw_image, 
                        raw_filename,
                        CPL_BPP_IEEE_FLOAT,
                        raw_header,
                        CPL_IO_DEFAULT);
        cpl_test_eq( cpl_error_get_code(), CPL_ERROR_NONE );
        
        uves_free_image(&raw_image);

        /* Wrap frame */
        cpl_frame_set_tag(raw_frame, "BIAS_BLUE"); /* Use recognized tag,
                                                      so that FRAME_TYPE
                                                      is set to RAW */
        cpl_frame_set_filename(raw_frame, raw_filename);
        cpl_frameset_insert(frames, raw_frame);
    }

    starttime = uves_initialize(frames, parameters, recipe_id,
                                "This recipe does not do anything");
    cpl_test_eq( cpl_error_get_code(), CPL_ERROR_NONE );

    uves_frameset_insert(frames, 
                         image, 
                         CPL_FRAME_GROUP_PRODUCT,
                         CPL_FRAME_TYPE_IMAGE,
                         CPL_FRAME_LEVEL_INTERMEDIATE,
                         "dfs_product.fits",
                         tag,
                         raw_header,
                         product_header,
                         NULL, /* table header */
                         parameters, 
                         recipe_id,
                         PACKAGE "/" PACKAGE_VERSION,
                         NULL, /* qc table */
                         starttime,
                         false,   /* dump PAF */
                         0   /* stats_mask */);

    cpl_test_eq( cpl_error_get_code(), CPL_ERROR_NONE );
    cpl_test( cpl_frameset_find(frames, tag) != NULL);

    uves_free_frameset(&frames);
    uves_free_parameterlist(&parameters);
    uves_free_image(&image);
    uves_free_propertylist(&raw_header);
    uves_free_propertylist(&product_header);
    uves_free_string_const(&starttime);
    return;
}


/*----------------------------------------------------------------------------*/
/**
  @brief   Test of uves_dfs
**/
/*----------------------------------------------------------------------------*/

int main(void)
{
    /* Initialize CPL + UVES messaging */
    cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_WARNING);
    cpl_errorstate initial_errorstate = cpl_errorstate_get();
//    cpl_msg_set_level(CPL_MSG_DEBUG);

    check( parse_midas_poly(),
	   "Test of MIDAS array conversion");


    check_nomsg( test_load_linetable() );

    check( convert_midas_array(),
	   "Test of MIDAS array conversion failed");

    check_nomsg( test_save_image() );

    check_nomsg( test_save_frame() );

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE)
        {
	  cpl_errorstate_dump(initial_errorstate,CPL_FALSE,NULL);
        }
    return cpl_test_end(0);
}


/**@}*/
