/* $Id: kmo_shift-test.c,v 1.7 2013-10-08 11:18:57 aagudo Exp $
 *
 * This file is part of the KMOS 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: aagudo $
 * $Date: 2013-10-08 11:18:57 $
 * $Revision: 1.7 $
 * $Name: not supported by cvs2svn $
 */

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

#include <string.h>
#include <sys/stat.h>

#include <cpl.h>

#include "kmclipm_functions.h"
#include "kmclipm_constants.h"

#include "kmo_dfs.h"

const char  *test_global_path_test_data;
float   test_global_seed_data, test_global_seed_noise;
int     test_global_size_x, test_global_size_y, test_global_size_z, test_global_nr_frames;
void kmo_get_pipe_command(char*, const char*, const char*, int);
int kmo_test_file_exists(const char*);
void kmo_test_verbose_off();
void kmo_test_verbose_on();
double kmo_test_esorex_data(const char*, int);
const char* kmo_test_cat_strings(char*, const char*, const char*);
int  kmo_test_create_RAW_data(int, int, const char*, char**, char**, int*, int, char**, char**, int*, int, float, float);
int  kmo_test_create_RAW_data_zero(int, int, const char*);
int  kmo_test_create_F2D_data(int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F2D_data_noise(int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F2I_data(int, int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F2I_data_noise(int, int, int, const char*, char *, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F3I_data(int, int, int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F3I_data_infinite(int, int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F3I_data2(int, int, int, int, int*, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F3I_data_noise(int, int, int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F1I_data(int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_F1I_data_noise(int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);
int  kmo_test_create_B2D_data(int, int, int, const char*, char*, char*, int*, int, char**, char**, int*, int);

const char      *path_recipe       = "kmo_shift/";

const char      *valid_files[]     = {"pyramid_orig.fits",
                                      "pyramid_orig_noise.fits"};

/**
    @defgroup kmo_shift_test  kmo_shift_test unit tests

    @{
*/

/**
  @brief   test
 */
static void
test_shift(const char *sof_path,
             const char *pars,
             int ret_val)
{
    // create esorex-command
    char esorex_command[256];
    sprintf(esorex_command,
            "esorex --suppress-prefix=TRUE kmo_shift %s %s", pars, sof_path);

    kmo_get_pipe_command(esorex_command,
                         "log_kmo_shift.txt", NULL, TRUE);

    // execute esorex-command
    if (ret_val == 0) {
        cpl_test_eq(0, system(esorex_command));
    } else {
        cpl_test_noneq(0, system(esorex_command));
    }

}

/**
    @brief
        Generates test data for kmo_shift.

    @param path      Path where the generated test data should be saved to.
*/
static int kmo_generate_test_data_shift(const char *path)
{
    int         i                   = 0;

    char        file_path[256],
                file_path_noise[256],
                file_ref[256],
                name1[256],
                name2[256],
                cmd[256];

    char *test_global_path_ref_data = cpl_sprintf("%s/ref_data/", getenv("srcdir"));
    kmo_test_cat_strings(file_ref, test_global_path_ref_data, "ref_cube_arc.fits");

    cpl_imagelist       *cube_in = kmclipm_imagelist_load(file_ref,
                                                      CPL_TYPE_FLOAT, 1),
                        *cube_out = NULL;
    cpl_propertylist    *pl;
    cpl_image           *img;
    int                 ix = 0,
                        iy = 0;

    float               *p = NULL;

    strcpy(name1, IFU_NAME_PREFIX);
    strcat(name1, "1");
    strcat(name1, IFU_NAME_POSTFIX);

    strcpy(name2, IFU_NAME_PREFIX);
    strcat(name2, "2");
    strcat(name2, IFU_NAME_POSTFIX);

    /* ----- valid test data ----- */
    //
    // create pyramid_orig.fits (center at (5,3), object in IFU1)
    //
    kmo_test_cat_strings(file_path, path, valid_files[0]);
    pl = kmclipm_propertylist_load(file_ref, 0);
    cpl_propertylist_save(pl, file_path, CPL_IO_DEFAULT);
    cpl_propertylist_delete(pl); pl = NULL;

    float o14[14] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9};
    float o13[14] = {10,10,10,10,10,10,10,10,10,10,10,10,10,10};
    float o12[14] = {11,11,11,11,11,11,11,11,11,11,11,11,11,11};
    float o11[14] = {12,12,12,12,12,12,12,12,12,12,12,12,12,11};
    float o10[14] = {13,13,13,13,13,13,13,13,13,13,13,13,12,11};
    float  o9[14] = {14,14,14,14,14,14,14,14,14,14,14,13,12,11};
    float  o8[14] = {15,15,15,15,15,15,15,15,15,15,14,13,12,11};
    float  o7[14] = {16,16,16,16,16,16,16,16,16,15,14,13,12,11};
    float  o6[14] = {16,17,17,17,17,17,17,17,16,15,14,13,12,11};
    float  o5[14] = {16,17,18,18,18,18,18,17,16,15,14,13,12,11};
    float  o4[14] = {16,17,18,19,19,19,18,17,16,15,14,13,12,11};
    float  o3[14] = {16,17,18,19,20,19,18,17,16,15,14,13,12,11};
    float  o2[14] = {16,17,18,19,19,19,18,17,16,15,14,13,12,11};
    float  o1[14] = {16,17,18,18,18,18,18,17,16,15,14,13,12,11};

    cube_out = cpl_imagelist_new();
    for (i = 0; i < cpl_imagelist_get_size(cube_in); i++) {
        img = cpl_image_new(14, 14, CPL_TYPE_FLOAT);
        p = cpl_image_get_data_float(img);
        iy =  0; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o1[ix];
        iy =  1; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o2[ix];
        iy =  2; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o3[ix];
        iy =  3; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o4[ix];
        iy =  4; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o5[ix];
        iy =  5; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o6[ix];
        iy =  6; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o7[ix];
        iy =  7; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o8[ix];
        iy =  8; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o9[ix];
        iy =  9; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o10[ix];
        iy = 10; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o11[ix];
        iy = 11; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o12[ix];
        iy = 12; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o13[ix];
        iy = 13; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = o14[ix];
        cpl_imagelist_set(cube_out, img, i);
    }

    pl = kmclipm_propertylist_load(file_ref, 1);
    cpl_propertylist_update_string(pl, name1, "XXX");
    cpl_propertylist_erase (pl, name2);
    cpl_imagelist_save(cube_out, file_path, CPL_BPP_IEEE_FLOAT, pl, CPL_IO_EXTEND);
    cpl_imagelist_delete(cube_out); cube_out = NULL;

    // adding noise
    kmo_test_cat_strings(file_path_noise, path, valid_files[1]);
    strcpy(cmd, "cp ");
    strcat(cmd, file_path);
    strcat(cmd, " ");
    strcat(cmd, file_path_noise);
    system(cmd);

    float n14[14] = { .9, .9, .9, .9, .9, .9, .9, .9, .9, .9, .9, .9, .9, .9};
    float n13[14] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
    float n12[14] = {1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1};
    float n11[14] = {1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.1};
    float n10[14] = {1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.2,1.1};
    float  n9[14] = {1.4,1.4,1.4,1.4,1.4,1.4,1.4,1.4,1.4,1.4,1.4,1.3,1.2,1.1};
    float  n8[14] = {1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.4,1.3,1.2,1.1};
    float  n7[14] = {1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n6[14] = {1.6,1.7,1.7,1.7,1.7,1.7,1.7,1.7,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n5[14] = {1.6,1.7,1.8,1.8,1.8,1.8,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n4[14] = {1.6,1.7,1.8,1.9,1.9,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n3[14] = {1.6,1.7,1.8,1.9,2.0,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n2[14] = {1.6,1.7,1.8,1.9,1.9,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1};
    float  n1[14] = {1.6,1.7,1.8,1.8,1.8,1.8,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1};

    cube_out = cpl_imagelist_new();
    for (i = 0; i < cpl_imagelist_get_size(cube_in); i++) {
        img = cpl_image_new(14, 14, CPL_TYPE_FLOAT);
        p = cpl_image_get_data_float(img);
        iy =  0; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n1[ix];
        iy =  1; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n2[ix];
        iy =  2; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n3[ix];
        iy =  3; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n4[ix];
        iy =  4; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n5[ix];
        iy =  5; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n6[ix];
        iy =  6; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n7[ix];
        iy =  7; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n8[ix];
        iy =  8; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n9[ix];
        iy =  9; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n10[ix];
        iy = 10; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n11[ix];
        iy = 11; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n12[ix];
        iy = 12; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n13[ix];
        iy = 13; for (ix = 0; ix < 14; ix++) p[ix+iy*14] = n14[ix];
        cpl_imagelist_set(cube_out, img, i);
    }

    cpl_propertylist_update_string(pl, EXTNAME, "IFU.1.NOISE");
    cpl_imagelist_save(cube_out, file_path_noise, CPL_BPP_IEEE_FLOAT, pl,
                       CPL_IO_EXTEND);
    cpl_imagelist_delete(cube_in); cube_in = NULL;
    cpl_imagelist_delete(cube_out); cube_out = NULL;

    cpl_propertylist_delete(pl); pl = NULL;

    cpl_free(test_global_path_ref_data);

    return 0;
}

void check_headers(const char *orig_path, const char *out_path, int ext,
                   double crpix1, double crpix2, int nax1, int nax2,
                   double tol)
{
    cpl_propertylist *h_orig = kmclipm_propertylist_load(orig_path, 1);
    cpl_propertylist *h = kmclipm_propertylist_load(out_path, ext);

    cpl_test_abs(cpl_propertylist_get_double(h, CRPIX1), crpix1, tol);
    cpl_test_abs(cpl_propertylist_get_double(h, CRPIX2), crpix2, tol);
    cpl_test_eq(cpl_propertylist_get_int(h, NAXIS1), nax1);
    cpl_test_eq(cpl_propertylist_get_int(h, NAXIS2), nax2);

    cpl_test_abs(cpl_propertylist_get_double(h, CRVAL1),
                 cpl_propertylist_get_double(h_orig, CRVAL1), tol);
    cpl_test_abs(cpl_propertylist_get_double(h, CRVAL2),
                 cpl_propertylist_get_double(h_orig, CRVAL2), tol);
    cpl_test_abs(cpl_propertylist_get_double(h, CRVAL3),
                 cpl_propertylist_get_double(h_orig, CRVAL3), tol);
    cpl_test_abs(cpl_propertylist_get_double(h, CDELT1),
                 cpl_propertylist_get_double(h_orig, CDELT1), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CDELT2),
                 cpl_propertylist_get_double(h_orig, CDELT2), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CDELT3),
                 cpl_propertylist_get_double(h_orig, CDELT3), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CRPIX3),
                 cpl_propertylist_get_double(h_orig, CRPIX3), tol);
    cpl_test_abs(cpl_propertylist_get_double(h, CD1_1),
                 cpl_propertylist_get_double(h_orig, CD1_1), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD1_2),
                 cpl_propertylist_get_double(h_orig, CD1_2), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD1_3),
                 cpl_propertylist_get_double(h_orig, CD1_3), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD2_1),
                 cpl_propertylist_get_double(h_orig, CD2_1), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD2_2),
                 cpl_propertylist_get_double(h_orig, CD2_2), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD2_3),
                 cpl_propertylist_get_double(h_orig, CD2_3), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD3_1),
                 cpl_propertylist_get_double(h_orig, CD3_1), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD3_2),
                 cpl_propertylist_get_double(h_orig, CD3_2), tol/1000);
    cpl_test_abs(cpl_propertylist_get_double(h, CD3_3),
                 cpl_propertylist_get_double(h_orig, CD3_3), tol/1000);
    cpl_propertylist_delete(h); h = NULL;
    cpl_propertylist_delete(h_orig); h_orig = NULL;
}

/**
  @brief    Test of kmo_shift recipe.
  @param    argc   the number of parameters
  @param    argv   the parameter list

  Call @c kmo_shift-test @c --generate when only the test data for this
  recipe should be generated.
  Call @c kmo_shift-test @c --no-gen when only the tests should be
  executed and no data should be generated.

 */
int main(int argc, char *argv[])
{
    float   tol = 0.01;

    char    test_path[256],
            file_path[256],
            orig_file_path[256],
            out_path[256];

    cpl_image *img = NULL;

    int tmp_int;

    cpl_test_init("<kmos-spark@mpe.mpg.de>", CPL_MSG_WARNING);

    strcpy(test_path, test_global_path_test_data);
    strcat(test_path, path_recipe);

    if (kmo_test_file_exists(test_path) == FALSE) {
        mkdir(test_path, 0777);
    }

    if ((argc == 1) || ((argc > 1) && (strcmp(argv[1], "--no-gen") != 0))) {
        cpl_test_zero(kmo_generate_test_data_shift(test_path));
    }

    if ((argc > 1) && (strcmp(argv[1], "--generate") == 0)) {
        // just generate the data
        return cpl_test_end(0);
    }

    /* ----- run the tests ----- */
    FILE *fd = fopen("log_kmo_shift.txt", "w");
    fprintf(fd, "%s\n", " ");
    fclose(fd);

    /* ----- valid tests ----- */

    //
    // whole pixel shift of IFU#1, one pix left, one pix up
    //
    kmo_test_cat_strings(file_path, test_path, valid_files[0]);

    test_shift(file_path,
                 "--ifu=1 --shifts=\"0.2,0.2\" --imethod=\"NN\" --flux=true"
                 " --wcs-only=true",
                 0);

    // check output
    strcpy(out_path, "shift");
    cpl_test_abs(kmo_test_esorex_data(out_path, 0), 13.7551, tol);
    // test for correctness of flux conservation
    kmo_test_cat_strings(orig_file_path, test_path, "pyramid_orig");
    cpl_test_abs(kmo_test_esorex_data(orig_file_path, 0),
                 kmo_test_esorex_data(out_path, 0), tol);
    strcat(out_path, ".fits");
    check_headers(file_path, out_path, 1, 5.5, 9.5, 14, 14, tol);

    img = kmclipm_image_load(out_path, CPL_TYPE_FLOAT, 0, 1);
    cpl_test_abs(cpl_image_get(img, 5, 3, &tmp_int), 20.0, tol);
    cpl_image_delete(img); img = NULL;

    //
    // sub pixel shift of IFU#1, 1.5 pix right, 1.5 pix down
    //
    test_shift(file_path,
                 "--ifu=1 --shifts=\"-0.3,-0.3\" --imethod=\"BCS\" "
                 "--flux=false --extrapolate=true",
                 0);

    // check output
    strcpy(out_path, "shift");
    cpl_test_abs(kmo_test_esorex_data(out_path, 0), 13.5845, tol);
    strcat(out_path, ".fits");
    check_headers(file_path, out_path, 1, 10.5, 4.5, 14, 14, tol);

    img = kmclipm_image_load(out_path, CPL_TYPE_FLOAT, 0, 1);
    cpl_test_abs(cpl_image_get(img, 5, 3, &tmp_int), 18.4395, tol);
    cpl_image_delete(img); img = NULL;

    //
    // sub pixel shift with noise of IFU#1, 1.2 pix left, 1.2 pix down
    //
    kmo_test_cat_strings(file_path, test_path, valid_files[1]);

    test_shift(file_path,
                 "--ifu=1 --shifts=\"-0.222,-0.222\" --imethod=\"BCS\" "
                 "--flux=false --extrapolate=true",
                 0);
    
    // check output data
    strcpy(out_path, "shift");
    cpl_test_abs(kmo_test_esorex_data(out_path, 0), 15.1473, tol);
    strcat(out_path, ".fits");
    check_headers(file_path, out_path, 1, 9.72, 5.28, 14, 14, tol);

    img = kmclipm_image_load(out_path, CPL_TYPE_FLOAT, 0, 1);
    cpl_test_abs(cpl_image_get(img, 5, 3, &tmp_int), 18.8881, tol);
    cpl_image_delete(img); img = NULL;

    // check output noise
    check_headers(file_path, out_path, 2, 9.72, 5.28, 14, 14, tol);

    img = kmclipm_image_load(out_path, CPL_TYPE_FLOAT, 0, 2);
    cpl_test_abs(cpl_image_get(img, 5, 3, &tmp_int), 1.88881, tol);
    cpl_image_delete(img); img = NULL;

    /* ----- invalid data ----- */

    return cpl_test_end(0);
}

/** @} */
