/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set sw=2 sts=2 et cin: */
/*
 * This file is part of the MUSE Instrument Pipeline
 * Copyright (C) 2014 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <string.h>

#include <muse.h>

/*----------------------------------------------------------------------------*/
/**
  @brief    Test program to check that the functions from the muse_rvcorrect
            module work when called with necessary data.

  This program explicitely tests
    muse_rvcorrect_select_type
    muse_rvcorrect
 */
/*----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
  UNUSED_ARGUMENTS(argc, argv);
  cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_DEBUG);

  /* test muse_rvcorrect_select_type() */
  cpl_errorstate state = cpl_errorstate_get();
  muse_rvcorrect_type type = muse_rvcorrect_select_type("bary");
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test(type == MUSE_RVCORRECT_BARY);
  type = muse_rvcorrect_select_type("helio");
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test(type == MUSE_RVCORRECT_HELIO);
  type = muse_rvcorrect_select_type("geo");
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test(type == MUSE_RVCORRECT_GEO);
  type = muse_rvcorrect_select_type("none");
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test(type == MUSE_RVCORRECT_NONE);
  type = muse_rvcorrect_select_type("none");
  /* failure cases */
  type = muse_rvcorrect_select_type(NULL);
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_NULL_INPUT);
  cpl_errorstate_set(state);
  cpl_test(type == MUSE_RVCORRECT_UNKNOWN);
  type = muse_rvcorrect_select_type("geobla");
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT);
  cpl_errorstate_set(state);
  cpl_test(type == MUSE_RVCORRECT_UNKNOWN);
  type = muse_rvcorrect_select_type("none_");
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT);
  cpl_errorstate_set(state);
  cpl_test(type == MUSE_RVCORRECT_UNKNOWN);

  /* test muse_rvcorrect() */
  /* create two minimal pixel tables from headers of observations *
   * of Tol 1924-416 in June 2014 and August 2014.                */
  muse_pixtable *pt1 = cpl_calloc(1, sizeof(muse_pixtable)),
                *pt2 = cpl_calloc(1, sizeof(muse_pixtable));
  pt1->table = muse_cpltable_new(muse_pixtable_def, 5),
  pt2->table = muse_cpltable_new(muse_pixtable_def, 5);
  /* fill five example wavelengths */
  cpl_table_set(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, 4650.);
  cpl_table_set(pt1->table, MUSE_PIXTABLE_LAMBDA, 1, 4800.);
  cpl_table_set(pt1->table, MUSE_PIXTABLE_LAMBDA, 2, 5555.);
  cpl_table_set(pt1->table, MUSE_PIXTABLE_LAMBDA, 3, 7000.);
  cpl_table_set(pt1->table, MUSE_PIXTABLE_LAMBDA, 4, 9300.);
  cpl_table_set(pt2->table, MUSE_PIXTABLE_LAMBDA, 0, 4650.);
  cpl_table_set(pt2->table, MUSE_PIXTABLE_LAMBDA, 1, 4800.);
  cpl_table_set(pt2->table, MUSE_PIXTABLE_LAMBDA, 2, 5555.);
  cpl_table_set(pt2->table, MUSE_PIXTABLE_LAMBDA, 3, 7000.);
  cpl_table_set(pt2->table, MUSE_PIXTABLE_LAMBDA, 4, 9300.);
  /* use the data from muse_test_astro.c for the headers */
  pt1->header = cpl_propertylist_new();
  pt2->header = cpl_propertylist_new();
  /* exposure from June 2014 */
  cpl_propertylist_append_string(pt1->header, "OBJECT", "ESO338-IG04"); // Original target.
  cpl_propertylist_append_double(pt1->header, "RA", 291.993241); // [deg] 19:27:58.3 RA (J2000) pointing
  cpl_propertylist_append_double(pt1->header, "DEC", -41.57611); // [deg] -41:34:34.0 DEC (J2000) pointing
  cpl_propertylist_append_double(pt1->header, "EQUINOX", 2000.0); // Standard FK5
  cpl_propertylist_append_string(pt1->header, "RADECSYS", "FK5"); // Coordinate system
  cpl_propertylist_append_double(pt1->header, "EXPTIME", 750.0000000); // Integration time
  cpl_propertylist_append_double(pt1->header, "MJD-OBS", 56833.23799131); // Obs start
  cpl_propertylist_append_string(pt1->header, "DATE-OBS", "2014-06-25T05:42:42.449"); // Observing date
  cpl_propertylist_append_double(pt1->header, "UTC", 20553.000); // [s] 05:42:33.000 UTC
  cpl_propertylist_append_double(pt1->header, "LST", 69246.094); // [s] 19:14:06.094 LST
  cpl_propertylist_append_double(pt1->header, "ESO TEL GEOELEV", 2648.0); // [m] Elevation above sea level
  cpl_propertylist_append_double(pt1->header, "ESO TEL GEOLAT", -24.6270); // [deg] Tel geo latitute (+=North)
  cpl_propertylist_append_double(pt1->header, "ESO TEL GEOLON", -70.4040); // [deg] Tel geo longitude (+=East)
  /* exposure from August 2014 */
  cpl_propertylist_append_string(pt2->header, "OBJECT", "Tol 1924-416"); // Original target.
  cpl_propertylist_append_double(pt2->header, "RA", 291.992791); // [deg] 19:27:58.2 RA (J2000) pointing
  cpl_propertylist_append_double(pt2->header, "DEC", -41.57483); // [deg] -41:34:29.3 DEC (J2000) pointing
  cpl_propertylist_append_double(pt2->header, "EQUINOX", 2000.0); // Standard FK5
  cpl_propertylist_append_string(pt2->header, "RADECSYS", "FK5"); // Coordinate system
  cpl_propertylist_append_double(pt2->header, "EXPTIME", 900.0000000); // Integration time
  cpl_propertylist_append_double(pt2->header, "MJD-OBS", 56872.09891026); // Obs start
  cpl_propertylist_append_string(pt2->header, "DATE-OBS", "2014-08-03T02:22:25.846"); // Observing date
  cpl_propertylist_append_double(pt2->header, "UTC", 8536.000); // [s] 02:22:16.000 UTC
  cpl_propertylist_append_double(pt2->header, "LST", 66421.853); // [s] 18:27:01.853 LST
  cpl_propertylist_append_double(pt2->header, "ESO TEL GEOELEV", 2648.0); // [m] Elevation above sea level
  cpl_propertylist_append_double(pt2->header, "ESO TEL GEOLAT", -24.6270); // [deg] Tel geo latitute (+=North)
  cpl_propertylist_append_double(pt2->header, "ESO TEL GEOLON", -70.4040); // [deg] Tel geo longitude (+=East)

  state = cpl_errorstate_get();
  muse_rvcorrect(pt1, MUSE_RVCORRECT_BARY);
  muse_rvcorrect(pt2, MUSE_RVCORRECT_BARY);
  cpl_test(cpl_errorstate_is_equal(state));
  /* the output headers should now list the computed RV correction and type */
  cpl_test_abs(cpl_propertylist_get_double(pt1->header, MUSE_HDR_PT_RVCORR),
               6.49092037559768719746, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt1->header, MUSE_HDR_PT_RVCORR),
                          "bary"));
  cpl_test_abs(cpl_propertylist_get_double(pt2->header, MUSE_HDR_PT_RVCORR),
               -11.37728421731174321962, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt2->header, MUSE_HDR_PT_RVCORR),
                          "bary"));
