/*
 * This file is part of the KMOS Library
 * Copyright (C) 2007-2008 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
 */

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

#include <cpl.h>

#include "kmo_priv_lcorr.h"

/**
    @defgroup kmo_priv_lcorr_test   kmo_priv_lcorr unit tests

    @{
*/

void kmo_test_verbose_off();

/**
    @brief   test for kmo_lcorr_create_lambda_vector()
*/
void test_kmo_lcorr_create_lambda_vector(cpl_propertylist *header) {

    cpl_vector *spectrum = NULL;
    double tol = 0.000001;

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_lambda_vector(NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    cpl_test_noneq_ptr(NULL, spectrum = kmo_lcorr_create_lambda_vector(header));
    cpl_test_abs(cpl_vector_get(spectrum, 0), 1.46000003814697, tol);
    cpl_test_abs(cpl_vector_get(spectrum, 1), 1.4604639053577531, tol);
    cpl_test_abs(cpl_vector_get(spectrum, 1000), 1.9238672489300339, tol);
    cpl_test_abs(cpl_vector_get(spectrum, 1001), 1.924331116140817, tol);
    cpl_test_abs(cpl_vector_get(spectrum, 2046), 2.4090723514091188, tol);
    cpl_test_abs(cpl_vector_get(spectrum, 2047), 2.4095362186199019, tol);

    cpl_vector_delete(spectrum);
}

void test_kmo_lcorr_read_reference_spectrum(const char *filename, cpl_vector *lambda) {

    cpl_bivector *spectrum = NULL;
    double tol = 0.000001;

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_read_reference_spectrum(NULL, lambda));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_read_reference_spectrum(NULL, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    cpl_test_noneq_ptr(NULL, spectrum = kmo_lcorr_read_reference_spectrum(filename, lambda));
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),0), 0.045893, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1), 0.658884, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1165), 0.21426816, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1166), 0.90487508, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1488), 0.077060191, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1489), 0.80229586, tol);
    cpl_bivector_delete(spectrum);

    cpl_test_noneq_ptr(NULL, spectrum = kmo_lcorr_read_reference_spectrum(filename, NULL));
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),0), 1.40000, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),1), 1.40001, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),2), 1.40002, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),0), 0.00077955999026654127, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),1), 0.00073621104860563684, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),2), 0.00069448298810292073, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),10553), 1.50553, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),10554), 1.50554, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_x(spectrum),10555), 1.50555, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),10553), 1.2842280400003749, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),10554), 1.2861153919782762, tol);
    cpl_test_abs(cpl_vector_get(cpl_bivector_get_y(spectrum),10555), 1.2852328623318428, tol);
    cpl_bivector_delete(spectrum);
}
void test_kmo_lcorr_extract_spectrum (
        cpl_imagelist *obj_cube,
        cpl_propertylist *header,
        cpl_vector *ref_spectrum) {

    cpl_bivector *spectrum = NULL;
    double min_frac = 0.8;
    cpl_vector *range = cpl_vector_new(4);
    cpl_vector *broken_range = cpl_vector_new(3);
    cpl_vector_set(range,0,1.5);
    cpl_vector_set(range,1,1.7);
    cpl_vector_set(range,2,2.0);
    cpl_vector_set(range,3,2.3);

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_extract_spectrum(NULL, NULL, min_frac, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_extract_spectrum(NULL, NULL, min_frac, range));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    spectrum = kmo_lcorr_extract_spectrum(obj_cube, header, min_frac, range);
    cpl_bivector_delete(spectrum);

    spectrum = kmo_lcorr_extract_spectrum(obj_cube, header, min_frac, NULL);
    cpl_test_vector_abs(cpl_bivector_get_y(spectrum), ref_spectrum, 1.);
//    cpl_vector_save(cpl_bivector_get_y(spectrum), "lcorr_spec.fits", CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
    cpl_bivector_delete(spectrum);

    cpl_vector_delete(range);
    cpl_vector_delete(broken_range);

}

