/* $Id: fors_dfs-test.c,v 1.0 2017-01-19 11:16:00 msalmist Exp $
 *
 * This file is part of the FORS Library
 * Copyright (C) 2002-2006 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: msalmist $
 * $Date: 2017-01-19 11:16:00 $
 * $Revision: 1.0 $
 * $Name: not supported by cvs2svn $
 */

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

#include <fors_dfs.h>
#include <fors_utils.h>

#include <test_simulate.h>
#include <test.h>

#include <cpl.h>

#include <fs_test.h>

#include <string.h>

const char* test_context = "test";

const char* test_bool_name = "test.bool";
const char* test_bool_alias = "bool_arg";

const char* test_int_name = "test.int";
const char* test_int_alias = "int_arg";

const char* test_double_name = "test.double";
const char* test_double_alias = "double_arg";

const char* test_string_name = "test.string";
const char* test_string_alias = "string_arg";

//utility functions definition

static cpl_parameterlist*
get_test_parameterlist(int bool_default,
					int bool_value,
					int int_default,
					int int_value,
					double double_default,
					double double_value,
					const char* string_default,
					const char* string_value);

static cpl_parameterlist*
get_test_default_parameterlist(int bool_default,
					int int_default,
					double double_default,
					const char* string_default);

static void fill_table(cpl_table* table,
		int bool_table, int int_table,
		double double_table, const char* string_table);

static void
add_common_keywords(cpl_frame* f1, cpl_frame* f2,
		cpl_boolean use_first_on_1, cpl_boolean use_first_on_2);

static void
add_keyword(cpl_frame* frame, cpl_boolean use_val1);


static cpl_frameset*
get_fset_kwords(cpl_boolean use_first_on_1,
		cpl_boolean use_first_on_2, const char* bias_fname,
		const char* sky_flat_fname);

// Test for dfs-related get parameters functions

#undef cleanup
#define cleanup \
do\
{ \
	cpl_parameterlist_delete(non_default_parameters);\
	cpl_parameterlist_delete(default_parameters);\
    cpl_table_delete(fallback_parameters_table);\
    \
} while(0)

