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

/* This file was automatically generated */

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

/*----------------------------------------------------------------------------*
 *                              Includes                                      *
 *----------------------------------------------------------------------------*/
#include <string.h> /* strcmp(), strstr() */
#include <strings.h> /* strcasecmp() */
#include <cpl.h>

#include "muse_scipost_make_cube_z.h" /* in turn includes muse.h */

/*----------------------------------------------------------------------------*/
/**
  @defgroup recipe_muse_scipost_make_cube         Recipe muse_scipost_make_cube: Make a MUSE cube from a MUSE pixel table.
  @author Ole Streicher
  This recipe takes a pixel table and resamples it to either a FITS cube or a Euro3D table and optionally to a stacked spectrum. This is a part of the muse_scipost recipe.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*----------------------------------------------------------------------------*
 *                         Static variables                                   *
 *----------------------------------------------------------------------------*/
static const char *muse_scipost_make_cube_help =
  "This recipe takes a pixel table and resamples it to either a FITS cube or a Euro3D table and optionally to a stacked spectrum. This is a part of the muse_scipost recipe.";

static const char *muse_scipost_make_cube_help_esorex =
  "\n\nInput frames for raw frame tag \"PIXTABLE_OBJECT\":\n"
  "\n Frame tag            Type Req #Fr Description"
  "\n -------------------- ---- --- --- ------------"
  "\n PIXTABLE_OBJECT      raw   Y      Input pixel table"
  "\n FILTER_LIST          calib .    1 File to be used to create field-of-view images."
  "\n OUTPUT_WCS           calib .    1 WCS to override output cube location / dimensions"
  "\n\nProduct frames for raw frame tag \"PIXTABLE_OBJECT\":\n"
  "\n Frame tag            Level    Description"
  "\n -------------------- -------- ------------"
  "\n DATACUBE_FINAL       final    Output datacube"
  "\n IMAGE_FOV            final    Field-of-view images corresponding to the \"filter\" parameter."
  "\n OBJECT_RESAMPLED     final    Stacked image (if --stacked=true)";

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Create the recipe config for this plugin.

  @remark The recipe config is used to check for the tags as well as to create
          the documentation of this plugin.
 */
/*----------------------------------------------------------------------------*/
static cpl_recipeconfig *
muse_scipost_make_cube_new_recipeconfig(void)
{
  cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
      
  cpl_recipeconfig_set_tag(recipeconfig, "PIXTABLE_OBJECT", 1, -1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "FILTER_LIST", -1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_OBJECT", "OUTPUT_WCS", -1, 1);
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "DATACUBE_FINAL");
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "IMAGE_FOV");
  cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_OBJECT", "OBJECT_RESAMPLED");
    
  return recipeconfig;
} /* muse_scipost_make_cube_new_recipeconfig() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return a new header that shall be filled on output.
  @param   aFrametag   tag of the output frame
  @param   aHeader     the prepared FITS header
  @return  CPL_ERROR_NONE on success another cpl_error_code on error

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code
muse_scipost_make_cube_prepare_header(const char *aFrametag, cpl_propertylist *aHeader)
{
  cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
  cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
  } else if (!strcmp(aFrametag, "IMAGE_FOV")) {
  } else if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
  } else {
    cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
    return CPL_ERROR_ILLEGAL_INPUT;
  }
  return CPL_ERROR_NONE;
} /* muse_scipost_make_cube_prepare_header() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return the level of an output frame.
  @param   aFrametag   tag of the output frame
  @return  the cpl_frame_level or CPL_FRAME_LEVEL_NONE on error

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static cpl_frame_level
muse_scipost_make_cube_get_frame_level(const char *aFrametag)
{
  if (!aFrametag) {
    return CPL_FRAME_LEVEL_NONE;
  }
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "IMAGE_FOV")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  return CPL_FRAME_LEVEL_NONE;
} /* muse_scipost_make_cube_get_frame_level() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Return the mode of an output frame.
  @param   aFrametag   tag of the output frame
  @return  the muse_frame_mode

  @remark This function is also used to generate the recipe documentation.
 */