#if 0
  state = cpl_errorstate_get();
  muse_pixtable_dump(pt1, 0, 5, 1);
  muse_pixtable_dump(pt2, 0, 5, 1);
  cpl_errorstate_set(state); /* reset, complains about missing pixtable headers */
#endif
  /* check that it doesn't correct it again */
  cpl_test(muse_rvcorrect(pt1, MUSE_RVCORRECT_HELIO) == CPL_ERROR_NONE);
  cpl_test_abs(cpl_propertylist_get_double(pt1->header, MUSE_HDR_PT_RVCORR),
               6.49092037559768719746, DBL_EPSILON);
  /* although "heliocentric" was requested, the header should still *
   * say "bary" if the correction was (successfully) skipped        */
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt1->header, MUSE_HDR_PT_RVCORR),
                          "bary"));

  /* check against results computed with IRAF dopcor, ensure    *
   * a precision of 0.0002 Angstrom or 1/6250th of a MUSE pixel */
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, NULL), 4650.10067999176, 0.0002);
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 1, NULL), 4800.10392773343, 0.0002);
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 2, NULL), 5555.12027469983, 0.0002);
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 3, NULL), 7000.15156127791, 0.0002);
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 4, NULL), 9300.20135998351, 0.0002);

  cpl_test_abs(cpl_table_get(pt2->table, MUSE_PIXTABLE_LAMBDA, 0, NULL), 4649.82353338571, 0.0002);
  cpl_test_abs(cpl_table_get(pt2->table, MUSE_PIXTABLE_LAMBDA, 1, NULL), 4799.81784091428, 0.0002);
  cpl_test_abs(cpl_table_get(pt2->table, MUSE_PIXTABLE_LAMBDA, 2, NULL), 5554.78918880809, 0.0002);
  cpl_test_abs(cpl_table_get(pt2->table, MUSE_PIXTABLE_LAMBDA, 3, NULL), 6999.73435133333, 0.0002);
  /* here allow degradation to 1/3125th pixel to pass: */
  cpl_test_abs(cpl_table_get(pt2->table, MUSE_PIXTABLE_LAMBDA, 4, NULL), 9299.64706677142, 0.0004);

  /* try again for the other two implemented velocity corrections; but now   *
   * do not check the wavelengths, since we verified them already once;      *
   * remove the headers of the first run, otherwise the test will be skipped */
  cpl_propertylist_erase(pt1->header, MUSE_HDR_PT_RVCORR);
  cpl_propertylist_erase(pt2->header, MUSE_HDR_PT_RVCORR);
  state = cpl_errorstate_get();
  muse_rvcorrect(pt1, MUSE_RVCORRECT_HELIO);
  muse_rvcorrect(pt2, MUSE_RVCORRECT_HELIO);
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test_abs(cpl_propertylist_get_double(pt1->header, MUSE_HDR_PT_RVCORR),
               6.49438528031983164368, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt1->header, MUSE_HDR_PT_RVCORR),
                          "helio"));
  cpl_test_abs(cpl_propertylist_get_double(pt2->header, MUSE_HDR_PT_RVCORR),
               -11.37327183169344024805, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt2->header, MUSE_HDR_PT_RVCORR),
                          "helio"));
  cpl_propertylist_erase(pt1->header, MUSE_HDR_PT_RVCORR);
  cpl_propertylist_erase(pt2->header, MUSE_HDR_PT_RVCORR);
  state = cpl_errorstate_get();
  muse_rvcorrect(pt1, MUSE_RVCORRECT_GEO);
  muse_rvcorrect(pt2, MUSE_RVCORRECT_GEO);
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test_abs(cpl_propertylist_get_double(pt1->header, MUSE_HDR_PT_RVCORR),
               -0.20908024205152309083, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt1->header, MUSE_HDR_PT_RVCORR),
                          "geo"));
  cpl_test_abs(cpl_propertylist_get_double(pt2->header, MUSE_HDR_PT_RVCORR),
               -0.25217220106373228194, DBL_EPSILON);
  cpl_test_nonnull(strstr(cpl_propertylist_get_comment(pt2->header, MUSE_HDR_PT_RVCORR),
                          "geo"));
  /* test no correction */
  cpl_propertylist_erase(pt1->header, MUSE_HDR_PT_RVCORR);
  double lambda = cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, NULL);
  state = cpl_errorstate_get();
  cpl_test(muse_rvcorrect(pt1, MUSE_RVCORRECT_NONE) == CPL_ERROR_NONE);
  cpl_test(cpl_errorstate_is_equal(state));
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, NULL),
               lambda, FLT_EPSILON); /* wavelengths unchanged */

  /* check failure cases */
  state = cpl_errorstate_get();
  lambda = cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, NULL);
  muse_rvcorrect(pt1, MUSE_RVCORRECT_UNKNOWN);
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT);
  cpl_errorstate_set(state);
  cpl_test_abs(cpl_table_get(pt1->table, MUSE_PIXTABLE_LAMBDA, 0, NULL),
               lambda, FLT_EPSILON); /* wavelengths unchanged */
  state = cpl_errorstate_get();
  muse_rvcorrect(NULL, MUSE_RVCORRECT_HELIO);
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_NULL_INPUT);
  cpl_errorstate_set(state);
  state = cpl_errorstate_get();

  muse_pixtable_delete(pt1);
  muse_pixtable_delete(pt2);

  return cpl_test_end(0);
}
