/* -*- 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) 2005-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.
 */

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

/*----------------------------------------------------------------------------*
 *                              Includes                                      *
 *----------------------------------------------------------------------------*/
#include <stdio.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include <cpl.h>
#include <muse.h>
#include "muse_bias_z.h"

/*---------------------------------------------------------------------------*
 *                              Functions code                               *
 *---------------------------------------------------------------------------*/

/* Explicitely specify the parameters for the cpl_flux_get_noise_window() *
 * function so that we can control, how many pixels are involved which is *
 * necessary to know for the determination of the variance.               *
 * Make the boxes a bit larger than the default to use more pixels for    *
 * the determination and hence also get smaller error estimates.          */
#define RON_HALFSIZE 9
#define RON_NSAMPLES 100

/*---------------------------------------------------------------------------*/
/**
  @brief Add the QC parameters to the masterbias.
  @param aImage   the output image
  @param aRON     the bivector containing the read-out noise paramters from
                  all input frames
  @param aList    the list of all single images
  @return CPL_ERROR_NONE on success or CPL_ERROR_NULL_INPUT if any input pointer
          is NULL
 */
/*---------------------------------------------------------------------------*/
static cpl_error_code
muse_bias_qc_header(muse_image *aImage, cpl_bivector *aRON,
                    muse_imagelist *aList)
{
  cpl_ensure_code(aImage && aRON && aList, CPL_ERROR_NULL_INPUT);
  cpl_msg_debug(__func__, "Adding QC parameters");

  /* copy saturation numbers from the input images */
  unsigned int k;
  for (k = 0; k < muse_imagelist_get_size(aList); k++) {
    char *keyword = cpl_sprintf(QC_BIAS_PREFIXi" "QC_BASIC_NSATURATED, k+1);
    int nsaturated = cpl_propertylist_get_int(muse_imagelist_get(aList, k)->header,
                                              MUSE_HDR_TMP_NSAT);
    cpl_propertylist_update_int(aImage->header, keyword, nsaturated);
    cpl_free(keyword);
  } /* for k (all images in list) */

  /* propagate read-out noise and per-quadrant medians */
  cpl_vector *vmedians = cpl_vector_new(aList->size);
  const double *ron = cpl_bivector_get_x_data_const(aRON),
               *ronerr = cpl_bivector_get_y_data_const(aRON);
  unsigned char n;
  for (n = 1; n <= 4; n++) {
    /* get the image window for this quadrant */
    cpl_size *window = muse_quadrants_get_window(aImage, n);

    /* save read-out noise and its error in the respective FITS keywords */
    char keyword[KEYWORD_LENGTH];
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_MASTER_RON, n);
    cpl_propertylist_append_float(aImage->header, keyword, ron[n-1]);

    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_MASTER_RONERR, n);
    cpl_propertylist_append_float(aImage->header, keyword, ronerr[n-1]);

    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_MASTERn_PREFIX, n);
    unsigned stats = CPL_STATS_MEDIAN | CPL_STATS_MEAN | CPL_STATS_STDEV
                   | CPL_STATS_MIN | CPL_STATS_MAX;
    muse_basicproc_stats_append_header_window(aImage->data, aImage->header,
                                              keyword, stats, window[0],
                                              window[2], window[1], window[3]);

    /* add slopes for the quadrant */
    cpl_vector *slopes = muse_cplimage_slope_window(aImage->data, window);
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_MASTER_SLOPEX, n);
    cpl_propertylist_append_float(aImage->header, keyword,
                                  cpl_vector_get(slopes, 0));
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_MASTER_SLOPEY, n);
    cpl_propertylist_append_float(aImage->header, keyword,
                                  cpl_vector_get(slopes, 1));
    cpl_vector_delete(slopes);

    cpl_free(window);

    /* propagate per-quadrant medians from individual images to master image */
    for (k = 0; k < aList->size; k++) {
      snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_TMP_QUADnMED, n);
      double val = cpl_propertylist_get_float(muse_imagelist_get(aList, k)->header,
                                              keyword);
      cpl_vector_set(vmedians, k, val);
    } /* for k (all images in list) */
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_LEVELi_MEAN, n);
    cpl_propertylist_update_float(aImage->header, keyword,
                                  cpl_vector_get_mean(vmedians));
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_LEVELi_STDEV, n);
    cpl_propertylist_update_float(aImage->header, keyword,
                                  cpl_vector_get_stdev(vmedians));
    snprintf(keyword, KEYWORD_LENGTH, QC_BIAS_LEVELi_MEDIAN, n);
    cpl_propertylist_update_float(aImage->header, keyword,
                                  cpl_vector_get_median(vmedians));
  } /* for n (quadrants) */
  cpl_vector_delete(vmedians);
  return CPL_ERROR_NONE;
} /* muse_bias_qc_header() */

/*---------------------------------------------------------------------------*/
/**
  @brief Compute the master bias frame and save it to disk.
  @param    aProcessing  the processing structure
  @param    aParams      the parameters list
  @return 0 on success, another value on error

  This function reads all raw input frames, combines them with the
  specified method and writes the resulting image to disk.
 */
/*---------------------------------------------------------------------------*/
int
muse_bias_compute(muse_processing *aProcessing, muse_bias_params_t *aParams)
{
  muse_basicproc_params *bpars = muse_basicproc_params_new(aProcessing->parameters,
                                                           "muse.muse_bias");
  muse_imagelist *images = muse_basicproc_load(aProcessing, aParams->nifu, bpars);
  muse_basicproc_params_delete(bpars);
  cpl_ensure(images, cpl_error_get_code(), -1);

  cpl_bivector *bron = muse_imagelist_compute_ron(images,
                                                  RON_HALFSIZE,
                                                  RON_NSAMPLES);

  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
                                               "muse.muse_bias");
  muse_image *masterimage = muse_combine_images(cpars, images);
  muse_combinepar_delete(cpars);
  if (!masterimage) {
    cpl_msg_error(__func__, "Combining input frames failed!");
    muse_imagelist_delete(images);
    cpl_bivector_delete(bron);
    return -1;
  }
  cpl_propertylist_erase_regexp(masterimage->header, MUSE_WCS_KEYS, 0);

  muse_bias_qc_header(masterimage, bron, images);
  muse_imagelist_delete(images);
  cpl_bivector_delete(bron);

  int nbad = muse_quality_bad_columns(masterimage, aParams->losigmabadpix,
                                      aParams->hisigmabadpix);
  cpl_propertylist_append_int(masterimage->header, QC_BIAS_MASTER_NBADPIX,
                              nbad);

  muse_basicproc_qc_saturated(masterimage, QC_BIAS_MASTER_PREFIX);
  int rc = muse_processing_save_image(aProcessing, aParams->nifu,
                                      masterimage, MUSE_TAG_MASTER_BIAS);
  muse_image_delete(masterimage);
  return rc == CPL_ERROR_NONE ? 0 : -1;
} /* muse_bias_compute() */
