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

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

/*---------------------------------------------------------------------------*
 *                             Includes                                      *
 *---------------------------------------------------------------------------*/
#include <muse.h>
#include "muse_scipost_raman_z.h"
#include <string.h>


/*----------------------------------------------------------------------------*/
/**
  @brief    Load the input pixtables and combine them into one
  @param    aProcessing the processing framework
  @param    aParams     process parameters
  @return   the pixtable, or NULL on error.

 */
/* copied & slightly modified from muse_create_sky.c */
/*----------------------------------------------------------------------------*/
static muse_pixtable *
muse_raman_load_pixtable(muse_processing *aProcessing,
                         muse_scipost_raman_params_t *aParams)
{
   muse_pixtable *pixtable = NULL;

  /* sort input pixel tables into different exposures */
  cpl_table *exposures = muse_processing_sort_exposures(aProcessing);
  if (!exposures) {
    cpl_msg_error(__func__, "no science exposures found in input");
    return NULL;
  }
  int nexposures = cpl_table_get_nrow(exposures);
  if (nexposures != 1) {
    cpl_msg_error(__func__, "More than one exposure (%i) in raman",
                  nexposures);
  }

  /* now process all the pixel tables, do it separately for each exposure */
  int i;
  for (i = 0; i < nexposures; i++) {
    cpl_table *thisexp = cpl_table_extract(exposures, i, 1);
    muse_pixtable *p = muse_pixtable_load_merge_channels(thisexp,
                                                         aParams->lambdamin,
                                                         aParams->lambdamax);
    cpl_table_delete(thisexp);
    if (p == NULL) {
      muse_pixtable_delete(pixtable);
      pixtable = NULL;
      break;
    }
    /* erase pre-existing QC parameters */
    cpl_propertylist_erase_regexp(p->header, "ESO QC ", 0);
    if (pixtable == NULL) {
      pixtable = p;
    } else {
      cpl_table_insert(pixtable->table, p->table,
                       cpl_table_get_nrow(pixtable->table));
      muse_pixtable_delete(p);
    }
  }
  cpl_table_delete(exposures);

  muse_table *response = muse_processing_load_table(aProcessing,
                                                    MUSE_TAG_STD_RESPONSE, 0),
             *telluric = muse_processing_load_table(aProcessing,
                                                    MUSE_TAG_STD_TELLURIC, 0);
  cpl_table *extinction = muse_processing_load_ctable(aProcessing,
                                                      MUSE_TAG_EXTINCT_TABLE, 0);

  if ((pixtable != NULL) && (response != NULL)) {
    if (muse_pixtable_is_fluxcal(pixtable) == CPL_TRUE) {
      cpl_msg_error(__func__,
                    "Pixel table already flux calibrated. Dont specify %s, %s, %s",
                    MUSE_TAG_STD_RESPONSE, MUSE_TAG_EXTINCT_TABLE,
                    MUSE_TAG_STD_TELLURIC);
      muse_pixtable_delete(pixtable);
      pixtable = NULL;
    } else {
      /* See if we need to revert the flat-field spectrum correction, *
       * to match pixel table and response curve, before applying it. */
      muse_postproc_revert_ffspec_maybe(pixtable, response);
      cpl_error_code rc = muse_flux_calibrate(pixtable, response, extinction,
                                              telluric);
      if (rc != CPL_ERROR_NONE) {
        cpl_msg_error(__func__, "while muse_flux_calibrate");
        muse_pixtable_delete(pixtable);
        pixtable = NULL;
      }
    }
  }

  muse_table_delete(response);
  muse_table_delete(telluric);
  cpl_table_delete(extinction);

  if (pixtable != NULL) {
    cpl_table_and_selected_int(pixtable->table, MUSE_PIXTABLE_DQ,
                               CPL_NOT_EQUAL_TO, EURO3D_GOODPIXEL);
    cpl_table_erase_selected(pixtable->table);

    /* do DAR correction for WFM data */
    if (muse_pfits_get_mode(pixtable->header) < MUSE_MODE_NFM_AO_N) {
      cpl_msg_debug(__func__, "WFM detected: starting DAR correction");
      cpl_error_code rc = muse_dar_correct(pixtable, aParams->lambdaref);
      cpl_msg_debug(__func__, "DAR correction returned rc=%d: %s", rc,
                    rc != CPL_ERROR_NONE ? cpl_error_get_message() : "");
    } /* if WFM */
  }

  return pixtable;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Separate raman 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_raman_compute(muse_processing *aProcessing,
                           muse_scipost_raman_params_t *aParams)
{
  muse_pixtable *pixtable = muse_raman_load_pixtable(aProcessing, aParams);
  if (pixtable == NULL) {
    cpl_msg_error(__func__, "Could not load pixel table");
    return -1;
  }

  /* 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);
  prop->raman_lines = muse_raman_lines_load(aProcessing);
  prop->lsf_cube = muse_lsf_cube_load_all(aProcessing);
  prop->sky_mask = muse_processing_load_mask(aProcessing, MUSE_TAG_SKY_MASK);
  /* parameters relevant for Raman processing */
  prop->skymodel_params.fraction = aParams->fraction;
  prop->skymodel_params.ignore = aParams->ignore;
  prop->skymodel_params.crsigmac = aParams->crsigma;
  prop->raman_width = aParams->width;

  muse_datacube *ramancube = NULL;
  muse_postproc_correct_raman(pixtable, prop, NULL/*XXX*/, &ramancube);
  muse_postproc_properties_delete(prop);

  cpl_msg_info(__func__, "Write output pixel table");
  muse_processing_save_table(aProcessing, -1, pixtable, NULL,
                             MUSE_TAG_PIXTABLE_REDUCED,
                             MUSE_TABLE_TYPE_PIXTABLE);
  muse_pixtable_delete(pixtable);

  cpl_msg_info(__func__, "Write other output file");
  muse_processing_save_cube(aProcessing, -1, ramancube,
                            MUSE_TAG_RAMAN_IMAGES, MUSE_CUBE_TYPE_FITS);
  muse_datacube_delete(ramancube);

  return cpl_error_get_code();
}