/*----------------------------------------------------------------------------*/
static muse_frame_mode
muse_scipost_make_cube_get_frame_mode(const char *aFrametag)
{
  if (!aFrametag) {
    return MUSE_FRAME_MODE_ALL;
  }
  if (!strcmp(aFrametag, "DATACUBE_FINAL")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "IMAGE_FOV")) {
    return MUSE_FRAME_MODE_SEQUENCE;
  }
  if (!strcmp(aFrametag, "OBJECT_RESAMPLED")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  return MUSE_FRAME_MODE_ALL;
} /* muse_scipost_make_cube_get_frame_mode() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Setup the recipe options.
  @param   aPlugin   the plugin
  @return  0 if everything is ok, -1 if not called as part of a recipe.

  Define the command-line, configuration, and environment parameters for the
  recipe.
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_make_cube_create(cpl_plugin *aPlugin)
{
  /* Check that the plugin is part of a valid recipe */
  cpl_recipe *recipe;
  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
    recipe = (cpl_recipe *)aPlugin;
  } else {
    return -1;
  }

  /* register the extended processing information (new FITS header creation, *
   * getting of the frame level for a certain tag)                           */
  muse_processinginfo_register(recipe,
                               muse_scipost_make_cube_new_recipeconfig(),
                               muse_scipost_make_cube_prepare_header,
                               muse_scipost_make_cube_get_frame_level,
                               muse_scipost_make_cube_get_frame_mode);

  /* XXX initialize timing in messages                                       *
   *     since at least esorex is too stupid to turn it on, we have to do it */
  if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
    cpl_msg_set_time_on();
  }

  /* Create the parameter list in the cpl_recipe object */
  recipe->parameters = cpl_parameterlist_new();
  /* Fill the parameters list */
  cpl_parameter *p;
      
  /* --lambdamin: Cut off the data below this wavelength after loading the pixel table(s). */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.lambdamin",
                              CPL_TYPE_DOUBLE,
                             "Cut off the data below this wavelength after loading the pixel table(s).",
                              "muse.muse_scipost_make_cube",
                              (double)4000.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamin");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamin");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --lambdamax: Cut off the data above this wavelength after loading the pixel table(s). */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.lambdamax",
                              CPL_TYPE_DOUBLE,
                             "Cut off the data above this wavelength after loading the pixel table(s).",
                              "muse.muse_scipost_make_cube",
                              (double)10000.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamax");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamax");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --resample: The resampling technique to use for the final output cube. */
  p = cpl_parameter_new_enum("muse.muse_scipost_make_cube.resample",
                             CPL_TYPE_STRING,
                             "The resampling technique to use for the final output cube.",
                             "muse.muse_scipost_make_cube",
                             (const char *)"drizzle",
                             6,
                             (const char *)"nearest",
                             (const char *)"linear",
                             (const char *)"quadratic",
                             (const char *)"renka",
                             (const char *)"drizzle",
                             (const char *)"lanczos");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "resample");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "resample");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dx: Horizontal step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.dx",
                              CPL_TYPE_DOUBLE,
                             "Horizontal step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units.",
                              "muse.muse_scipost_make_cube",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dx");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dx");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dy: Vertical step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.dy",
                              CPL_TYPE_DOUBLE,
                             "Vertical step size for resampling (in arcsec or pixel). The following defaults are taken when this value is set to 0.0: 0.2'' for WFM, 0.075'' for NFM, 1.0 if data is in pixel units.",
                              "muse.muse_scipost_make_cube",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dy");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dy");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --dlambda: Wavelength step size (in Angstrom). Natural instrument sampling is used, if this is 0.0 */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.dlambda",
                              CPL_TYPE_DOUBLE,
                             "Wavelength step size (in Angstrom). Natural instrument sampling is used, if this is 0.0",
                              "muse.muse_scipost_make_cube",
                              (double)0.0);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dlambda");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dlambda");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --crtype: Type of statistics used for detection of cosmic rays during final resampling. "iraf" uses the variance information, "mean" uses standard (mean/stdev) statistics, "median" uses median and the median median of the absolute median deviation. */
  p = cpl_parameter_new_enum("muse.muse_scipost_make_cube.crtype",
                             CPL_TYPE_STRING,
                             "Type of statistics used for detection of cosmic rays during final resampling. \"iraf\" uses the variance information, \"mean\" uses standard (mean/stdev) statistics, \"median\" uses median and the median median of the absolute median deviation.",
                             "muse.muse_scipost_make_cube",
                             (const char *)"median",
                             3,
                             (const char *)"iraf",
                             (const char *)"mean",
                             (const char *)"median");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "crtype");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "crtype");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --crsigma: Sigma rejection factor to use for cosmic ray rejection during final resampling. A zero or negative value switches cosmic ray rejection off. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.crsigma",
                              CPL_TYPE_DOUBLE,
                             "Sigma rejection factor to use for cosmic ray rejection during final resampling. A zero or negative value switches cosmic ray rejection off.",
                              "muse.muse_scipost_make_cube",
                              (double)15.);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "crsigma");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "crsigma");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --rc: Critical radius for the "renka" resampling method. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.rc",
                              CPL_TYPE_DOUBLE,
                             "Critical radius for the \"renka\" resampling method.",
                              "muse.muse_scipost_make_cube",
                              (double)1.25);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "rc");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rc");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --pixfrac: Pixel down-scaling factor for the "drizzle" resampling method. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.pixfrac",
                              CPL_TYPE_DOUBLE,
                             "Pixel down-scaling factor for the \"drizzle\" resampling method.",
                              "muse.muse_scipost_make_cube",
                              (double)0.8);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "pixfrac");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pixfrac");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --ld: Number of adjacent pixels to take into account during resampling in all three directions (loop distance); this affects all resampling methods except "nearest". */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.ld",
                              CPL_TYPE_INT,
                             "Number of adjacent pixels to take into account during resampling in all three directions (loop distance); this affects all resampling methods except \"nearest\".",
                              "muse.muse_scipost_make_cube",
                              (int)1);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ld");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ld");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --format: Type of output file format, "Cube" is a standard FITS cube with NAXIS=3 and multiple extensions (for data and variance). The extended "x" formats include the reconstructed image(s) in FITS image extensions within the same file. */
  p = cpl_parameter_new_enum("muse.muse_scipost_make_cube.format",
                             CPL_TYPE_STRING,
                             "Type of output file format, \"Cube\" is a standard FITS cube with NAXIS=3 and multiple extensions (for data and variance). The extended \"x\" formats include the reconstructed image(s) in FITS image extensions within the same file.",
                             "muse.muse_scipost_make_cube",
                             (const char *)"Cube",
                             4,
                             (const char *)"Cube",
                             (const char *)"Euro3D",
                             (const char *)"xCube",
                             (const char *)"xEuro3D");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "format");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "format");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --stacked: If true, write an additional output file in form of a 2D stacked image (x direction is pseudo-spatial, y direction is wavelength). */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.stacked",
                              CPL_TYPE_BOOL,
                             "If true, write an additional output file in form of a 2D stacked image (x direction is pseudo-spatial, y direction is wavelength).",
                              "muse.muse_scipost_make_cube",
                              (int)FALSE);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "stacked");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "stacked");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --filter: The filter name(s) to be used for the output field-of-view image. Each name has to correspond to an EXTNAME in an extension of the FILTER_LIST file. If an unsupported filter name is given, creation of the respective image is omitted. If multiple filter names are given, they have to be comma separated. */
  p = cpl_parameter_new_value("muse.muse_scipost_make_cube.filter",
                              CPL_TYPE_STRING,
                             "The filter name(s) to be used for the output field-of-view image. Each name has to correspond to an EXTNAME in an extension of the FILTER_LIST file. If an unsupported filter name is given, creation of the respective image is omitted. If multiple filter names are given, they have to be comma separated.",
                              "muse.muse_scipost_make_cube",
                              (const char *)"white");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "filter");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filter");

  cpl_parameterlist_append(recipe->parameters, p);
    
  return 0;
} /* muse_scipost_make_cube_create() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief   Fill the recipe parameters into the parameter structure
  @param   aParams       the recipe-internal structure to fill
  @param   aParameters   the cpl_parameterlist with the parameters
  @return  0 if everything is ok, -1 if something went wrong.

  This is a convienience function that centrally fills all parameters into the
  according fields of the recipe internal structure.
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_make_cube_params_fill(muse_scipost_make_cube_params_t *aParams, cpl_parameterlist *aParameters)
{
  cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
  cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
  cpl_parameter *p;
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.lambdamin");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->lambdamin = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.lambdamax");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->lambdamax = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.resample");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->resample_s = cpl_parameter_get_string(p);
  aParams->resample =
    (!strcasecmp(aParams->resample_s, "nearest")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_NEAREST :
    (!strcasecmp(aParams->resample_s, "linear")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_LINEAR :
    (!strcasecmp(aParams->resample_s, "quadratic")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_QUADRATIC :
    (!strcasecmp(aParams->resample_s, "renka")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_RENKA :
    (!strcasecmp(aParams->resample_s, "drizzle")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_DRIZZLE :
    (!strcasecmp(aParams->resample_s, "lanczos")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_LANCZOS :
      MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_INVALID_VALUE;
  cpl_ensure_code(aParams->resample != MUSE_SCIPOST_MAKE_CUBE_PARAM_RESAMPLE_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.dx");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dx = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.dy");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dy = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.dlambda");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->dlambda = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.crtype");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->crtype_s = cpl_parameter_get_string(p);
  aParams->crtype =
    (!strcasecmp(aParams->crtype_s, "iraf")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_CRTYPE_IRAF :
    (!strcasecmp(aParams->crtype_s, "mean")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_CRTYPE_MEAN :
    (!strcasecmp(aParams->crtype_s, "median")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_CRTYPE_MEDIAN :
      MUSE_SCIPOST_MAKE_CUBE_PARAM_CRTYPE_INVALID_VALUE;
  cpl_ensure_code(aParams->crtype != MUSE_SCIPOST_MAKE_CUBE_PARAM_CRTYPE_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.crsigma");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->crsigma = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.rc");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->rc = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.pixfrac");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->pixfrac = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.ld");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->ld = cpl_parameter_get_int(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.format");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->format_s = cpl_parameter_get_string(p);
  aParams->format =
    (!strcasecmp(aParams->format_s, "Cube")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_CUBE :
    (!strcasecmp(aParams->format_s, "Euro3D")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_EURO3D :
    (!strcasecmp(aParams->format_s, "xCube")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_XCUBE :
    (!strcasecmp(aParams->format_s, "xEuro3D")) ? MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_XEURO3D :
      MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_INVALID_VALUE;
  cpl_ensure_code(aParams->format != MUSE_SCIPOST_MAKE_CUBE_PARAM_FORMAT_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.stacked");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->stacked = cpl_parameter_get_bool(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_scipost_make_cube.filter");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->filter = cpl_parameter_get_string(p);
    
  return 0;
} /* muse_scipost_make_cube_params_fill() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Execute the plugin instance given by the interface
  @param    aPlugin   the plugin
  @return   0 if everything is ok, -1 if not called as part of a recipe
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_make_cube_exec(cpl_plugin *aPlugin)
{
  if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
    return -1;
  }
  muse_processing_recipeinfo(aPlugin);
  cpl_recipe *recipe = (cpl_recipe *)aPlugin;
  cpl_msg_set_threadid_on();

  cpl_frameset *usedframes = cpl_frameset_new(),
               *outframes = cpl_frameset_new();
  muse_scipost_make_cube_params_t params;
  muse_scipost_make_cube_params_fill(&params, recipe->parameters);

  cpl_errorstate prestate = cpl_errorstate_get();

  muse_processing *proc = muse_processing_new("muse_scipost_make_cube",
                                              recipe);
  int rc = muse_scipost_make_cube_compute(proc, &params);
  cpl_frameset_join(usedframes, proc->usedframes);
  cpl_frameset_join(outframes, proc->outframes);
  muse_processing_delete(proc);
  
  if (!cpl_errorstate_is_equal(prestate)) {
    /* dump all errors from this recipe in chronological order */
    cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
    /* reset message level to not get the same errors displayed again by esorex */
    cpl_msg_set_level(CPL_MSG_INFO);
  }
  /* clean up duplicates in framesets of used and output frames */
  muse_cplframeset_erase_duplicate(usedframes);
  muse_cplframeset_erase_duplicate(outframes);

  /* to get esorex to see our classification (frame groups etc.), *
   * replace the original frameset with the list of used frames   *
   * before appending product output frames                       */
  /* keep the same pointer, so just erase all frames, not delete the frameset */
  muse_cplframeset_erase_all(recipe->frames);
  cpl_frameset_join(recipe->frames, usedframes);
  cpl_frameset_join(recipe->frames, outframes);
  cpl_frameset_delete(usedframes);
  cpl_frameset_delete(outframes);
  return rc;
} /* muse_scipost_make_cube_exec() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Destroy what has been created by the 'create' function
  @param    aPlugin   the plugin
  @return   0 if everything is ok, -1 if not called as part of a recipe
 */
