/* -*- 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) 2010-2013 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>

#include "math.h"

/**
 * Check the statistics in a window. The window is assumed to contain only
 * statistical fluctuations in the data. Their standard deviation is compared
 * to the mean value taken from the "stat" field.
 *
 * @param aImage      input image.
 * @param tolerance   A non-negative relative tolerance.
 * @param llx         Lower left x position (FITS convention, 1 for leftmost)
 * @param lly         Lower left y position (FITS convention, 1 for lowest)
 * @param urx         Upper right x position (FITS convention)
 * @param ury         Upper right y position (FITS convention)
 */
int
check_statistics_window(muse_image *aImage, double tolerance,
                        int llx, int lly, int urx, int ury)
{
  double stddev = cpl_image_get_stdev_window(aImage->data, llx, lly, urx, ury);
  double statmean = cpl_image_get_mean_window(aImage->stat, llx, lly, urx, ury);
  double mean = cpl_image_get_mean_window(aImage->data, llx, lly, urx, ury);
  cpl_msg_debug(__func__, "%f %f %f", mean, stddev, sqrt(statmean));
  cpl_test_rel(stddev, sqrt(statmean), tolerance);
  return CPL_ERROR_NONE;
}

int
check_statistics(muse_image *aImage, double tolerance)
{
  /* List of regions to test. These regions should habe a uniform            *
   * illumination, that means that the data distribution should only reflect *
   * the statistical error.                                                  */
  int llx[] = { 47, 47, -1 };
  int urx[] = { 121, 121, -1 };
  int lly[] = { 396, 2110, -1 };
  int ury[] = { 436, 2138, -1 };
  int i_window;
  for (i_window = 0; llx[i_window] > 0; i_window++) {
    check_statistics_window(aImage, tolerance, llx[i_window], lly[i_window],
                            urx[i_window], ury[i_window]);
  }
  return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Check error statistics creation and propagation.
 */
/*----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
  UNUSED_ARGUMENTS(argc, argv);
  cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_INFO);
  int rc;
  double tolerance = 0.032; /* this passes with 3.2% relative accuracy */

  muse_image *raw = muse_image_load_from_raw(BASEFILENAME"_raw.fits", 1);
  muse_image *img = muse_quadrants_trim_image(raw);
  muse_image_delete(raw);
  muse_image *bias = muse_image_load(BASEFILENAME"_MASTER_BIAS.fits");
  if (bias == NULL) {
    cpl_msg_error("muse_image_load", "(MASTER_BIAS): %s",
                  cpl_error_get_message());
    muse_image_delete(img);
    return cpl_test_end(1);
  }

  cpl_msg_info(__func__, "Create variance");
  rc = muse_image_variance_create(img, bias);
  if (rc != CPL_ERROR_NONE) {
    cpl_msg_error("muse_image_variance_create", "%s", cpl_error_get_message());
    muse_image_delete(img);
    muse_image_delete(bias);
    return cpl_test_end(1);
  }
  cpl_msg_info(__func__, "Subtract bias");
  rc = muse_image_subtract(img, bias);
  if (rc != CPL_ERROR_NONE) {
    cpl_msg_error("muse_image_subtract", "%s", cpl_error_get_message());
    muse_image_delete(img);
    muse_image_delete(bias);
    return cpl_test_end(1);
  }
  check_statistics(img, tolerance);
  muse_image_delete(bias);

  cpl_msg_info(__func__, "Convert from adu to count");
  rc = muse_image_adu_to_count(img);
  if (rc != CPL_ERROR_NONE) {
    cpl_msg_error("muse_image_adu_to_count", "%s", cpl_error_get_message());
    muse_image_delete(img);
    muse_image_delete(bias);
    return cpl_test_end(1);
  }
  check_statistics(img, tolerance);

  muse_image *dark = muse_image_load(BASEFILENAME"_MASTER_DARK.fits");
  if (dark != NULL) {
    double scale = muse_pfits_get_exptime(img->header);
    if (muse_pfits_get_exptime(dark->header) > 0) {
      scale /= muse_pfits_get_exptime(dark->header);
    }
    cpl_msg_info(__func__, "Subtract dark");
    rc = muse_image_scale(dark, scale);
    if (rc != CPL_ERROR_NONE) {
      cpl_msg_error("muse_image_scale", "%s", cpl_error_get_message());
      muse_image_delete(img);
      muse_image_delete(bias);
      muse_image_delete(dark);
      return cpl_test_end(1);
    }
    rc = muse_image_subtract(img, dark);
    if (rc != CPL_ERROR_NONE) {
      cpl_msg_error("muse_image_subtract", "%s", cpl_error_get_message());
      muse_image_delete(img);
      muse_image_delete(bias);
      muse_image_delete(dark);
      return cpl_test_end(1);
    }
    check_statistics(img, tolerance);
    muse_image_delete(dark);
  }

  muse_image *flat = muse_image_load(BASEFILENAME"_MASTER_FLAT.fits");
  if (flat != NULL) {
    cpl_msg_info(__func__, "Divide by the flat-field");
    rc = muse_image_divide(img, flat);
    if (rc != CPL_ERROR_NONE) {
      cpl_msg_error("muse_image_divide", "%s", cpl_error_get_message());
      muse_image_delete(img);
      muse_image_delete(bias);
      muse_image_delete(flat);
      return cpl_test_end(1);
    }
    check_statistics(img, tolerance);
    muse_image_delete(flat);
  }

  muse_image_delete(img);
  return cpl_test_end(0);
}
