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

#include <cpl_plot.h>

#include "kmclipm_math.h"

#include "kmo_utils.h"
#include "kmo_cpl_extensions.h"
#include "kmo_debug.h"

/**
    @defgroup kmo_cpl_extensions_test   kmo_cpl_extensions unit tests

    @{
*/

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

/**
    @brief   tests kmo_image_histogram()
 */
void test_kmo_image_histogram(void)
{
    cpl_image   *img    = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_vector  *res    = NULL;
    float       tol     = 0.01;

    kmo_test_fill_image(img, 0.0, 1.0);
    res = kmo_image_histogram(img, 5);
    cpl_test_abs(cpl_vector_get_mean(res), 0.8, tol);

    cpl_image_delete(img);
    cpl_vector_delete(res);
}

/**
    @brief   tests kmo_image_sort()
 */
void test_kmo_image_sort(void)
{
    cpl_image   *img    = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_vector  *res    = NULL;
    float       tol     = 0.01;

    kmo_test_fill_image(img, 0.0, 1.0);
    res = kmo_image_sort(img);
    cpl_test_abs(cpl_vector_get_mean(res), 1.5, tol);

    cpl_image_delete(img);
    cpl_vector_delete(res);
}

/**
    @brief   tests kmo_image_fill()
 */
void test_kmo_image_fill(void)
{
    cpl_image   *img    = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    float       tol     = 0.01;

    cpl_test_eq(CPL_ERROR_NONE, kmo_image_fill(img, 2));
    cpl_test_abs(cpl_image_get_mean(img), 2, tol);

    cpl_image_delete(img);
}

/**
    @brief   tests kmo_image_get_flux()
 */
