/* $Id: fors_image-test.c,v 1.20 2007-11-23 14:24:24 jmlarsen 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: jmlarsen $
 * $Date: 2007-11-23 14:24:24 $
 * $Revision: 1.20 $
 * $Name: not supported by cvs2svn $
 */

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

#include <fors_image.h>
#include <fors_pfits.h>

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

#include <math.h>

/**
 * @defgroup fors_image_test  Test of image module
 */

/**@{*/

#undef cleanup
#define cleanup \
do { \
    fors_setting_delete(&setting); \
} while(0)

/**
 * @brief  Test functions
 */
static void
test_image(void)
{
    const int nx = 200; /* Duplication here. Must be updated
                           in synchronization with nx and ny in ./test_simulate.c */
    const int ny = 200;
    const char *const filename = "fors_image.fits";

    cpl_frame *frame = cpl_frame_new();
    cpl_propertylist *header = cpl_propertylist_new();
    fors_setting *setting = NULL;
    cpl_image *data     = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *variance = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    fors_image *image;
    double saturated;

    cpl_image_add_scalar(variance, 1.0);
    cpl_image_set(data, 1, 1, 1); /* One non-saturated pixel */
    image   = fors_image_new(data, variance);
    
    /* save, load */
    {
        double exptime = 423;
        create_standard_keys(header, exptime);
    }

    fors_image_save(image, header, filename);
    fors_image_delete(&image);
    cpl_frame_set_filename(frame, filename);

    setting = fors_setting_new(frame);

    image = fors_image_load(frame, NULL, setting, &saturated);
    
    test_rel( saturated, 100 * (1 - 1.0 / (nx*ny)), 0.01 ); 
    test_eq( nx, fors_image_get_size_x(image) );
    test_eq( ny, fors_image_get_size_y(image) );

    cpl_frame_delete(frame);
    cpl_propertylist_delete(header);
    fors_image_delete(&image);


    /* Test croppping */
    data     = cpl_image_new(3, 2, FORS_IMAGE_TYPE);
    variance = cpl_image_new(3, 2, FORS_IMAGE_TYPE);

    {
        int x, y;
        for (y = 1; y <= 2; y++) {
            for (x = 1; x <= 3; x++) {
                cpl_image_set(data, x, y, x*y);
            }
        }
        /*
          Input data now:
            2 4 6
            1 2 3
        */
    }
    cpl_image_add_scalar(variance, 1.0);
    image = fors_image_new(data, variance);
    
    int xlo = 2;
    int xhi = 2;
    int ylo = 1;
    int yhi = 2;
    fors_image_crop(image, 
                    xlo, ylo, xhi, yhi);

    /*
         Should have now:
              4
              2
     */
    test_eq( fors_image_get_size_x(image), 1 );
    test_eq( fors_image_get_size_y(image), 2 );

    test_rel( fors_image_get_min(image), 2, 0.0001 );
    test_rel( fors_image_get_max(image), 4, 0.0001 );
    test_rel( fors_image_get_mean(image, NULL), 3, 0.0001 );
    test_rel( fors_image_get_stdev(image, NULL), sqrt(2), 0.0001 );

    fors_image_delete(&image);

    cleanup;
    return;
}

/**
 * @brief  Median filtering benchmark
 */
static void
test_median_filter(void)
{
    //const int nx = 4000;
    //const int ny = 2000;
    const int nx = 400;
    const int ny = 200;

    const int xradius = 1;
    const int yradius = 1;
    const int xstart = 1;
    const int ystart = 1;
    const int xend = nx;
    const int yend = ny;
    const int xstep = 1;
    const int ystep = 1;

    cpl_image *data     = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *variance = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    
    fors_image *image;
    cpl_image *smooth;

    cpl_image_add_scalar(variance, 1.0);

    image = fors_image_new(data, variance);
    bool use_data = true;
    smooth = fors_image_filter_median_create(image,
                                             xradius, yradius,
                                             xstart, ystart,
                                             xend, yend,
                                             xstep, ystep,
                                             use_data);
    
    cpl_image_delete(smooth);
    fors_image_delete(&image);

    return;
}

#undef cleanup
#define cleanup \
do { \
    fors_image_delete(&left); \
    fors_image_delete(&right); \
} while(0)

/**
 * @brief   Test image subtraction
 */
static void
test_subtract(void)
{
    /* Simulate data */
    const int nx = 20;
    const int ny = 30;
    const double values  = 17;
    const double variance = 5;
    cpl_image *left_data     = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *left_variance = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *right_data;
    cpl_image *right_variance;
    fors_image *left, *right;
    double error_before, error_after;

    cpl_image_add_scalar(left_data, values);
    cpl_image_add_scalar(left_variance, variance);
    
    right_data    = cpl_image_multiply_scalar_create(left_data, 0.6);
    right_variance = cpl_image_duplicate(left_variance);

    left  = fors_image_new(left_data , left_variance);
    right = fors_image_new(right_data, right_variance);

    error_before = fors_image_get_error_mean(left, NULL);

    /* Call function */
    fors_image_subtract(left, right);

    /* Check results */
    error_after = fors_image_get_error_mean(left, NULL);
    test_rel( fors_image_get_mean(left, NULL), values*(1-0.6), 0.001);
    test_rel( error_after, error_before*sqrt(2.0), 0.001);
    
    cleanup;
    return;
}

#undef cleanup
#define cleanup \
do { \
    fors_image_delete(&image); \
} while(0)

/**
 * @brief   Test image exponentiation
 */
static void
test_exponential(void)
{
    /* Simulate data */
    const int nx = 20;
    const int ny = 30;
    const double values  = 17;
    const double var_val = 5;
    cpl_image *data     = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *variance = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    double base = 10;
    double dbase = -1;
    fors_image *image;
    
    cpl_image_add_scalar(data, values);
    cpl_image_add_scalar(variance, var_val);
    
    image = fors_image_new(data, variance);

    /* Call function */
    fors_image_exponential(image, base, dbase);

    /* Check results */
    test_rel( fors_image_get_mean(image, NULL), pow(base, values), 0.001);
    
    cleanup;
    return;
}


#undef cleanup
#define cleanup \
do { \
    fors_image_delete(&image); \
} while(0)
/**
 * @brief   Test image division
 */
static void
test_divide(void)
{
    /* Simulate data */
    const int nx = 20;
    const int ny = 30;
    const double values  = 17;
    const double variance_value = 5;
    cpl_image *data     = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    cpl_image *variance = cpl_image_new(nx, ny, FORS_IMAGE_TYPE);
    fors_image *image = NULL;
    double error_before, error_after;

    cpl_image_add_scalar(data    , values);
    cpl_image_add_scalar(variance, variance_value);
    
    image  = fors_image_new(data, variance);

    error_before = 
        fors_image_get_error_mean(image, NULL) /
        fors_image_get_mean      (image, NULL);

    /* Call function */
    fors_image_divide(image, image);

    /* Check results,
     * relative errors add in quadrature
     */
    error_after =
        fors_image_get_error_mean(image, NULL) /
        fors_image_get_mean      (image, NULL);
    test_rel( fors_image_get_mean(image, NULL), 1.0, 0.001 );
    test_rel( error_after, error_before*sqrt(2.0), 0.001 );

    cleanup;
    return;
}



/**
 * @brief   Test of image module
 */
int main(void)
{
    TEST_INIT;

    test_image();
    
    test_median_filter();

    test_subtract();

    test_divide();

    test_exponential();

    TEST_END;
}

/**@}*/