static void
test_dfs_get_parameters(void)
{
	int bool_default = FALSE;
	int bool_value = TRUE;
	int bool_table = FALSE;

	int int_default = 55;
	int int_value = 66;
	int int_table = 77;

	double double_default = 166.0;
	double double_value = 177.0;
	double double_table = 188.0;

	const char* string_default = "String default";
	const char* string_value = "String value";
	const char* string_table = "String in table";

    cpl_table *fallback_parameters_table = cpl_table_new(1);
    cpl_parameterlist *non_default_parameters = get_test_parameterlist
											   (bool_default, bool_value,
											   int_default, int_value,
											   double_default, double_value,
											   string_default, string_value);

    cpl_parameterlist* default_parameters =
    		get_test_default_parameterlist(bool_default, int_default,
										   double_default, string_default);

    //Cases when table is ignored
    //Case 1 - Table non specified, we use the list values
    int extracted_bool = dfs_get_parameter_bool
						(non_default_parameters, test_bool_name,   NULL);

    int extracted_int = dfs_get_parameter_int
						(non_default_parameters, test_int_name,    NULL);
    double extracted_double = dfs_get_parameter_double
						      (non_default_parameters, test_double_name, NULL);
    const char* extracted_string = dfs_get_parameter_string
								   (non_default_parameters, test_string_name,
								    NULL);

    assure(extracted_bool   == bool_value, return,
    	"Case 1: the extracted bool should be the one contained in the list");

    assure(extracted_int    == int_value, return,
    	"Case 1: The extracted int should be the one contained in the list");

    assure(extracted_double == double_value, return,
    	"Case 1: The extracted double should be the one contained in the list");

    assure(!strcmp(extracted_string, string_value), return,
    	"Case 1: The extracted string should be the one contained in the list");

    /*
     * Case 2 - The table does not contain the value we are searching for,
     * we use the parameter list
     */

    extracted_bool = dfs_get_parameter_bool
    		(non_default_parameters, test_bool_name,   fallback_parameters_table);

    extracted_int = dfs_get_parameter_int
    		(non_default_parameters, test_int_name,    fallback_parameters_table);

    extracted_double = dfs_get_parameter_double
    		(non_default_parameters, test_double_name, fallback_parameters_table);

    extracted_string = dfs_get_parameter_string
    		(non_default_parameters, test_string_name, fallback_parameters_table);

    assure( extracted_bool   == bool_value         , return,
    		"Case 2: the extracted bool should be the one contained in the list");
    assure( extracted_int    == int_value          , return,
    		"Case 2: The extracted int should be the one contained in the list");
    assure( extracted_double == double_value       , return,
    		"Case 2: The extracted double should be the one contained in the list");
    assure( !strcmp(extracted_string, string_value), return,
    		"Case 2: The extracted string should be the one contained in the list");

    //Fill the table with corresponding data
    fill_table(fallback_parameters_table, bool_table, int_table, double_table, string_table);

    //Case 3 -The table contains the values we are searching for, but we are passing a NON-default list. The table is ignored
    extracted_bool = dfs_get_parameter_bool
    		(non_default_parameters, test_bool_name,   fallback_parameters_table);

    extracted_int = dfs_get_parameter_int
			(non_default_parameters, test_int_name,    fallback_parameters_table);

	extracted_double = dfs_get_parameter_double
			(non_default_parameters, test_double_name, fallback_parameters_table);

	extracted_string = dfs_get_parameter_string
			(non_default_parameters, test_string_name, fallback_parameters_table);

	assure( extracted_bool   == bool_value, return,
			"Case 3: the extracted bool should be the one contained in the list");

	assure( extracted_int    == int_value, return,
			"Case 3: The extracted int should be the one contained in the list");

	assure( extracted_double == double_value, return,
			"Case 3: The extracted double should be the one contained in the list");

	assure( !strcmp(extracted_string, string_value), return,
			"Case 3: The extracted string should be the one contained in the list");

    //Case when the table is used: contains the value we are searching for AND parameter list contains defaults
    //Case 4
	extracted_bool   = dfs_get_parameter_bool
			(default_parameters, test_bool_name,   fallback_parameters_table);

	extracted_int    = dfs_get_parameter_int
			(default_parameters, test_int_name,    fallback_parameters_table);

	extracted_double = dfs_get_parameter_double
			(default_parameters, test_double_name, fallback_parameters_table);

	extracted_string = dfs_get_parameter_string
			(default_parameters, test_string_name, fallback_parameters_table);

	assure( extracted_bool   == bool_table         , return,
			"Case 4: the extracted bool should be the one contained in the table");

	assure( extracted_int    == int_table          , return,
			"Case 4: The extracted int should be the one contained in the table");

	assure( extracted_double == double_table       , return,
			"Case 4: The extracted double should be the one contained in the table");

	assure( !strcmp(extracted_string, string_table), return,
			"Case 4: The extracted string should be the one contained in the table");

	//Case 4 Test side effects on list
	extracted_bool   =
			dfs_get_parameter_bool_const  (default_parameters, test_bool_name);
	extracted_int    =
			dfs_get_parameter_int_const   (default_parameters, test_int_name);
	extracted_double =
			dfs_get_parameter_double_const(default_parameters, test_double_name);
	extracted_string =
			dfs_get_parameter_string_const(default_parameters, test_string_name);

	assure( extracted_bool   == bool_table         , return,
			"Case 4: The bool in the list should be modified");
	assure( extracted_int    == int_table          , return,
			"Case 4: The int in the list should be modified");
	assure( extracted_double == double_table       , return,
			"Case 4: The double in the list should be modified");
	assure( !strcmp(extracted_string, string_table), return,
			"Case 4: The in the list should be modified");

    cleanup;
}

