/*
 * 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 "kmclipm_constants.h"

#include "kmo_functions.h"
#include "kmo_cpl_extensions.h"
#include "kmo_priv_combine.h"
#include "kmo_utils.h"

/*----------------------------------------------------------------------------*/
/**
    @defgroup kmo_priv_combine_test   kmo_priv_combine unit tests
*/
/*----------------------------------------------------------------------------*/

/**@{*/

void kmo_test_verbose_off();
void kmo_test_verbose_on();
void kmo_test_fill_cube(cpl_imagelist *cube, float seed, float offset);
void kmo_test_alloc_cube(cpl_imagelist *cube, int x, int y, int z);

/*----------------------------------------------------------------------------*/
/**
 * @brief   Test for kmo_align_subpix()
 */
/*----------------------------------------------------------------------------*/
void test_kmo_align_subpix(void)
{
    double              xshift          = 0.1,
                        yshift          = 0.3,
                        tol             = 0.00001;
    int                 xmin            = 0,
                        ymin            = 0,
                        xmax            = 0,
                        ymax            = 0;
    cpl_imagelist       *data           = cpl_imagelist_new(),
                        *noise          = NULL;
    cpl_propertylist    *header_data    = cpl_propertylist_new(),
                        *header_noise   = NULL;

    FILE                *fid            = fopen("shifts.txt", "w");

    kmo_test_alloc_cube(data, 14, 14, 10);
    kmo_test_fill_cube(data, 0.0, 1.0);

    kmo_test_verbose_off();

    // empty header_data
    kmo_align_subpix(&xshift, &yshift, &data, NULL, &header_data, NULL, 0, 
            "BCS", BCS_NATURAL, tol, fid, &xmin, &xmax, &ymin, &ymax, "gaga");
    cpl_test_error(CPL_ERROR_DATA_NOT_FOUND);

    kmo_test_verbose_on();

    // add header_data
    cpl_propertylist_append_int(header_data, NAXIS, 3);
    cpl_propertylist_append_int(header_data, NAXIS1, 14);
    cpl_propertylist_append_int(header_data, NAXIS2, 14);
    cpl_propertylist_append_int(header_data, NAXIS3, 10);
    cpl_propertylist_append_double(header_data, CRPIX1, 7.5);
    cpl_propertylist_append_double(header_data, CRPIX2, 7.5);
    cpl_propertylist_append_double(header_data, CRPIX3, 1);
    cpl_propertylist_append_double(header_data, CRVAL1, 6.12595);
    cpl_propertylist_append_double(header_data, CRVAL2, -72.08291);
    cpl_propertylist_append_double(header_data, CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data, CDELT1, -5.55555e-5);
    cpl_propertylist_append_double(header_data, CDELT2, -5.55555e-5);
    cpl_propertylist_append_double(header_data, CDELT3, 0.0002075);
    cpl_propertylist_append_string(header_data, CTYPE1, "RA---TAN");
    cpl_propertylist_append_string(header_data, CTYPE2, "DEC--TAN");
    cpl_propertylist_append_string(header_data, CTYPE3, "WAVE");
    cpl_propertylist_append_string(header_data, CUNIT1, "deg");
    cpl_propertylist_append_string(header_data, CUNIT2, "deg");
    cpl_propertylist_append_string(header_data, CUNIT3, "um");
    cpl_propertylist_append_string(header_data, "ESO PRO FRNAME", "ga");
    cpl_propertylist_append_int(header_data, "ESO PRO IFUNR", 1);

    /* CDN_M Matrix needs to be specified - PIPE-5106 PIPE-5115 */
    /* If not specified, cpl_wcs() fails  */
    cpl_propertylist_append_double(header_data, CD1_1, -5.55555e-5);
    cpl_propertylist_append_double(header_data, CD1_2, 0);
    cpl_propertylist_append_double(header_data, CD1_3, 0);
    cpl_propertylist_append_double(header_data, CD2_1, 0);
    cpl_propertylist_append_double(header_data, CD2_2, -5.55555e-5);
    cpl_propertylist_append_double(header_data, CD2_3, 0);
    cpl_propertylist_append_double(header_data, CD3_1, 0);
    cpl_propertylist_append_double(header_data, CD3_2, 0);
    cpl_propertylist_append_double(header_data, CD3_3, 0.0002075);

    kmo_align_subpix(&xshift, &yshift, &data, NULL, &header_data, NULL, 0, 
            "BCS", BCS_NATURAL, tol, fid, &xmin, &xmax, &ymin, &ymax, "gaga");
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(xmin, 0);
    cpl_test_eq(ymin, 0);
    cpl_test_eq(xmax, 14);
    cpl_test_eq(ymax, 15);
    cpl_test_abs(xshift, 0, 0.01);
    cpl_test_abs(yshift, 1, 0.01);

    // with noise (empty)
    noise = cpl_imagelist_new();
    header_noise = cpl_propertylist_new();
    kmo_align_subpix(&xshift, &yshift, &data, &noise, &header_data, 
            &header_noise, 0, "BCS", BCS_NATURAL, tol, fid, &xmin, &xmax,
            &ymin, &ymax, "gaga");
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(xmin, 0);
    cpl_test_eq(ymin, 0);
    cpl_test_eq(xmax, 14);
    cpl_test_eq(ymax, 15);
    cpl_test_abs(xshift, 0, 0.01);
    cpl_test_abs(yshift, 1, 0.01);

    // with noise
    kmo_test_alloc_cube(noise, 14, 14, 10);
    kmo_test_fill_cube(noise, 0.0, 0.1);

    kmo_align_subpix(&xshift, &yshift, &data, &noise, &header_data, 
            &header_noise, 0, "BCS", BCS_NATURAL, tol, fid, &xmin, &xmax, 
            &ymin, &ymax, "gaga");
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(xmin, 0);
    cpl_test_eq(ymin, 0);
    cpl_test_eq(xmax, 14);
    cpl_test_eq(ymax, 15);
    cpl_test_abs(xshift, 0, 0.01);
    cpl_test_abs(yshift, 1, 0.01);

    fclose(fid);

    cpl_propertylist_delete(header_data); header_data = NULL;
    cpl_propertylist_delete(header_noise); header_noise = NULL;
    cpl_imagelist_delete(data); data = NULL;
    cpl_imagelist_delete(noise); noise = NULL;
    cpl_test_error(CPL_ERROR_NONE);
}