void test_kmo_lcorr_create_object_mask (cpl_imagelist *cube,
        const cpl_vector *lambda,
        cpl_image *obj_mask){

    cpl_vector *range = cpl_vector_new(4);
    cpl_vector *broken_range = cpl_vector_new(3);
    cpl_vector *empty_vector = cpl_vector_new(1);
    cpl_image *returned_mask = NULL;
    float min_frac = 0.8;
    float mask_range_data []= {
            1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
            0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
            0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
            0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
            0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
            0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
    };

    cpl_vector_set(range,0,1.5);
    cpl_vector_set(range,1,1.7);
    cpl_vector_set(range,2,2.0);
    cpl_vector_set(range,3,2.3);

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(NULL, min_frac, lambda, range));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(cube, min_frac, NULL, range));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(cube, min_frac, lambda, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(NULL, min_frac, NULL, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(cube, min_frac, lambda, broken_range));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_test_eq_ptr(NULL, kmo_lcorr_create_object_mask(cube, min_frac, empty_vector, range));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    /* --- valid tests --- */
    returned_mask = kmo_lcorr_create_object_mask(cube, min_frac, lambda, range);
//    float *mask_data = (float *) cpl_image_get_data(returned_mask);
//    for (int x=0; x<14; x++) {
//        for (int y=0; y<14; y++) {
//            printf("%4.1f ",mask_data[ x+y*14]);
//        }
//        printf("\n");
//    }
//    printf("\n");
//    for (int x=0; x<14; x++) {
//        for (int y=0; y<14; y++) {
//            printf("%4.1f ",mask_range_data[ x+y*14]);
//        }
//        printf("\n");
//    }
//    printf("\n");
    cpl_image *test_img = cpl_image_wrap_float(14,14,mask_range_data);
    cpl_test_image_abs(returned_mask, test_img, 0.01);
    cpl_image_unwrap(test_img);
    cpl_image_delete(returned_mask);

    returned_mask = kmo_lcorr_create_object_mask(cube, min_frac, NULL, NULL);
    cpl_test_image_abs(returned_mask, obj_mask, 0.01);
    cpl_vector_delete(range);
    cpl_vector_delete(broken_range);
    cpl_vector_delete(empty_vector);
    cpl_image_delete(returned_mask);
}

