/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004-2009 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*
 * $Author: amodigli $
 * $Date: 2013-08-15 11:55:52 $
 * $Revision: 1.12 $
 */

#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_error.h>
#include <sinfo_msg.h>
#include <sinfo_utils_wrappers.h>
#include <sinfo_utl_efficiency.h>

const float EPSILON = 1E-5;

#define TEST_SERIE 5
#define TEST_SEQ_EVEN   10
#define TEST_SEQ_ODD   11
struct _Interpol_test_data_even
{
	double dataX[TEST_SEQ_EVEN];
	double dataY[TEST_SEQ_EVEN];
	double dataW[TEST_SERIE];
	double data_result[TEST_SERIE];
};

typedef struct _Interpol_test_data_even Interpol_test_data_even;

struct _Interpol_test_data_odd
{
	double dataX[TEST_SEQ_ODD];
	double dataY[TEST_SEQ_ODD];
	double dataW[TEST_SERIE];
	double data_result[TEST_SERIE];
};

typedef struct _Interpol_test_data_odd Interpol_test_data_odd;


const char* SINFO_EFF_BUG_REPORT = "amodigli@eso.org";


static const char COL_NAME_WAVELENGTH[] = "WAVELENGTH";
static const char COL_NAME_FLUX[] 		= "FLUX";
static const char COL_NAME_LAMBDA[] 	= "LAMBDA";
static const char COL_NAME_LA_SILLA[]	= "LA_SILLA";
static const char COL_NAME_EFF[]		= "EFF";
static const double C_GAIN				= 2.42;

static void interpolation_test_even(void)
{
	Interpol_test_data_even test_data[] = {
			{
					{0,1,2,3,4,6,7,8,10,11},
					{0,1,2,3,4,6,7,8,10,11},
					{-1, 5, 20, 4.5, 1.001},
					{-1, 5, 20, 4.5, 1.001},
			},
			{
					{0,1,2,3,4,6,7,8,10,11},
					{11,10,8,7,6,4,3,2,1,0},
					{-1, 5, 20, 1.5, 7.99999},
					{12, 5, -9, 9, 2.00001},
			},
			{
					{1,2,3,4,5,6,7,8,10,11},
					{1,1,1,1,1,1,1,1,1,1},
					{-1, 9, 20, 5.000001, 6.9999999},
					{1, 1, 1, 1, 1},
			},
			{
					{1, 2, 3, 4, 5, 6, 7, 8, 10, 11},
					{1, 0, 1, 0, 1, 0, 1, 0, 1,  0},
					{0, 5, 9, 8.99, 8.000001},
					{2, 1, 0.5, 0.495, 0},
			},
			{
					{1, 2, 3, 4, 5, 6, 7, 8, 10, 11},
					{1, 4, 9, 16, 25, 36, 49, 64, 100,  121},
					{0, 5, 9, 5.5, 9.9},
					{-2, 25, 82, 30.5, 98.2},
			},
	};
	int n_test = sizeof(test_data) / sizeof(test_data[0]);
	int i = 0;
	for (i = 0; i < n_test; i++)
	{
		int d = 0;
		for (d = 0; d < TEST_SERIE; d++)
		{
			float result = sinfo_data_interpolate(test_data[i].dataW[d],TEST_SEQ_EVEN, test_data[i].dataX, test_data[i].dataY);
			cpl_test_abs(result, test_data[i].data_result[d], EPSILON);
		}
	}

	return;
}
static void interpolation_test_odd(void)
{
	Interpol_test_data_odd test_data[] = {
			{
					{0,1,2,3,4,6,7,8,10,11, 12},
					{0,1,2,3,4,6,7,8,10,11, 12},
					{-1, 5, 20, 4.5, 1.001},
					{-1, 5, 20, 4.5, 1.001},
			},
			{
					{0,1,2,3,4,6,7,8,10,11, 12},
					{11,10,8,7,6,4,3,2,1,0, -1},
					{-1, 5, 20, 1.5, 7.99999},
					{12, 5, -9, 9, 2.00001},
			},
			{
					{1,2,3,4,5,6,7,8,10,11, 12},
					{1,1,1,1,1,1,1,1,1,1, 1},
					{-1, 9, 20, 5.000001, 6.9999999},
					{1, 1, 1, 1, 1},
			},
			{
					{1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12},
					{1, 0, 1, 0, 1, 0, 1, 0, 1,  0, 1},
					{0, 5, 9, 8.99, 8.000001},
					{2, 1, 0.5, 0.495, 0},
			},
			{
					{1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12},
					{1, 4, 9, 16, 25, 36, 49, 64, 100,  121, 144},
					{0, 5, 9, 5.5, 9.9},
					{-2, 25, 82, 30.5, 98.2},
			},
	};
	int n_test = sizeof(test_data) / sizeof(test_data[0]);
	int i = 0;
	for (i = 0; i < n_test; i++)
	{
		int d = 0;
		for (d = 0; d < TEST_SERIE; d++)
		{
			double result = sinfo_data_interpolate(test_data[i].dataW[d],TEST_SEQ_ODD, test_data[i].dataX, test_data[i].dataY);
			cpl_test_abs(result, test_data[i].data_result[d], EPSILON);
		}
	}

	return;
}
static void fill_table(cpl_table* ptable, int nrows, const double* data, const char* col_name )
{
	cpl_table_new_column(ptable, col_name, CPL_TYPE_DOUBLE);
	int i = 0;
	for ( i = 0; i < nrows; i++)
	{
		cpl_table_set(ptable,col_name, i,data[i]);
	};
        return;
}