void test_kmo_image_get_flux(void)
{
    kmo_test_verbose_off();
    kmo_image_get_flux(NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmo_test_verbose_on();

    cpl_image *i = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(i, 1, 1, 1);
    cpl_image_set(i, 1, 2, 2);
    cpl_image_set(i, 2, 1, 3);
    cpl_image_set(i, 2, 2, 4);

    cpl_test_abs(kmo_image_get_flux(i), 10.0, DBL_EPSILON);
    cpl_test_error(CPL_ERROR_NONE);

    cpl_image_delete(i);
}

/**
    @brief   tests kmo_image_reject_from_mask()
 */
void test_kmo_image_reject_from_mask(void)
{
    cpl_image *img = cpl_image_new(2, 1, CPL_TYPE_FLOAT);
    cpl_image *map = cpl_image_new(2, 1, CPL_TYPE_FLOAT);
    cpl_image *im2 = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(img, 1, 1, 5);
    cpl_image_set(img, 2, 1, 7);
    cpl_image_set(map, 1, 1, 0);
    cpl_image_set(map, 2, 1, 1);

    kmo_test_verbose_off();
    kmo_image_reject_from_mask(NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmo_image_reject_from_mask(img, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmo_image_reject_from_mask(im2, map);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    kmo_test_verbose_on();

    cpl_test_abs(6, cpl_image_get_mean(img), 0.01);
    kmo_image_reject_from_mask(img, map);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(7, cpl_image_get_mean(img), 0.01);

    cpl_image_delete(img);
    cpl_image_delete(map);
    cpl_image_delete(im2);
}

/**
    @brief   tests kmo_imagelist_power()
 */
void test_kmo_imagelist_power(void)
{
    cpl_imagelist   *cube1  = cpl_imagelist_new();

    int             rej     = 0;
    float           tol     = 0.01;

    kmo_test_alloc_cube(cube1, 5, 5, 3);
    kmo_test_fill_cube(cube1, 0.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_power(cube1, 2.0));
    cpl_test_abs(cpl_image_get(cpl_imagelist_get(cube1, 2), 2, 2, &rej),
                 64.0, tol);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_power(cube1, 0.0));
    cpl_test_abs(cpl_image_get(cpl_imagelist_get(cube1, 2), 2, 2, &rej),
                 1.0, tol);

    cpl_image *tmp = cpl_imagelist_get(cube1,0);
    cpl_image_set(tmp, 1, 1, 0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_power(cube1, -1.0));

    cpl_image *img = cpl_imagelist_get(cube1, 0);
    cpl_test_abs(kmo_imagelist_get_mean(cube1), 0.986667, tol);
    cpl_test_eq(1, cpl_image_count_rejected(img));

    kmo_test_verbose_on();

    kmo_test_fill_cube(cube1, 1.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_power(cube1, 0.0));
    cpl_test_eq(cpl_image_get(cpl_imagelist_get(cube1, 2), 2, 2, &rej),
                1.0);

    cpl_imagelist_delete(cube1);
}

/**
    @brief   tests test_kmo_imagelist_get_mean()
 */
void test_kmo_imagelist_get_mean(void)
{
    cpl_imagelist   *cube1  = cpl_imagelist_new();

    float           tol     = 0.01;

    kmo_test_alloc_cube(cube1, 5, 5, 3);
    kmo_test_fill_cube(cube1, 0.0, 1.0);

    cpl_test_abs(kmo_imagelist_get_mean(cube1), 13, tol);

    cpl_imagelist_delete(cube1);
}

/**
    @brief   tests test_kmo_imagelist_get_flux()
 */
void test_kmo_imagelist_get_flux(void)
{
    cpl_imagelist   *cube1  = cpl_imagelist_new();

    float           tol     = 0.01;

    kmo_test_alloc_cube(cube1, 5, 5, 3);
    kmo_test_fill_cube(cube1, 0.0, 1.0);

    cpl_test_abs(kmo_imagelist_get_flux(cube1), 975, tol);

    cpl_imagelist_delete(cube1);
}

/**
    @brief   tests kmo_imagelist_turn()
 */
void test_kmo_imagelist_turn(void)
{
    int t;
    kmo_test_verbose_off();
    kmo_imagelist_turn(NULL, 0);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmo_test_verbose_on();

    cpl_imagelist *l = cpl_imagelist_new();
    cpl_image *i = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(i, 1, 1, 1);
    cpl_image_set(i, 1, 2, 2);
    cpl_image_set(i, 2, 1, 3);
    cpl_image_set(i, 2, 2, 4);
    cpl_imagelist_set(l, i, 0);
    i = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(i, 1, 1, 5);
    cpl_image_set(i, 1, 2, 6);
    cpl_image_set(i, 2, 1, 7);
    cpl_image_set(i, 2, 2, 8);
    cpl_imagelist_set(l, i, 1);

    kmo_imagelist_turn(l, 1);
    cpl_test_error(CPL_ERROR_NONE);
    i = cpl_imagelist_get(l, 0);
    cpl_test_abs(cpl_image_get(i, 1, 1, &t), 3, DBL_EPSILON);
    cpl_test_abs(cpl_image_get(i, 1, 2, &t), 1, DBL_EPSILON);
    i = cpl_imagelist_get(l, 1);
    cpl_test_abs(cpl_image_get(i, 1, 1, &t), 7, DBL_EPSILON);
    cpl_test_abs(cpl_image_get(i, 1, 2, &t), 5, DBL_EPSILON);

    cpl_imagelist_delete(l);
}

/**
    @brief   tests kmo_vector_divide()
 */
void test_kmo_vector_divide(void)
{
    cpl_vector           *vec1  = cpl_vector_new(3),
                         *vec2  = cpl_vector_new(3);

    cpl_vector_set(vec1, 0, -1.0);
    cpl_vector_set(vec1, 1, 0.0);
    cpl_vector_set(vec1, 2, 1.0);

    cpl_vector_set(vec2, 0, 0.0);
    cpl_vector_set(vec2, 1, 0.0);
    cpl_vector_set(vec2, 2, 0.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_vector_divide(vec1, vec2));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_vector_get(vec1, 0)) != 0);
    cpl_test_eq(TRUE, isnan(cpl_vector_get(vec1, 1)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_vector_get(vec1, 2)) != 0);

    cpl_vector_delete(vec1);
    cpl_vector_delete(vec2);
}

/**
    @brief   tests kmo_array_fill_int()
*/
void test_kmo_array_fill_int(void)
{
    kmo_test_verbose_off();
    kmo_array_fill_int(NULL, 0.0);
    cpl_error_reset();
    kmo_test_verbose_on();

    cpl_array *a = cpl_array_new(5, CPL_TYPE_INT);
    kmo_array_fill_int(a, 9);
    cpl_test_abs(cpl_array_get_mean(a), 9, DBL_EPSILON);

    cpl_array_delete(a);
}

/**
    @brief   tests kmo_vector_histogram_old()
*/
void test_kmo_vector_histogram_old(void)
{
    /* all the same values in input vector --> all values in first entry of out-vector */
    cpl_vector* in = cpl_vector_new(108);
    cpl_vector_fill(in, 1.23457834678);
    cpl_vector *out = NULL;
    cpl_test_nonnull(out = kmo_vector_histogram_old(in, 100));
    cpl_test_eq(108, cpl_vector_get(out, 0));
    cpl_test_eq(0, cpl_vector_get(out, 1));

    cpl_vector_delete(in); in = NULL;
    cpl_vector_delete(out); out = NULL;

    /* binsize > size of input vector*/
    in = cpl_vector_new(90);
    cpl_vector_fill(in, 1.23457834678);
    cpl_test_nonnull(out = kmo_vector_histogram_old(in, 100));
    cpl_test_eq(90, cpl_vector_get(out, 0));
    cpl_test_eq(0, cpl_vector_get(out, 1));

    cpl_vector_delete(in); in = NULL;
    cpl_vector_delete(out); out = NULL;
}

