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

#include <muse.h>
#include "muse_standard_z.h"

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

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

  @pseudocode
  muse_processing_sort_exposures()
  for i = 0 ... nexposures:
    response = muse_postproc_process_exposure()
    save response@endpseudocode
 */
/*----------------------------------------------------------------------------*/
int
muse_standard_compute(muse_processing *aProcessing,
                      muse_standard_params_t *aParams)
{
  cpl_errorstate state = cpl_errorstate_get();

  muse_postproc_properties *prop = muse_postproc_properties_new(MUSE_POSTPROC_STANDARD);
  /* per-exposure parameters */
  prop->lambdamin = aParams->lambdamin;
  prop->lambdamax = aParams->lambdamax;
  prop->lambdaref = aParams->lambdaref;
  prop->darcheck = MUSE_POSTPROC_DARCHECK_NONE;
  if (aParams->darcheck == MUSE_STANDARD_PARAM_DARCHECK_CHECK) {
    prop->darcheck = MUSE_POSTPROC_DARCHECK_CHECK;
  } else if (aParams->darcheck == MUSE_STANDARD_PARAM_DARCHECK_CORRECT) {
    prop->darcheck = MUSE_POSTPROC_DARCHECK_CORRECT;
  }
  /* setup and check flux integration profiles */
  prop->profile = MUSE_FLUX_PROFILE_GAUSSIAN;
  if (aParams->profile == MUSE_STANDARD_PARAM_PROFILE_MOFFAT) {
    prop->profile = MUSE_FLUX_PROFILE_MOFFAT;
  } else if (aParams->profile == MUSE_STANDARD_PARAM_PROFILE_CIRCLE) {
    prop->profile = MUSE_FLUX_PROFILE_CIRCLE;
  } else if (aParams->profile == MUSE_STANDARD_PARAM_PROFILE_SQUARE) {
    prop->profile = MUSE_FLUX_PROFILE_EQUAL_SQUARE;
  } else if (aParams->profile != MUSE_STANDARD_PARAM_PROFILE_GAUSSIAN) {
    cpl_msg_error(__func__, "unknown profile \"%s\"", aParams->profile_s);
    muse_postproc_properties_delete(prop);
    return -1;
  }
  /* flux reference */
  prop->refframe = muse_frameset_find_master(aProcessing->inframes,
                                             MUSE_TAG_STD_FLUX_TABLE, 0);
  if (!prop->refframe) {
    cpl_msg_error(__func__, "Required input %s not found in input files",
                  MUSE_TAG_STD_FLUX_TABLE);
    cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT,
                          MUSE_TAG_STD_FLUX_TABLE" missing");
    muse_postproc_properties_delete(prop);
    return -1;
  }
  muse_processing_append_used(aProcessing, prop->refframe,
                              CPL_FRAME_GROUP_CALIB, 1);
  prop->select = MUSE_FLUX_SELECT_BRIGHTEST;
  if (aParams->select == MUSE_STANDARD_PARAM_SELECT_DISTANCE) {
    prop->select = MUSE_FLUX_SELECT_NEAREST;
  } else if (aParams->select != MUSE_STANDARD_PARAM_SELECT_FLUX) {
    cpl_msg_error(__func__, "unknown selection \"%s\"", aParams->select_s);
    muse_postproc_properties_delete(prop);
    return -1;
  }
  prop->smooth = MUSE_SPECTRUM_SMOOTH_PPOLY;
  if (aParams->smooth == MUSE_STANDARD_PARAM_SMOOTH_MEDIAN) {
    prop->smooth = MUSE_SPECTRUM_SMOOTH_MEDIAN;
  } else if (aParams->smooth == MUSE_STANDARD_PARAM_SMOOTH_NONE) {
    prop->smooth = MUSE_SPECTRUM_SMOOTH_NONE;
  } else if (aParams->smooth != MUSE_STANDARD_PARAM_SMOOTH_PPOLY) {
    cpl_msg_error(__func__, "unknown smoothing \"%s\"", aParams->smooth_s);
    muse_postproc_properties_delete(prop);
    return -1;
  }
  prop->extinction = muse_processing_load_ctable(aProcessing,
                                                 MUSE_TAG_EXTINCT_TABLE, 0);
  /* telluric regions */
  prop->tellregions = muse_processing_load_ctable(aProcessing, MUSE_TAG_TELLREG, 0);
  if (!prop->tellregions) {
    cpl_msg_debug(__func__, "%s could not be found or loaded, using defaults",
                  MUSE_TAG_TELLREG);
  }

  /* sort input pixel tables into different exposures */
  prop->exposures = muse_processing_sort_exposures(aProcessing);
  if (!prop->exposures) {
    cpl_msg_error(__func__, "No standard star exposures found in input!");
    muse_postproc_properties_delete(prop);
    return -1;
  }
  int nexposures = cpl_table_get_nrow(prop->exposures);

  /* now process all the pixel tables, do it separately for each exposure */
  muse_flux_object **fluxobjs = cpl_calloc(nexposures, sizeof(muse_flux_object *));
  int i;
  for (i = 0; i < nexposures; i++) {
    fluxobjs[i] = muse_postproc_process_exposure(prop, i, NULL, NULL, NULL, NULL);
    if (!fluxobjs[i]) {
      int i2;
      for (i2 = 0; i2 <= i; i2++) {
        muse_flux_object_delete(fluxobjs[i2]);
      } /* for i2 */
      cpl_free(fluxobjs);
      muse_postproc_properties_delete(prop);
      return -1; /* enough error messages, just return */
    }
  } /* for i (exposures) */
  muse_postproc_properties_delete(prop);

  /* XXX now combine the possibly more than one response tables */

  cpl_array *filters = muse_cplarray_new_from_delimited_string(aParams->filter,
                                                               ",");
  for (i = 0; i < nexposures; i++) {
    int ifilt, ipos = 0, nfilt = cpl_array_get_size(filters);
    for (ifilt = 0; ifilt < nfilt; ifilt++) {
      /* try to find and load the filter from a table */
      const char *filtername = cpl_array_get_string(filters, ifilt);
      muse_table *filter = muse_table_load_filter(aProcessing, filtername);
      if (!filter) {
        continue;
      }
      /* add an image integrated over this filter to the output cube */
      muse_image *fov = muse_datacube_collapse(fluxobjs[i]->cube, filter);
      if (!fluxobjs[i]->cube->recimages) {
        fluxobjs[i]->cube->recimages = muse_imagelist_new();
        fluxobjs[i]->cube->recnames = cpl_array_new(0, CPL_TYPE_STRING);
      }
      muse_imagelist_set(fluxobjs[i]->cube->recimages, fov, ipos);
      cpl_array_set_size(fluxobjs[i]->cube->recnames, ipos+1);
      cpl_array_set_string(fluxobjs[i]->cube->recnames, ipos,
                           cpl_array_get_string(filters, ifilt));
      /* check that the filter we are processing is one for which *
       * we actually want a zeropoint measurement before calling  */
      if (!strncmp(filtername, "Johnson_V", 10) ||
          !strncmp(filtername, "Cousins_R", 10) ||
          !strncmp(filtername, "Cousins_I", 10)) {
        /* add zeropoint to the QC parameters, using this filter */
        muse_flux_compute_qc_zp(fluxobjs[i], filter, filtername);
      } /* if matching filter */
      muse_table_delete(filter);
      ipos++;
    } /* for ifilt (all filters) */
    muse_postproc_qc_fwhm(aProcessing, fluxobjs[i]->cube); /* before NANs! */
    muse_datacube_convert_dq(fluxobjs[i]->cube);
    muse_processing_save_cube(aProcessing, -1, fluxobjs[i]->cube,
                              MUSE_TAG_CUBE_STD, MUSE_CUBE_TYPE_FITS);
    /* save the integrated flux image as well */
    muse_processing_save_image(aProcessing, -1, fluxobjs[i]->intimage,
                               MUSE_TAG_STD_INTIMAGE);

    /* save the response-curve and the telluric correction */
    cpl_propertylist *header = cpl_propertylist_duplicate(fluxobjs[i]->cube->header);
    cpl_propertylist_erase_regexp(header, "^NAXIS|^EXPTIME$|"MUSE_WCS_KEYS, 0);
    char *objorig = cpl_strdup(cpl_propertylist_get_string(header, "OBJECT"));
    char *object = cpl_sprintf("Response curve (%s)", objorig);
    cpl_propertylist_update_string(header, "OBJECT", object);
    cpl_error_code rc = rc = muse_processing_save_table(aProcessing, -1,
                                                        fluxobjs[i]->response,
                                                        header,
                                                        MUSE_TAG_STD_RESPONSE,
                                                        MUSE_TABLE_TYPE_MUSE);
    cpl_free(object);
    if (rc != CPL_ERROR_NONE) {
      cpl_free(objorig);
      cpl_propertylist_delete(header);
      for ( ; i < nexposures; i++) {
        muse_flux_object_delete(fluxobjs[i]);
      } /* for rest of i */
      break;
    } /* if */
    object = cpl_sprintf("Telluric correction (%s)", objorig);
    cpl_propertylist_update_string(header, "OBJECT", object);
    rc = muse_processing_save_table(aProcessing, -1, fluxobjs[i]->telluric, header,
                                    MUSE_TAG_STD_TELLURIC, MUSE_TABLE_TYPE_MUSE);
    cpl_free(object);
    cpl_free(objorig);
    cpl_propertylist_delete(header);
    muse_flux_object_delete(fluxobjs[i]);
    if (rc != CPL_ERROR_NONE) {
      for ( ; i < nexposures; i++) {
        muse_flux_object_delete(fluxobjs[i]);
      } /* for rest of i */
      break;
    } /* if */
  } /* for i (exposures) */
  cpl_array_delete(filters);
  cpl_free(fluxobjs);

  return cpl_errorstate_is_equal(state) ? 0 : -1;
} /* muse_standard_compute() */
