/* -*- 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 <muse.h>
#include "muse_scipost_subtract_sky_z.h"

/*----------------------------------------------------------------------------*/
/**
  @brief    Separate sky subtraction main routine
  @param    aProcessing  the processing structure
  @param    aParams      the parameters list
  @return   CPL_ERROR_NONE if everything is ok, any other CPL error if something
            went wrong
 */
/*----------------------------------------------------------------------------*/
int
muse_scipost_subtract_sky_compute(muse_processing *aProcessing,
                                  muse_scipost_subtract_sky_params_t *aParams)
{
  cpl_table *continuum = muse_sky_continuum_load(aProcessing);
  cpl_table *lines = muse_sky_lines_load(aProcessing);
  cpl_errorstate prestate = cpl_errorstate_get();
  muse_lsf_cube **lsfCubes = NULL;
#ifdef USE_LSF_PARAMS
  muse_lsf_params **lsfParams = NULL;
#endif
  if (lines != NULL) {
    lsfCubes = muse_lsf_cube_load_all(aProcessing);
    if (lsfCubes == NULL) {
#ifdef USE_LSF_PARAMS
      lsfParams = muse_processing_lsf_params_load(aProcessing, 0);
      if (lsfParams == NULL) {
#endif
        cpl_msg_error(__func__, "Could not load LSF");
        cpl_table_delete(continuum);
        cpl_table_delete(lines);
        return CPL_ERROR_NULL_INPUT;
#ifdef USE_LSF_PARAMS
      }
      cpl_errorstate_set(prestate);
#endif
    }
  } else {
    cpl_msg_warning(__func__, "Could not load sky lines");
  }
  cpl_frameset *inframes = muse_frameset_find_tags(aProcessing->inframes,
                                                   aProcessing->intags, 0,
                                                   CPL_FALSE);
  cpl_error_code rc = CPL_ERROR_NONE;
  cpl_size iframe, nframes = cpl_frameset_get_size(inframes);
  for (iframe = 0; iframe < nframes; iframe++) {
    cpl_frame *frame = cpl_frameset_get_position(inframes, iframe);
    const char *fn = cpl_frame_get_filename(frame);
    muse_pixtable *pixtable =
      muse_pixtable_load_restricted_wavelength(fn,
                                               aParams->lambdamin,
                                               aParams->lambdamax);
    if (pixtable == NULL) {
      cpl_msg_error(__func__, "NULL pixel table for %s", fn);
      rc = CPL_ERROR_NULL_INPUT;
      break;
    }
    muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);

    /* erase pre-existing QC parameters */
    cpl_propertylist_erase_regexp(pixtable->header, "ESO QC ", 0);
    if (muse_pixtable_is_fluxcal(pixtable) != CPL_TRUE) {
      cpl_msg_error(__func__, "Pixel table %s not flux calibrated",
                    cpl_frame_get_filename(frame));
      muse_pixtable_delete(pixtable);
      rc = CPL_ERROR_ILLEGAL_INPUT;
      break;
    }

    if (muse_pixtable_is_skysub(pixtable) == CPL_TRUE) {
      cpl_msg_error(__func__, "Pixel table %s already sky subtracted",
                    cpl_frame_get_filename(frame));
      muse_pixtable_delete(pixtable);
      rc = CPL_ERROR_ILLEGAL_INPUT;
      break;
    }

    /* to fix the flux offset (using propagated flat-field fluxes) */
    double fluxs = cpl_propertylist_get_double(pixtable->header,
                                               MUSE_HDR_FLAT_FLUX_SKY);
    double fluxl = cpl_propertylist_get_double(pixtable->header,
                                               MUSE_HDR_FLAT_FLUX_LAMP);
    cpl_errorstate_set(prestate); /* ignore errors here */

    if (aParams->orig != NULL) {
      cpl_table_duplicate_column(pixtable->table, aParams->orig,
                                 pixtable->table, MUSE_PIXTABLE_DATA);
    }

    double scale = 1.;

    if (aParams->flux_sky > 0. && fluxs > 0.) { /* prefer sky flat flux scaling */
      scale = fluxs / aParams->flux_sky;
      cpl_msg_debug(__func__, "Scaling by SKY %e/%e = %f",
                    fluxs, aParams->flux_sky, scale);
    } else if (aParams->flux_lamp > 0. && fluxl > 0.) {
      scale = fluxl / aParams->flux_lamp;
      cpl_msg_debug(__func__, "Scaling by LAMP %e/%e = %f",
                    fluxl, aParams->flux_lamp, scale);
    }

    cpl_table_divide_scalar(pixtable->table, MUSE_PIXTABLE_DATA, scale);

    if (continuum != NULL) {
      rc = muse_sky_subtract_continuum(pixtable, continuum);
    }
    if (rc != CPL_ERROR_NONE) {
      cpl_msg_error(__func__, "while muse_sky_subtract_continuum(%s)",
                    cpl_frame_get_filename(frame));
      muse_pixtable_delete(pixtable);
      break;
    }
    if (lines != NULL) {
      if (lsfCubes != NULL) {
        rc = muse_sky_subtract_lines(pixtable, lines, lsfCubes);
#ifdef USE_LSF_PARAMS
      } else if (lsfParams != NULL) {
        rc = muse_sky_subtract_lines_old(pixtable, lines, lsfParams);
#endif
      }
      if (rc != CPL_ERROR_NONE) {
        cpl_msg_error(__func__, "while muse_sky_subtract_lines(%s)",
                      cpl_frame_get_filename(frame));
        muse_pixtable_delete(pixtable);
        break;
      }
    }
    cpl_table_multiply_scalar(pixtable->table, MUSE_PIXTABLE_DATA, scale);
    if (pixtable->header) {
      /* add the status header */
      cpl_propertylist_update_bool(pixtable->header, MUSE_HDR_PT_SKYSUB,
                                   CPL_TRUE);
      cpl_propertylist_set_comment(pixtable->header, MUSE_HDR_PT_SKYSUB,
                                   MUSE_HDR_PT_SKYSUB_COMMENT);
    }
    muse_processing_save_table(aProcessing, -1, pixtable, NULL,
                               MUSE_TAG_PIXTABLE_REDUCED,
                               MUSE_TABLE_TYPE_PIXTABLE);
    muse_pixtable_delete(pixtable);
  } /* for iframe (all input frames) */
  cpl_frameset_delete(inframes);
  cpl_table_delete(continuum);
  cpl_table_delete(lines);
  muse_lsf_cube_delete_all(lsfCubes);

  return cpl_error_get_code();
} /* muse_scipost_subtract_sky_compute() */