const int NROWS_SPECTRUM 	= 10;
const int NROWS_ATMEXT 		= 10;
const int NROWS_REF			= 10;
static cpl_table* prepare_spectrum(void)
{
	const double wavelength_buf[] = {0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4}; // should be ordered
	const double flux_buf[] = {2e-13 , 2.1e-13, 2.2e-13, 2.3e-13, 2.4e-13, 2.5e-13, 2.6e-13, 2.7e-13, 2.8e-13, 2.9e-13};

	cpl_test(NROWS_SPECTRUM == sizeof(wavelength_buf) / sizeof(wavelength_buf[0]));
	cpl_test(NROWS_SPECTRUM == sizeof(flux_buf) / sizeof(flux_buf[0]));

	cpl_table* retval = cpl_table_new(NROWS_SPECTRUM);
	fill_table(retval,NROWS_SPECTRUM,wavelength_buf,  COL_NAME_WAVELENGTH);
	fill_table(retval,NROWS_SPECTRUM,flux_buf,  COL_NAME_FLUX);
	return retval;
}

static cpl_table* prepare_atmext(void)
{
	const double wavelength_buf[] = {0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4}; // should be ordered
	const double flux_buf[] = {1,1,1,1,1,1,1,1,1,1};

	cpl_test(NROWS_ATMEXT == sizeof(wavelength_buf) / sizeof(wavelength_buf[0]));
	cpl_test(NROWS_ATMEXT == sizeof(flux_buf) / sizeof(flux_buf[0]));

	cpl_table* retval = cpl_table_new(NROWS_ATMEXT);
	fill_table(retval,NROWS_ATMEXT,wavelength_buf,  COL_NAME_LAMBDA);
	fill_table(retval,NROWS_ATMEXT,flux_buf,  COL_NAME_LA_SILLA);
	return retval;
}

static cpl_table* prepare_ref(double exp_time, double tel_area)
{
	const double wavelength_buf[] = {0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4}; // should be ordered
	double flux_buf[] = {2e-13, 2.1e-13, 2.2e-13, 2.3e-13, 2.4e-13, 2.5e-13, 2.6e-13, 2.7e-13, 2.8e-13, 2.9e-13};

	cpl_test(NROWS_REF == sizeof(wavelength_buf) / sizeof(wavelength_buf[0]));
	cpl_test(NROWS_REF == sizeof(flux_buf) / sizeof(flux_buf[0]));
	int i = 0;
	for (i = 0; i < NROWS_REF; i++)
	{
		flux_buf[i] /= exp_time * tel_area;
	}

	cpl_table* retval = cpl_table_new(NROWS_REF);
	fill_table(retval,NROWS_REF,wavelength_buf,  COL_NAME_WAVELENGTH);
	fill_table(retval,NROWS_REF,flux_buf, COL_NAME_FLUX);
	return retval;
}
/* TODO: find a way to make work the following unit test
static void eff_test(void)
{
	cpl_table* tbl_obj_spectrum = 0;
	cpl_table* tbl_atmext = 0;
	cpl_table* tbl_ref = 0;
	double exptime = 600;
	double airmass = 1;
	double EFF_INDEX[] = {1,1,1,1,1,1,1,1,1,1};
	double tel_area = 51.2e4;
	double airprim = 1.446;
	int i = 0;
	tbl_obj_spectrum = prepare_spectrum();
	tbl_atmext = prepare_atmext();
	tbl_ref = prepare_ref(exptime, tel_area);
	const double mk2AA=1E4; // mkm/AA 
	cpl_table* tbl_result = sinfo_utl_efficiency_internal(
							      tbl_obj_spectrum,
							      tbl_atmext,
							      tbl_ref,
							      exptime,
							      airmass,
							      airprim,
							      C_GAIN,1,mk2AA,
							      "LAMBDA",
							      "LA_SILLA",
							      "LAMBDA",
							      "F_LAMBDA",
							      "BIN_WIDTH",
							      "WAVELENGTH",
							      "INT_OBJ");


	// check the result table
	cpl_test_nonnull(tbl_result);

	// size should be the same as the spectrum table
	int result_size = cpl_table_get_nrow(tbl_result);
	cpl_table_dump(tbl_result, 0, result_size, stdout);
	cpl_test(result_size == NROWS_SPECTRUM);
	for (i = 0; i < result_size; i++)
	{
		int px = 0;
		double eff_value = 0;
		check_nomsg(eff_value = cpl_table_get_double(tbl_result, COL_NAME_EFF, i, &px));
		cpl_test_abs(eff_value, EFF_INDEX[i], EPSILON);
	}
cleanup:
	// cleanup
	cpl_table_delete(tbl_obj_spectrum);
	cpl_table_delete(tbl_atmext);
	cpl_table_delete(tbl_ref);
	cpl_table_delete(tbl_result);
	return;
}
*/

int main(void)
{
	cpl_test_init(SINFO_EFF_BUG_REPORT, CPL_MSG_WARNING);
	interpolation_test_even();
	interpolation_test_odd();
        /* TODO: find a way to make work following unit test
	eff_test();
        */
	return cpl_test_end(0);
}