#undef cleanup
#define cleanup \
do \
{ \
	cpl_frameset_delete(f_set);\
	cpl_image_delete(img1);\
	cpl_image_delete(img2);\
	cpl_image_delete(img3);\
	cpl_propertylist_delete(l1);\
	cpl_propertylist_delete(l2);\
	cpl_propertylist_delete(l3);\
	cpl_table_delete(t1);\
	cpl_table_delete(t2);\
	delete_test_file(bias_fname);\
	delete_test_file(sky_flat_fname);\
	delete_test_file(table_fname);\
} while(0)

//test for the load functions of the dfs module
static void
test_dfs_load(void)
{
	cpl_image* img1 = NULL;
	cpl_image* img2 = NULL;
	cpl_image* img3 = NULL;

	cpl_table* t1 = NULL;
	cpl_table* t2 = NULL;

	cpl_propertylist* l1 = NULL;
	cpl_propertylist* l2 = NULL;
	cpl_propertylist* l3 = NULL;

	cpl_frameset* f_set = cpl_frameset_new();

	const char* bias_tag 			  = "BIAS";
	const char* flat_tag 		      = "FLAT";
	const char* bias_not_existing_tag = "BIAS_NOT_EXISTING";
	const char* tab_tag 			  = "PHOT_TAG";
	const char* tab_not_existing_tag  = "PHOT_TABLE_NOT_EXISTING";

	const char* bias_fname = "master_bias_dfs_test.fits";
	const char* sky_flat_fname = "master_sky_flat_dfs_test.fits";
	const char* table_fname = "phot_table_dfs_test.fits";

	cpl_frame* f1 = create_bias(bias_fname, bias_tag, CPL_FRAME_GROUP_CALIB);
	cpl_frame* f2 = create_sky_flat(sky_flat_fname, flat_tag,
			CPL_FRAME_GROUP_CALIB, 1.0);

	cpl_frame* f3 = cpl_frame_new();
	cpl_frame_set_filename(f3, "zeropoint_master_bias_DOES_NOT_EXIST.fits");
	cpl_frame_set_tag(f3, bias_not_existing_tag);

	cpl_frame* f4 = create_phot_table(table_fname , tab_tag, CPL_FRAME_GROUP_CALIB);

	cpl_frame* f5 = cpl_frame_new();
	cpl_frame_set_filename(f5, "zeropoint_phot_table_DOES_NOT_EXIST.fits");
	cpl_frame_set_tag(f5, tab_not_existing_tag);

	cpl_frameset_insert(f_set, f1);
	cpl_frameset_insert(f_set, f2);
	cpl_frameset_insert(f_set, f3);
	cpl_frameset_insert(f_set, f4);
	cpl_frameset_insert(f_set, f5);

	img1 = dfs_load_image(f_set, bias_tag, 				CPL_TYPE_DOUBLE, 0, 1);
	img2 = dfs_load_image(f_set, flat_tag, 			    CPL_TYPE_DOUBLE, 0, 1);
	img3 = dfs_load_image(f_set, bias_not_existing_tag, CPL_TYPE_DOUBLE, 0, 1);

	assure(cpl_error_get_code() != CPL_ERROR_NONE, return,
			"Load image: CPL error not set");

	cpl_error_reset();

	assure(img1, return,  "Img1 must be correctly extracted");
	assure(img2, return,  "Img2 must be correctly extracted");
	assure(!img3, return, "Img3 must be NULL");

	l1 = dfs_load_header(f_set, bias_tag, 			   0);
	l2 = dfs_load_header(f_set, flat_tag,			   0);
	l3 = dfs_load_header(f_set, bias_not_existing_tag, 0);

	assure(cpl_error_get_code() != CPL_ERROR_NONE, return,
			"Load header: CPL error not set");
	cpl_error_reset();

	assure(l1, return,  "l1 must be correctly extracted");
	assure(l2, return,  "l2 must be correctly extracted");
	assure(!l3, return, "l3 must be NULL");

	t1 = dfs_load_table(f_set, tab_tag, 			 1);
	t2 = dfs_load_table(f_set, tab_not_existing_tag, 1);

	assure(cpl_error_get_code() != CPL_ERROR_NONE, return,
			"Load header: CPL error not set");
	cpl_error_reset();

	assure(t1, return,  "t1 must be correctly extracted");
	assure(!t2, return, "t2 must be NULL");

	cleanup;
}