/*----------------------------------------------------------------------------*/
/**
 * @brief   Test for kmo_priv_combine()
 */
/*----------------------------------------------------------------------------*/
void test_kmo_priv_combine(void)
{
    int                 nr_frames       = 3;
    cpl_imagelist       **data          = NULL,
                        **noise         = NULL,
                        *data_out       = NULL,
                        *noise_out      = NULL;
    cpl_propertylist    **header_data   = NULL,
                        **header_noise  = NULL;

    data = cpl_calloc(nr_frames, sizeof(cpl_imagelist*));
    header_data = cpl_calloc(nr_frames, sizeof(cpl_propertylist*));
    int i = 0;
    for (i = 0; i < nr_frames; i++) {
        data[i] = cpl_imagelist_new();
        kmo_test_alloc_cube(data[i], 14, 14, 10);
        kmo_test_fill_cube(data[i], 0.0, 1.0);
        header_data[i] = cpl_propertylist_new();
    }

    kmo_test_verbose_off();

    // empty header_data
    kmo_priv_combine(data, NULL, header_data, NULL, nr_frames, 0, "obj_bla", 
            "", "header", "BCS", "gauss", "shifts2.txt", "ksigma", 3.0, 3.0, 
            3, 5, 5, BCS_NATURAL, TRUE, &data_out, &noise_out, NULL);
    cpl_test_error(CPL_ERROR_DATA_NOT_FOUND);

    // add CD-matrix to header_data
    for (i = 0; i < nr_frames; i++) {
        cpl_propertylist_append_double(header_data[i], CD1_1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD1_2, 0);
        cpl_propertylist_append_double(header_data[i], CD1_3, 0);
        cpl_propertylist_append_double(header_data[i], CD2_1, 0);
        cpl_propertylist_append_double(header_data[i], CD2_2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD2_3, 0);
        cpl_propertylist_append_double(header_data[i], CD3_1, 0);
        cpl_propertylist_append_double(header_data[i], CD3_2, 0);
        cpl_propertylist_append_double(header_data[i], CD3_3, 0.0002075);
        cpl_propertylist_append_string(header_data[i], "ESO PRO FRNAME", "ga");
        cpl_propertylist_append_int(header_data[i], "ESO PRO IFUNR", i+1);
    }
    kmo_priv_combine(data, NULL, header_data, NULL, nr_frames, 0, "obj_bla", 
            "", "header", "BCS", "gauss", "shifts2.txt", "ksigma", 3.0, 3.0, 
            3, 5, 5, BCS_NATURAL, TRUE, &data_out,&noise_out, NULL);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

//    kmo_test_verbose_on();

    // add all the rest to header_data, without noise_data
    for (i = 0; i < 3; i++) {
        cpl_propertylist_append_int(header_data[i], NAXIS, 3);
        cpl_propertylist_append_int(header_data[i], NAXIS1, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS2, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS3, 10);
        cpl_propertylist_append_double(header_data[i], CRPIX1, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX2, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX3, 1);
        cpl_propertylist_append_double(header_data[i], CDELT1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT3, 0.0002075);
        cpl_propertylist_append_string(header_data[i], CTYPE1, "RA---TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE2, "DEC--TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE3, "WAVE");
        cpl_propertylist_append_string(header_data[i], CUNIT1, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT2, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT3, "um");
        cpl_propertylist_append_string(header_data[i], "ESO PRO FRNAME", "ga");
        cpl_propertylist_append_int(header_data[i], "ESO PRO IFUNR", i+1);
    }
    cpl_propertylist_append_double(header_data[0], CRVAL1, 6.12595);
    cpl_propertylist_append_double(header_data[0], CRVAL2, -72.08291);
    cpl_propertylist_append_double(header_data[0], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[1], CRVAL1, 6.1265);
    cpl_propertylist_append_double(header_data[1], CRVAL2, -72.09);
    cpl_propertylist_append_double(header_data[1], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[2], CRVAL1, 6.125);
    cpl_propertylist_append_double(header_data[2], CRVAL2, -72.078);
    cpl_propertylist_append_double(header_data[2], CRVAL3, 1.43099);

    kmo_priv_combine(data, NULL, header_data, NULL, nr_frames, 0, "obj_bla", 
            "", "header", "BCS", "gauss", "shifts2.txt", "ksigma", 3.0, 3.0, 
            3, 5, 5, BCS_NATURAL, TRUE, &data_out,&noise_out, NULL);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(kmo_imagelist_get_mean(data_out), 11.3376, 0.01);
    cpl_test_abs(kmo_imagelist_get_mean(noise_out), 0, 0.01);
    cpl_imagelist_delete(data_out); data_out = NULL;
    cpl_imagelist_delete(noise_out); noise_out = NULL;

    // with noise_data
    noise = cpl_calloc(nr_frames, sizeof(cpl_imagelist*));
    for (i = 0; i < nr_frames; i++) {
        cpl_imagelist_delete(data[i]); data[i] = NULL;
        data[i] = cpl_imagelist_new();
        kmo_test_alloc_cube(data[i], 14, 14, 10);
        kmo_test_fill_cube(data[i], 0.0, 1.0);

        cpl_propertylist_delete(header_data[i]); header_data[i] = NULL;
        header_data[i] = cpl_propertylist_new();
        cpl_propertylist_append_double(header_data[i], CD1_1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD1_2, 0);
        cpl_propertylist_append_double(header_data[i], CD1_3, 0);
        cpl_propertylist_append_double(header_data[i], CD2_1, 0);
        cpl_propertylist_append_double(header_data[i], CD2_2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD2_3, 0);
        cpl_propertylist_append_double(header_data[i], CD3_1, 0);
        cpl_propertylist_append_double(header_data[i], CD3_2, 0);
        cpl_propertylist_append_double(header_data[i], CD3_3, 0.0002075);
        cpl_propertylist_append_int(header_data[i], NAXIS, 3);
        cpl_propertylist_append_int(header_data[i], NAXIS1, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS2, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS3, 10);
        cpl_propertylist_append_double(header_data[i], CRPIX1, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX2, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX3, 1);
        cpl_propertylist_append_double(header_data[i], CDELT1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT3, 0.0002075);
        cpl_propertylist_append_string(header_data[i], CTYPE1, "RA---TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE2, "DEC--TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE3, "WAVE");
        cpl_propertylist_append_string(header_data[i], CUNIT1, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT2, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT3, "um");
        cpl_propertylist_append_string(header_data[i], "ESO PRO FRNAME", "ga");
        cpl_propertylist_append_int(header_data[i], "ESO PRO IFUNR", i+1);

        noise[i] = cpl_imagelist_new();
        kmo_test_alloc_cube(noise[i], 14, 14, 10);
        kmo_test_fill_cube(noise[i], 0.0, 0.1);
    }
    cpl_propertylist_append_double(header_data[0], CRVAL1, 6.12595);
    cpl_propertylist_append_double(header_data[0], CRVAL2, -72.08291);
    cpl_propertylist_append_double(header_data[0], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[1], CRVAL1, 6.1265);
    cpl_propertylist_append_double(header_data[1], CRVAL2, -72.09);
    cpl_propertylist_append_double(header_data[1], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[2], CRVAL1, 6.125);
    cpl_propertylist_append_double(header_data[2], CRVAL2, -72.078);
    cpl_propertylist_append_double(header_data[2], CRVAL3, 1.43099);

    kmo_priv_combine(data, noise, header_data, NULL, nr_frames, 0, "obj_bla", 
            "", "header", "BCS", "gauss", "shifts2.txt", "ksigma", 3.0, 3.0, 
            3, 5, 5, BCS_NATURAL, TRUE, &data_out,&noise_out, NULL);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(kmo_imagelist_get_mean(data_out), 11.3376, 0.01);
    cpl_test_abs(kmo_imagelist_get_mean(noise_out), 0, 0.01);
    cpl_imagelist_delete(data_out); data_out = NULL;
    cpl_imagelist_delete(noise_out); noise_out = NULL;

    // with noise_data and header_noise
    header_noise = cpl_calloc(nr_frames, sizeof(cpl_propertylist*));
    for (i = 0; i < nr_frames; i++) {
        cpl_imagelist_delete(data[i]); data[i] = NULL;
        data[i] = cpl_imagelist_new();
        kmo_test_alloc_cube(data[i], 14, 14, 10);
        kmo_test_fill_cube(data[i], 0.0, 1.0);

        cpl_propertylist_delete(header_data[i]); header_data[i] = NULL;
        header_data[i] = cpl_propertylist_new();
        cpl_propertylist_append_double(header_data[i], CD1_1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD1_2, 0);
        cpl_propertylist_append_double(header_data[i], CD1_3, 0);
        cpl_propertylist_append_double(header_data[i], CD2_1, 0);
        cpl_propertylist_append_double(header_data[i], CD2_2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CD2_3, 0);
        cpl_propertylist_append_double(header_data[i], CD3_1, 0);
        cpl_propertylist_append_double(header_data[i], CD3_2, 0);
        cpl_propertylist_append_double(header_data[i], CD3_3, 0.0002075);
        cpl_propertylist_append_int(header_data[i], NAXIS, 3);
        cpl_propertylist_append_int(header_data[i], NAXIS1, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS2, 14);
        cpl_propertylist_append_int(header_data[i], NAXIS3, 10);
        cpl_propertylist_append_double(header_data[i], CRPIX1, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX2, 7.5);
        cpl_propertylist_append_double(header_data[i], CRPIX3, 1);
        cpl_propertylist_append_double(header_data[i], CDELT1, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT2, -5.55555e-5);
        cpl_propertylist_append_double(header_data[i], CDELT3, 0.0002075);
        cpl_propertylist_append_string(header_data[i], CTYPE1, "RA---TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE2, "DEC--TAN");
        cpl_propertylist_append_string(header_data[i], CTYPE3, "WAVE");
        cpl_propertylist_append_string(header_data[i], CUNIT1, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT2, "deg");
        cpl_propertylist_append_string(header_data[i], CUNIT3, "um");
        cpl_propertylist_append_string(header_data[i], "ESO PRO FRNAME", "ga");
        cpl_propertylist_append_int(header_data[i], "ESO PRO IFUNR", i+1);

        cpl_imagelist_delete(noise[i]); noise[i] = NULL;
        noise[i] = cpl_imagelist_new();
        kmo_test_alloc_cube(noise[i], 14, 14, 10);
        kmo_test_fill_cube(noise[i], 0.0, 0.1);
    }
    cpl_propertylist_append_double(header_data[0], CRVAL1, 6.12595);
    cpl_propertylist_append_double(header_data[0], CRVAL2, -72.08291);
    cpl_propertylist_append_double(header_data[0], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[1], CRVAL1, 6.1265);
    cpl_propertylist_append_double(header_data[1], CRVAL2, -72.09);
    cpl_propertylist_append_double(header_data[1], CRVAL3, 1.43099);
    cpl_propertylist_append_double(header_data[2], CRVAL1, 6.125);
    cpl_propertylist_append_double(header_data[2], CRVAL2, -72.078);
    cpl_propertylist_append_double(header_data[2], CRVAL3, 1.43099);
    header_noise[0] = cpl_propertylist_duplicate(header_data[0]);
    header_noise[1] = cpl_propertylist_duplicate(header_data[1]);
    header_noise[2] = cpl_propertylist_duplicate(header_data[2]);
    kmo_priv_combine(data, noise, header_data, header_noise, nr_frames, 
            nr_frames, "obj_bla", "", "header", "BCS", "gauss", "shifts2.txt", 
            "ksigma", 3.0, 3.0, 3, 5, 5, BCS_NATURAL, TRUE, &data_out,
            &noise_out, NULL);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(kmo_imagelist_get_mean(data_out), 11.3376, 0.01);
    cpl_test_abs(kmo_imagelist_get_mean(noise_out), 0, 0.01);
    cpl_imagelist_delete(data_out); data_out = NULL;
    cpl_imagelist_delete(noise_out); noise_out = NULL;

    // free
    for (i = 0; i < nr_frames; i++) {
        cpl_imagelist_delete(data[i]); data[i] = NULL;
        cpl_imagelist_delete(noise[i]); noise[i] = NULL;
        cpl_propertylist_delete(header_data[i]); header_data[i] = NULL;
        cpl_propertylist_delete(header_noise[i]); header_noise[i] = NULL;
    }
    cpl_free(data); data = NULL;
    cpl_free(noise); noise = NULL;
    cpl_free(header_data); header_data = NULL;
    cpl_free(header_noise); header_noise = NULL;

    cpl_test_error(CPL_ERROR_NONE);
    kmo_test_verbose_on();
}

/*----------------------------------------------------------------------------*/
/**
    @brief   Test of helper functions for kmo_combine
*/
/*----------------------------------------------------------------------------*/
int main(void)
{
    cpl_test_init("<usd-help@eso.org>", CPL_MSG_WARNING);
    test_kmo_priv_combine();
    return cpl_test_end(0);
}

/** @} */
