/* -*- 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) 2018 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 <muse.h>

/*----------------------------------------------------------------------------*/
/**
  @brief   Test program to check that function(s) from muse_raman
           work as expected when called with the necessary data.

  This program explicitely tests
    XXX
    muse_raman_lines_load

    muse_postproc_correct_raman
 */
/*----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
  cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_DEBUG);

  /* testing of muse_resampling_cube*() */
  /* load pixel table, it has spatial units in pixels */
  muse_pixtable *ptpix = NULL;
  if (argc > 1) {
    /* assume that the 1st parameter is the filename of a pixel table */
    ptpix = muse_pixtable_load(argv[1]);
  } else {
    ptpix = muse_pixtable_load(BASEFILENAME"_pixtable_short.fits");
  }
  if (!ptpix) {
    return cpl_test_end(1);
  }

  /* create a fake processing structure using a fake recipe */
  cpl_recipe *recipe = cpl_calloc(1, sizeof(cpl_recipe));
  recipe->frames = cpl_frameset_new();
  recipe->parameters = cpl_parameterlist_new();
  cpl_frame *f = cpl_frame_new();
  cpl_frame_set_filename(f, BASEFILENAME"_lines.fits");
  cpl_frame_set_tag(f, MUSE_TAG_RAMAN_LINES);
  cpl_frameset_insert(recipe->frames, f);
  f = cpl_frame_new();
  cpl_frame_set_filename(f, BASEFILENAME"_lsf_profile.fits");
  cpl_frame_set_tag(f, MUSE_TAG_LSF_PROFILE);
  cpl_frameset_insert(recipe->frames, f);
  muse_processing *proc = muse_processing_new("raman_test", recipe);
 
  /* load all necessary calibrations and optional inputs *
   * and store them in a post-processing structure       */
  muse_postproc_properties *prop
    = muse_postproc_properties_new(MUSE_POSTPROC_SCIPOST);
  cpl_errorstate state = cpl_errorstate_get();
  prop->raman_lines = muse_raman_lines_load(proc);
  cpl_test(cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_NONE);
  prop->lsf_cube = muse_lsf_cube_load_all(proc);

  /* test success case of muse_postproc_correct_raman() */
  prop->skymodel_params.fraction = 0.75;
  prop->skymodel_params.ignore = 0.05;
  prop->skymodel_params.crsigmac = 15.;
  prop->raman_width = 20.;
  muse_datacube *ramancube = NULL;

  muse_pixtable *pt1 = muse_pixtable_duplicate(ptpix);
  state = cpl_errorstate_get();
  cpl_error_code rc = muse_postproc_correct_raman(pt1, prop, NULL, &ramancube);
  cpl_test(cpl_errorstate_is_equal(state) && rc == CPL_ERROR_NONE);
  muse_pixtable_delete(pt1);

  /* use the outputs to check that the statistics make sense */
  cpl_test_nonnull(ramancube);
  cpl_image *ramanimage = cpl_imagelist_get(ramancube->data, 0);
  cpl_stats *s = cpl_stats_new_from_image(ramanimage, CPL_STATS_ALL);
  cpl_msg_debug(__func__, "ramanimage stats: %f +/- %f (%f) %f ... %f",
                cpl_stats_get_mean(s),
                cpl_stats_get_stdev(s),
                cpl_stats_get_median(s),
                cpl_stats_get_min(s),
                cpl_stats_get_max(s));
  cpl_test_abs(cpl_stats_get_mean(s), 1.0, 0.01);
  cpl_test_abs(cpl_stats_get_stdev(s), 0.015, 0.01);
  cpl_test_abs(cpl_stats_get_median(s), 1.0, 0.01);
  cpl_test(cpl_stats_get_min(s) > 0.95);
  cpl_test(cpl_stats_get_max(s) < 1.06);
  cpl_stats_delete(s);
  muse_datacube_delete(ramancube);

  /* add testing of cases with different wavelength coverage */
  muse_pixtable *pts[5] = { /* ptpix has 6450 ... 6900 */
    muse_pixtable_extract_wavelength(ptpix, 6450., 6470.), /* too blue */
    muse_pixtable_extract_wavelength(ptpix, 6840., 6900.), /* too red */
    muse_pixtable_extract_wavelength(ptpix, 6500., 6800.), /* in between */
    muse_pixtable_extract_wavelength(ptpix, 6450., 6500.), /* covering O2 */
    muse_pixtable_extract_wavelength(ptpix, 6700., 6900.)  /* covering N2 */
  };
  int i;
  for (i = 0; i < 5; i++) {
    float llo = cpl_propertylist_get_float(pts[i]->header, MUSE_HDR_PT_LLO),
          lhi = cpl_propertylist_get_float(pts[i]->header, MUSE_HDR_PT_LHI);
    cpl_msg_info(__func__,
                 "_________________ range %d (%.1f ... %.1f) _________________",
                 i+1, llo, lhi);
    muse_pixtable *pt2 = muse_pixtable_duplicate(pts[i]);

    state = cpl_errorstate_get();
    rc = muse_postproc_correct_raman(pts[i], prop, NULL, NULL);
    cpl_test(cpl_errorstate_is_equal(state) && rc == CPL_ERROR_NONE);
    muse_pixtable_delete(pts[i]);

    state = cpl_errorstate_get();
    rc = muse_postproc_correct_raman(pt2, prop, NULL, &ramancube);
    cpl_test(cpl_errorstate_is_equal(state) && rc == CPL_ERROR_NONE);
    if (i > 2) {
      cpl_test_nonnull(ramancube);
      muse_datacube_delete(ramancube);
    } else {
      cpl_test_null(ramancube);
    }
    muse_pixtable_delete(pt2);
  } /* for i (all special cases) */
  cpl_msg_info(__func__, "______________ done with the ranges. ______________");

  /* test failure cases of muse_postproc_correct_raman() */
  /* case 1: sky fraction of zero leads to failed fit */
  prop->skymodel_params.fraction = 0.0;
  state = cpl_errorstate_get();
  rc = muse_postproc_correct_raman(ptpix, prop, NULL, NULL);
  cpl_test(!cpl_errorstate_is_equal(state) &&
           cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT);
  cpl_errorstate_set(state);
  /* case 2 & 3: NULL inputs */
  state = cpl_errorstate_get();
  rc = muse_postproc_correct_raman(NULL, prop, NULL,  NULL);
  cpl_test(!cpl_errorstate_is_equal(state) && rc == CPL_ERROR_NULL_INPUT);
  cpl_errorstate_set(state);
  rc = muse_postproc_correct_raman(ptpix, NULL, NULL,  NULL);
  cpl_test(!cpl_errorstate_is_equal(state) && rc == CPL_ERROR_NULL_INPUT);
  cpl_errorstate_set(state);
  /* case 4 & 5: NULL calibrations */
  muse_lsf_cube **lsf = prop->lsf_cube;
  prop->lsf_cube = NULL;
  rc = muse_postproc_correct_raman(ptpix, prop, NULL,  NULL);
  cpl_test(!cpl_errorstate_is_equal(state) && rc == CPL_ERROR_DATA_NOT_FOUND);
  cpl_errorstate_set(state);
  prop->lsf_cube = lsf;
  cpl_table *rlines = prop->raman_lines;
  prop->raman_lines = NULL;
  rc = muse_postproc_correct_raman(ptpix, prop, NULL,  NULL);
  cpl_test(!cpl_errorstate_is_equal(state) && rc == CPL_ERROR_DATA_NOT_FOUND);
  cpl_errorstate_set(state);
  prop->raman_lines = rlines;
  /* case 6: small raman width */
  prop->raman_width = 2.5;
  rc = muse_postproc_correct_raman(ptpix, prop, NULL,  NULL);
  cpl_test(!cpl_errorstate_is_equal(state) && rc == CPL_ERROR_ILLEGAL_INPUT);
  cpl_errorstate_set(state);

  muse_pixtable_delete(ptpix);
  muse_postproc_properties_delete(prop);
  muse_processing_delete(proc);
  cpl_frameset_delete(recipe->frames);
  cpl_parameterlist_delete(recipe->parameters);
  cpl_free(recipe);

  return cpl_test_end(0);
}