#undef cleanup
#define cleanup \
do \
{ \
	cpl_propertylist_delete(list);\
	cpl_frame_delete(frame);\
	fors_setting_delete(&setting);\
	delete_test_file("skyflat.fits");\
} while(0)
//Test the functions for adding new elements to the property list
static void test_dfs_proplist_add(void)
{
	cpl_propertylist* list = cpl_propertylist_new();

	fors_setting* setting = NULL;

	const char* fname = "skyflat_dfs_test.fits";
	const char* tag = "SKY_FLAT";

	double expTime = 987.0f;

	cpl_frame* frame = create_sky_flat(fname, tag, CPL_FRAME_GROUP_CALIB, expTime);
    cpl_propertylist_update_double(list, "EXPTIME", 21.0);

	assure( 21.0 == cpl_propertylist_get_double(list, "EXPTIME"), return,
			"Case 1: Setter for list not working");

	//Case 1 - Exptime frame null, 5.0 should override
	fors_dfs_add_exptime(list, NULL, 5.0);

	assure(cpl_propertylist_get_size(list) == 1, return,
			"Case 1: wrong length of the list");

	assure( 5.0 == cpl_propertylist_get_double(list, "EXPTIME"), return,
			"Case 1: All the properties must be overridden");

	cpl_propertylist_delete(list); list = NULL;

	//Case 2 - frame != NULL -> we use the exptime of the frame

	list = cpl_propertylist_new();
    cpl_propertylist_update_double(list, "EXPTIME", 21.0);

	assure( 21.0 == cpl_propertylist_get_double(list, "EXPTIME"), return,
			"Case 2: Setter for list not working");

	fors_dfs_add_exptime(list, frame, 5.0);

	assure(cpl_propertylist_get_size(list) == 1, return,
				"Case 2: wrong length of the list");

	assure( expTime == cpl_propertylist_get_double(list, "EXPTIME"), return,
				"Case 2: All the properties must be overridden");

	cpl_propertylist_delete(list); list = NULL;

	//Case 3 - Exptime WCS
	list = cpl_propertylist_new();
    cpl_propertylist_update_double(list, "CRPIX1", 1.0);
    cpl_propertylist_update_double(list, "CRPIX2", 2.0);

    assure( 1.0 == cpl_propertylist_get_double(list, "CRPIX1"), return,
    			"Case 3: Setter1 for list not working");
    assure( 2.0 == cpl_propertylist_get_double(list, "CRPIX2"), return,
        			"Case 3: Setter2 for list not working");

    setting = fors_setting_new(frame);

    setting->prescan_x = 11;
    setting->prescan_y = 10;

	fors_dfs_add_wcs(list, frame, setting);

	assure(cpl_propertylist_get_size(list) == 8, return,
				"Case 3: wrong length of the list");

    assure( -10.0 == cpl_propertylist_get_double(list, "CRPIX1"), return,
    			"Case 3: add wcs + prescan correction not working (1)");
    assure( -9.0 == cpl_propertylist_get_double(list, "CRPIX2"), return,
        			"Case 3: add wcs + prescan correction not working (2)");

	cleanup;
}

#undef cleanup
#define cleanup \
{\
	cpl_frameset_delete(f_set);\
	delete_test_file(bias_fname);\
	delete_test_file(flat_fname);\
	delete_test_file("other.txt");\
}while(0);