/**
    @brief   tests kmo_image_divide_scalar()
 */
void test_kmo_image_divide_scalar(void)
{
    cpl_image       *img1   = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    int             rej     = 0;

    kmo_test_fill_image(img1, -1.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_image_divide_scalar(img1, 0.0));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 1, 1, &rej)) == -1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(img1, 2, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 2, 2, &rej)) == 1);

    cpl_image_delete(img1);
}

/**
    @brief   tests kmo_image_divide()
 */
void test_kmo_image_divide(void)
{
    cpl_image       *img1   = cpl_image_new(2, 2, CPL_TYPE_FLOAT),
                    *img2   = cpl_image_new(2, 2, CPL_TYPE_FLOAT);

    int             rej     = 0;

    kmo_test_fill_image(img1, -1.0, 1.0);
    kmo_test_fill_image(img1, -1.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_image_divide(img1, img2));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 1, 1, &rej)) == -1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(img1, 2, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(img1, 2, 2, &rej)) == 1);

    cpl_image_delete(img1);
    cpl_image_delete(img2);
}

/**
    @brief   tests kmo_image_power()
 */
void test_kmo_image_power(void)
{
    cpl_image       *img1   = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    float           tol     = 0.01;
    int             rej     = 0;

    kmo_test_fill_image(img1, -1.0, 1.0);
    cpl_test_eq(CPL_ERROR_NONE, kmo_image_power(img1, 0.0));
    cpl_test_abs(cpl_image_get(img1, 2, 2, &rej), 1.0, tol);

    cpl_image_delete(img1);
}

/**
    @brief   tests kmo_imagelist_divide_scalar()
 */
void test_kmo_imagelist_divide_scalar(void)
{
    cpl_imagelist   *cube3  = cpl_imagelist_new();
    cpl_image       *tmp    = NULL;
    int             rej     = 0;

    kmo_test_alloc_cube(cube3, 2, 2, 2);
    kmo_test_fill_cube(cube3, -1.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_divide_scalar(cube3, 0.0));
    tmp = cpl_imagelist_get(cube3, 0);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 1, &rej)) == -1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(tmp, 2, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 2, &rej)) == 1);
    tmp = cpl_imagelist_get(cube3, 1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(tmp, 1, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 1, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 2, &rej)) == 1);

    cpl_imagelist_delete(cube3);
}

/**
    @brief   tests kmo_imagelist_divide()
 */
void test_kmo_imagelist_divide(void)
{
    cpl_imagelist   *cube4  = cpl_imagelist_new(),
                    *cube5  = cpl_imagelist_new();
    cpl_image       *tmp    = NULL;
    int             rej     = 0;

    kmo_test_alloc_cube(cube4, 2, 2, 2);

    kmo_test_alloc_cube(cube5, 2, 2, 2);
    kmo_test_fill_cube(cube5, -1.0, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmo_imagelist_divide(cube5, cube4));
    tmp = cpl_imagelist_get(cube5, 0);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 1, &rej)) == -1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(tmp, 2, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 2, &rej)) == 1);
    tmp = cpl_imagelist_get(cube5, 1);
    cpl_test_eq(TRUE, isnan(cpl_image_get(tmp, 1, 1, &rej)));
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 1, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 1, 2, &rej)) == 1);
    cpl_test_eq(TRUE, kmclipm_is_inf(cpl_image_get(tmp, 2, 2, &rej)) == 1);

    cpl_imagelist_delete(cube4);
    cpl_imagelist_delete(cube5);
}

/**
    @brief   Tests for cpl extension functions.
 */
int main(void)
{
    cpl_test_init("<usd-help@eso.org>", CPL_MSG_WARNING);

    // ---------------------- missing in CPL -----------------------------------
    test_kmo_image_histogram();
    test_kmo_image_sort();
    test_kmo_image_fill();
    test_kmo_image_get_flux();
    test_kmo_image_reject_from_mask();

    test_kmo_imagelist_power();
    test_kmo_imagelist_get_mean();
    test_kmo_imagelist_get_flux();
    test_kmo_imagelist_turn();
    test_kmo_array_fill_int();
    test_kmo_vector_histogram_old();

    // ---------------------- CPL-overrides ------------------------------------
    test_kmo_vector_divide();

    test_kmo_image_divide_scalar();
    test_kmo_image_divide();
    test_kmo_image_power();

    test_kmo_imagelist_divide_scalar();
    test_kmo_imagelist_divide();

    return cpl_test_end(0);
}

/** @} */
