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

#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_dark_z.h"

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

/*---------------------------------------------------------------------------*/
/**
  @brief    Add the QC parameters to the combined dark
  @param    aImage   the output image
  @param    aBad     the number of bad pixels found
  @param    aNorm    the normalization factor (original exposure time)
 */
/*---------------------------------------------------------------------------*/
#define DARK_HALFSIZE 9
#define DARK_NSAMPLES 300
static void
muse_dark_qc_header(muse_image *aImage, int aBad, double aNorm,
                    muse_imagelist *aList)
{
  cpl_msg_debug(__func__, "Adding QC keywords");

  /* 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_DARK_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) */

  /* properties of the resulting master frame */
  cpl_propertylist_append_int(aImage->header, QC_DARK_MASTER_NBADPIX, aBad);

  unsigned stats = CPL_STATS_MEDIAN | CPL_STATS_MEAN | CPL_STATS_STDEV
                 | CPL_STATS_MIN | CPL_STATS_MAX;
  muse_basicproc_stats_append_header(aImage->data, aImage->header,
                                     QC_DARK_MASTER_PREFIX, stats);

  /* use the bias-level computation function to get an accurate dark current */
  double dval = 0., derr = 0.;
  cpl_flux_get_bias_window(aImage->data, NULL, DARK_HALFSIZE, DARK_NSAMPLES,
                           &dval, &derr);
  /* values per hour for output */
  double exptime = muse_pfits_get_exptime(aImage->header);
  dval *= 3600. / exptime;
  derr *= 3600. / exptime;
  cpl_propertylist_append_float(aImage->header, QC_DARK_MASTER_DARKVALUE, dval);
  cpl_propertylist_append_float(aImage->header, QC_DARK_MASTER_DARKERROR, derr);

  /* warn about unlikely values */
  if (dval < 0. || dval > 10.) {
    cpl_msg_warning(__func__, "Could not determine reliable dark current "
                    "(found %.3f+/-%.3f count/pix/h)", dval, derr);
    if (aNorm < 1000.) {
      cpl_msg_warning(__func__, "May be due to low dark time (%.2f s)",
                      aNorm);
    }
    return;
  }

  /* output info if good values found */
  cpl_msg_info(__func__, "Dark current is %.3f+/-%.3f count/pix/h",
               dval, derr);
} /* muse_dark_qc_header() */

/*---------------------------------------------------------------------------*/
/**
  @brief    Interpret the command line options and execute the data processing
  @param    aProcessing   the processing structure
  @param    aParams       the parameters list
  @return   0 if everything is ok, -1 something went wrong
 */
/*---------------------------------------------------------------------------*/
int
muse_dark_compute(muse_processing *aProcessing, muse_dark_params_t *aParams)
{
  muse_basicproc_params *bpars = muse_basicproc_params_new(aProcessing->parameters,
                                                           "muse.muse_dark");
  muse_imagelist *images = muse_basicproc_load(aProcessing, aParams->nifu, bpars);
  muse_basicproc_params_delete(bpars);
  cpl_ensure(images, cpl_error_get_code(), -1);

  /* exposure time of the first input image to be used later for normalization */
  double exptime0 =
    muse_pfits_get_exptime(muse_imagelist_get(images, 0)->header);

  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
                                               "muse.muse_dark");
  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);
    return -1;
  }
  cpl_propertylist_erase_regexp(masterimage->header, MUSE_WCS_KEYS, 0);
  int nbad = muse_quality_dark_badpix(masterimage, 0, aParams->hotsigma);

  if (aParams->normalize > 0) {
    /* all frames were scaled to exposure time of the first frame   *
     * so use that to normalize the master dark to 1s exposure time */
    cpl_msg_info(__func__, "Normalize master dark image to %.3fs",
                 aParams->normalize);
    double f = exptime0 / aParams->normalize;
    muse_image_scale(masterimage, 1. / f);
    cpl_propertylist_update_double(masterimage->header, "EXPTIME",
                                   aParams->normalize);
    char *comment = cpl_sprintf("[s] Master dark normalized to %.3fs exposure "
                                "time", aParams->normalize);
    cpl_propertylist_set_comment(masterimage->header, "EXPTIME", comment);
    cpl_free(comment);
  }

  muse_dark_qc_header(masterimage, nbad, exptime0, images);
  muse_imagelist_delete(images);
  muse_basicproc_qc_saturated(masterimage, QC_DARK_MASTER_PREFIX);
  int rc = muse_processing_save_image(aProcessing, aParams->nifu, masterimage,
                                      MUSE_TAG_MASTER_DARK);

  if (aParams->model) {
    cpl_msg_info(__func__, "Modeling the master dark:");
    cpl_msg_indent_more();
    muse_basicproc_darkmodel(masterimage);
    cpl_propertylist_erase_regexp(masterimage->header, "ESO QC", 0);
    rc = muse_processing_save_image(aProcessing, aParams->nifu, masterimage,
                                    MUSE_TAG_DARK_MODEL);
    cpl_msg_indent_less();
  }
  muse_image_delete(masterimage);

  return rc == CPL_ERROR_NONE ? 0 : -1;
} /* muse_dark_compute() */