void test_dfs_equal_keywords()
{
	const char* bias_fname = "bias_dfs_test.fits";
	const char* flat_fname = "flat_dfs_test.fits";

	cpl_frameset* f_set = get_fset_kwords(CPL_TRUE, CPL_TRUE, bias_fname,
			flat_fname);

	for(cpl_size i = 0; i < cpl_frameset_get_size(f_set); ++i){
		const char * fname = cpl_frame_get_filename(cpl_frameset_get_position_const(f_set, i));
		cpl_msg_warning(cpl_func, "file for test: %s", fname);
	}

	assure(f_set, return,
			"Generation of frameset for test failed");

    assure(dfs_equal_keyword(f_set, "TEST_PROP_INT"), return,
			"TEST_PROP_INT should be equal!");


	assure(dfs_equal_keyword(f_set, "TEST_PROP_STRING"), return,
				"TEST_PROP_INT should be equal!");

	assure(!dfs_equal_keyword(f_set, "TEST_PROP_FLOAT"), return,
				"TEST_PROP_FLOAT should be NEVER equal!");

	cleanup;

	f_set = get_fset_kwords(CPL_TRUE, CPL_FALSE, bias_fname,
				flat_fname);

	assure(!dfs_equal_keyword(f_set, "TEST_PROP_INT"), return,
			"TEST_PROP_INT should be NOT equal!");


	assure(!dfs_equal_keyword(f_set, "TEST_PROP_STRING"), return,
				"TEST_PROP_INT should be NOT equal!");

	assure(!dfs_equal_keyword(f_set, "TEST_PROP_FLOAT"), return,
				"TEST_PROP_FLOAT should be NEVER equal!");

	cleanup;

	f_set = get_fset_kwords(CPL_FALSE, CPL_FALSE, bias_fname,
					flat_fname);

	assure(dfs_equal_keyword(f_set, "TEST_PROP_INT"), return,
			"TEST_PROP_INT should be NOT equal!");


	assure(dfs_equal_keyword(f_set, "TEST_PROP_STRING"), return,
				"TEST_PROP_INT should be NOT equal!");

	assure(!dfs_equal_keyword(f_set, "TEST_PROP_FLOAT"), return,
				"TEST_PROP_FLOAT should be NEVER equal!");

	assure(dfs_equal_keyword(f_set, "TEST_PROP_NOT_EXISTING_STRING"), return,
					"Not existing property should be treated as found!");

	cleanup;
}

//Utility functions implementation
static cpl_parameterlist*
get_test_default_parameterlist(int bool_default,
					int int_default,
					double double_default,
					const char* string_default)
{
	return get_test_parameterlist(bool_default, bool_default, int_default,
			int_default, double_default, double_default, string_default,
			string_default);
}

static cpl_parameterlist*
get_test_parameterlist(int bool_default,
					int bool_value,
					int int_default,
					int int_value,
					double double_default,
					double double_value,
					const char* string_default,
					const char* string_value)
{
    cpl_parameterlist *parameters = cpl_parameterlist_new();

    cpl_parameter* p1 = cpl_parameter_new_value(test_bool_name, CPL_TYPE_BOOL,
    		"Boolean for test", test_context, bool_default);
	cpl_parameter_set_alias(p1, CPL_PARAMETER_MODE_CLI, test_bool_alias);

	cpl_parameter_set_bool(p1, bool_value);
	cpl_parameterlist_append(parameters, p1);


	cpl_parameter* p2 = cpl_parameter_new_value(test_int_name, CPL_TYPE_INT,
			"Integer for test", test_context, int_default);
	cpl_parameter_set_alias(p2, CPL_PARAMETER_MODE_CLI, test_int_alias);

	cpl_parameter_set_int(p2, int_value);
	cpl_parameterlist_append(parameters, p2);


	cpl_parameter* p3 = cpl_parameter_new_value(test_double_name, CPL_TYPE_DOUBLE,
			"Double for test", test_context, double_default);
	cpl_parameter_set_alias(p3, CPL_PARAMETER_MODE_CLI, test_double_alias);

	cpl_parameter_set_double(p3, double_value);
	cpl_parameterlist_append(parameters, p3);


	cpl_parameter* p4 = cpl_parameter_new_value(test_string_name, CPL_TYPE_STRING,
			"String for test", test_context, string_default);
	cpl_parameter_set_alias(p4, CPL_PARAMETER_MODE_CLI, test_string_alias);

	cpl_parameter_set_string(p4, string_value);
	cpl_parameterlist_append(parameters, p4);

	return parameters;
}