/*----------------------------------------------------------------------------*/
static int
muse_scipost_make_cube_destroy(cpl_plugin *aPlugin)
{
  /* Get the recipe from the plugin */
  cpl_recipe *recipe;
  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
    recipe = (cpl_recipe *)aPlugin;
  } else {
    return -1;
  }

  /* Clean up */
  cpl_parameterlist_delete(recipe->parameters);
  muse_processinginfo_delete(recipe);
  return 0;
} /* muse_scipost_make_cube_destroy() */

/*----------------------------------------------------------------------------*/
/**
  @private
  @brief    Add this recipe to the list of available plugins.
  @param    aList   the plugin list
  @return   0 if everything is ok, -1 otherwise (but this cannot happen)

  Create the recipe instance and make it available to the application using the
  interface. This function is exported.
 */
/*----------------------------------------------------------------------------*/
int
cpl_plugin_get_info(cpl_pluginlist *aList)
{
  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
  cpl_plugin *plugin = &recipe->interface;

  char *helptext;
  if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
    helptext = cpl_sprintf("%s%s", muse_scipost_make_cube_help,
                           muse_scipost_make_cube_help_esorex);
  } else {
    helptext = cpl_sprintf("%s", muse_scipost_make_cube_help);
  }

  /* Initialize the CPL plugin stuff for this module */
  cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
                  CPL_PLUGIN_TYPE_RECIPE,
                  "muse_scipost_make_cube",
                  "Make a MUSE cube from a MUSE pixel table.",
                  helptext,
                  "Ole Streicher",
                  "usd-help@eso.org",
                  muse_get_license(),
                  muse_scipost_make_cube_create,
                  muse_scipost_make_cube_exec,
                  muse_scipost_make_cube_destroy);
  cpl_pluginlist_append(aList, plugin);
  cpl_free(helptext);

  return 0;
} /* cpl_plugin_get_info() */

/**@}*/