void test_kmo_lcorr_get_peak_positions(cpl_bivector *spectrum) {

    cpl_array *pos;
    double min_frac = 0.2;
    cpl_vector *range = cpl_vector_new(4);
    cpl_vector *broken_range = cpl_vector_new(3);

    cpl_vector_set(range,0,1.5);
    cpl_vector_set(range,1,1.7);
    cpl_vector_set(range,2,2.0);
    cpl_vector_set(range,3,2.3);

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_get_peak_positions(NULL, min_frac, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    cpl_test_noneq_ptr(NULL, pos = kmo_lcorr_get_peak_positions(spectrum, min_frac, NULL));
    cpl_test_eq(cpl_array_get_size(pos), 77);
    cpl_array_delete(pos);

    cpl_test_noneq_ptr(NULL, pos = kmo_lcorr_get_peak_positions(spectrum, min_frac, range));
    cpl_test_eq(cpl_array_get_size(pos), 40);
    cpl_test_eq(cpl_array_get_int(pos,0,NULL),10554);
    cpl_test_eq(cpl_array_get_int(pos,1,NULL),10689);
    cpl_test_eq(cpl_array_get_int(pos,20,NULL),26923);
    cpl_test_eq(cpl_array_get_int(pos,21,NULL),27088);


    cpl_vector_delete(range);
    cpl_vector_delete(broken_range);
    cpl_array_delete(pos);
}

void test_kmo_lcorr_get_peak_lambdas(cpl_bivector *spectrum) {

    cpl_vector *pos;
    double min_frac = 0.2;
    double tol = 0.00001;
    cpl_vector *range = cpl_vector_new(4);
    cpl_vector *broken_range = cpl_vector_new(3);

    cpl_vector_set(range,0,1.5);
    cpl_vector_set(range,1,1.7);
    cpl_vector_set(range,2,2.0);
    cpl_vector_set(range,3,2.3);

    /* --- invalid tests --- */
    cpl_test_eq_ptr(NULL, kmo_lcorr_get_peak_lambdas(NULL, min_frac, NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    cpl_test_noneq_ptr(NULL, pos = kmo_lcorr_get_peak_lambdas(spectrum, min_frac, NULL));
    cpl_test_eq(cpl_vector_get_size(pos), 77);
    cpl_vector_delete(pos);

    cpl_test_noneq_ptr(NULL, pos = kmo_lcorr_get_peak_lambdas(spectrum, min_frac, range));
    cpl_test_abs(cpl_vector_get_size(pos), 40, tol);
    cpl_test_abs(cpl_vector_get(pos,0), 1.50554, tol);
    cpl_test_abs(cpl_vector_get(pos,1), 1.50689, tol);
    cpl_test_abs(cpl_vector_get(pos,20), 1.66923, tol);
    cpl_test_abs(cpl_vector_get(pos,21), 1.67088, tol);


    cpl_vector_delete(range);
    cpl_vector_delete(broken_range);
    cpl_vector_delete(pos);
}

void test_kmo_lcorr_crosscorrelate_spectra (cpl_bivector *object,
        cpl_bivector *reference,
        cpl_vector *peaks,
        char *band_id) {
    cpl_polynomial *coeffs = NULL;
    cpl_size pows[1];
    double tol = 0.00001;

    coeffs = kmo_lcorr_crosscorrelate_spectra(object, reference, peaks, band_id);
    cpl_test_eq(cpl_polynomial_get_degree(coeffs),2);
    double test_results [] = { -0.000574546 , 0.00057516 , -0.000145847 };
    int i = 0;
    for (i = 0; i <= cpl_polynomial_get_degree(coeffs); i++) {
        pows[0] = i;
        cpl_test_abs(cpl_polynomial_get_coeff(coeffs,pows), test_results[i], tol);
    }

    cpl_polynomial_delete(coeffs);
}

/**
    @brief   Test of helper functions for kmo_reconstruct
*/
int main()
{
    cpl_imagelist  *obj_cube = NULL;
    cpl_image      *obj_mask = NULL;
    cpl_vector     *obj_spectrum_file = NULL,
                   *lambda = NULL,
                   *peaks = NULL;
    cpl_bivector   *ref_spectrum = NULL,
                   *obj_spectrum = NULL;
    cpl_propertylist *obj_header = NULL;

    cpl_test_init("usd-help@eso.org", CPL_MSG_WARNING);
    kmo_test_verbose_off();

//    cpl_msg_set_level(CPL_MSG_INFO);
    cpl_vector *range = cpl_vector_new(4);
    cpl_vector_set(range,0,1.5);
    cpl_vector_set(range,1,1.7);
    cpl_vector_set(range,2,2.0);
    cpl_vector_set(range,3,2.3);
    char *my_path = cpl_sprintf("%s/ref_data/lcorr_obj_cube.fits", getenv("srcdir"));
    obj_cube = cpl_imagelist_load(my_path, CPL_TYPE_FLOAT, 0);
    obj_header = cpl_propertylist_load(my_path,0);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/ref_data/lcorr_obj_mask.fits", getenv("srcdir"));
    obj_mask = cpl_image_load(my_path, CPL_TYPE_FLOAT, 0, 0);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/ref_data/lcorr_obj_spectrum.fits", getenv("srcdir"));
    obj_spectrum_file = cpl_vector_load(my_path, 0);
    cpl_free(my_path);

    test_kmo_lcorr_create_lambda_vector(obj_header);
    lambda = kmo_lcorr_create_lambda_vector(obj_header);
    test_kmo_lcorr_extract_spectrum(obj_cube, obj_header, obj_spectrum_file);
    test_kmo_lcorr_create_object_mask(obj_cube, lambda, obj_mask);
    my_path = cpl_sprintf("%s/ref_data/lcorr_ohspecHK.fits", getenv("srcdir"));
    test_kmo_lcorr_read_reference_spectrum(my_path, lambda);
    ref_spectrum = kmo_lcorr_read_reference_spectrum(my_path, NULL);
    cpl_free(my_path);
    test_kmo_lcorr_get_peak_positions(ref_spectrum);
    test_kmo_lcorr_get_peak_lambdas(ref_spectrum);
    obj_spectrum = kmo_lcorr_extract_spectrum(obj_cube, obj_header, 0.8, NULL);
    peaks = kmo_lcorr_get_peak_lambdas(ref_spectrum, 0.2, range);
    test_kmo_lcorr_crosscorrelate_spectra(obj_spectrum, ref_spectrum, peaks, "HK");

    cpl_imagelist_delete(obj_cube);
    cpl_propertylist_delete(obj_header);
    cpl_image_delete(obj_mask);
    cpl_vector_delete(obj_spectrum_file);
    cpl_bivector_delete(ref_spectrum);
    cpl_bivector_delete(obj_spectrum);
    cpl_vector_delete(lambda);
    cpl_vector_delete(range);
    cpl_vector_delete(peaks);

    return cpl_test_end(0);
}

/** @} */