static void fill_table(cpl_table* table,
						int bool_table, int int_table,
						double double_table, const char* string_table)
{
	cpl_table_new_column(table, test_bool_alias, CPL_TYPE_INT);
	cpl_table_new_column(table, test_int_alias, CPL_TYPE_INT);
	cpl_table_new_column(table, test_double_alias, CPL_TYPE_DOUBLE);
	cpl_table_new_column(table, test_string_alias, CPL_TYPE_STRING);

	cpl_table_set_int   (table, test_bool_alias,   0, bool_table);
	cpl_table_set_int   (table, test_int_alias,    0, int_table);
	cpl_table_set_double(table, test_double_alias, 0, double_table);
	cpl_table_set_string(table, test_string_alias, 0, string_table);
}

static void
add_keyword(cpl_frame* frame, cpl_boolean use_val1)
{
	const char* fname = cpl_frame_get_filename(frame);

	cpl_propertylist* l =
			cpl_propertylist_load(fname, 0);

	cpl_test_eq_error(CPL_ERROR_NONE, cpl_error_get_code());

	//supported
	cpl_propertylist_append_string(l, "TEST_PROP_STRING", use_val1 ?
			"testString1" : "testString2");
	cpl_propertylist_append_int(l, "TEST_PROP_INT", use_val1 ? 1 : 2);


	//unsupported
	cpl_propertylist_append_float(l, "TEST_PROP_FLOAT",use_val1 ? 1.1f : 2.2f);

	cpl_propertylist_save(l, fname, CPL_IO_CREATE);

	cpl_propertylist_delete(l);

	cpl_test_eq_error(CPL_ERROR_NONE, cpl_error_get_code());
}

static void
add_common_keywords(cpl_frame* f1, cpl_frame* f2,
		cpl_boolean use_first_on_1, cpl_boolean use_first_on_2)
{
	add_keyword(f1, use_first_on_1);
	add_keyword(f2, use_first_on_2);
}

static cpl_frameset* get_fset_kwords(cpl_boolean use_first_on_1,
		cpl_boolean use_first_on_2, const char* bias_fname,
		const char* sky_flat_fname)
{

	const char* bias_tag = "BIAS";
	const char* flat_tag = "FLAT";

	cpl_frame* f1 = create_bias(bias_fname, bias_tag, CPL_FRAME_GROUP_CALIB);
	cpl_frame* f2 = create_sky_flat(sky_flat_fname, flat_tag,
				CPL_FRAME_GROUP_CALIB, 1.0);

	cpl_frame* f3 = cpl_frame_new();
	cpl_frame_set_filename(f3, "other.txt");
	cpl_frame_set_tag(f3, "OTHER_DATA");

	FILE *f = fopen("other.txt", "w");

	if(!f) return NULL;

	const char *text = "This is NOT A FITS file!";
	fprintf(f, "%s", text);
	fclose(f);

	cpl_frameset* f_set = cpl_frameset_new();

	add_common_keywords(f1, f2, use_first_on_1, use_first_on_2);

	cpl_frameset_insert(f_set, f1);
	cpl_frameset_insert(f_set, f2);
	cpl_frameset_insert(f_set, f3);

	return f_set;
}


 //Test of dfs module
int main(void)
{
    TEST_INIT;
    
    test_dfs_get_parameters();

    cpl_test_eq_error(cpl_error_get_code(), CPL_ERROR_NONE);

    test_dfs_load();

    cpl_test_eq_error(cpl_error_get_code(), CPL_ERROR_NONE);

    test_dfs_proplist_add();

    cpl_test_eq_error(cpl_error_get_code(), CPL_ERROR_NONE);

    test_dfs_equal_keywords();

    cpl_test_eq_error(cpl_error_get_code(), CPL_ERROR_NONE);

    TEST_END;
}
