/* -*- 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_test_processing_z.h" /* in turn includes muse.h */

/*----------------------------------------------------------------------------*/
/**
  @defgroup recipe_muse_test_processing         Recipe muse_test_processing: Dummy test recipe
  @author Ole Streicher
  This recipe is used to test our recipe framework.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*----------------------------------------------------------------------------*
 *                         Static variables                                   *
 *----------------------------------------------------------------------------*/
static const char *muse_test_processing_help =
  "This recipe is used to test our recipe framework.";

static const char *muse_test_processing_help_esorex =
  "\n\nInput frames for raw frame tag \"RAW\":\n"
  "\n Frame tag            Type Req #Fr Description"
  "\n -------------------- ---- --- --- ------------"
  "\n RAW                  raw   Y      "
  "\n NON_EXITING_OPTIONAL calib .      "
  "\n TABLE                calib Y    1 "
  "\n TABLE_IFU            calib .      "
  "\n HEADER               calib .    1 "
  "\n\nProduct frames for raw frame tag \"RAW\":\n"
  "\n Frame tag            Level    Description"
  "\n -------------------- -------- ------------"
  "\n processing_muse_imagefinal    "
  "\n processing_cpl_image temporar "
  "\n processing_cpl_table final    "
  "\n processing_muse_tablefinal    "
  "\n processing_header    final    "
  "\n processing_pixel_tablefinal    "
  "\n processing_data_cube final    "
  "\n processing_mask      final    ";

/*----------------------------------------------------------------------------*/
/**
  @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_test_processing_new_recipeconfig(void)
{
  cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
      
  cpl_recipeconfig_set_tag(recipeconfig, "RAW", 1, -1);
  cpl_recipeconfig_set_input(recipeconfig, "RAW", "NON_EXITING_OPTIONAL", -1, -1);
  cpl_recipeconfig_set_input(recipeconfig, "RAW", "TABLE", 1, 1);
  cpl_recipeconfig_set_input(recipeconfig, "RAW", "TABLE_IFU", -1, -1);
  cpl_recipeconfig_set_input(recipeconfig, "RAW", "HEADER", -1, 1);
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_muse_image");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_cpl_image");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_cpl_table");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_header");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_pixel_table");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_muse_table");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_data_cube");
  cpl_recipeconfig_set_output(recipeconfig, "RAW", "processing_mask");
    
  return recipeconfig;
} /* muse_test_processing_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_test_processing_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, "processing_muse_image")) {
    cpl_propertylist_update_string(aHeader, "ASTRING", "The default string");
    muse_processing_prepare_property(aHeader, "ASTRING",
                                     CPL_TYPE_STRING,
                                     "A constant string header with default");
    muse_processing_prepare_property(aHeader, "ADOUBLE",
                                     CPL_TYPE_DOUBLE,
                                     "A double value");
  } else if (!strcmp(aFrametag, "processing_cpl_image")) {
    cpl_propertylist_update_string(aHeader, "ASTRING", "The default string");
    muse_processing_prepare_property(aHeader, "ASTRING",
                                     CPL_TYPE_STRING,
                                     "A constant string header with default");
    muse_processing_prepare_property(aHeader, "ADOUBLE",
                                     CPL_TYPE_DOUBLE,
                                     "A double value");
  } else if (!strcmp(aFrametag, "processing_cpl_table")) {
    cpl_propertylist_update_string(aHeader, "ASTRING", "The default string");
    muse_processing_prepare_property(aHeader, "ASTRING",
                                     CPL_TYPE_STRING,
                                     "A constant string header with default");
    muse_processing_prepare_property(aHeader, "ADOUBLE",
                                     CPL_TYPE_DOUBLE,
                                     "A double value");
  } else if (!strcmp(aFrametag, "processing_muse_table")) {
    cpl_propertylist_update_string(aHeader, "ATABLE", "The default string");
    muse_processing_prepare_property(aHeader, "ATABLE",
                                     CPL_TYPE_STRING,
                                     "A constant string header with default");
    muse_processing_prepare_property(aHeader, "AFLOAT",
                                     CPL_TYPE_FLOAT,
                                     "A float value");
  } else if (!strcmp(aFrametag, "processing_header")) {
    cpl_propertylist_update_string(aHeader, "ASTRING", "The default string");
    muse_processing_prepare_property(aHeader, "ASTRING",
                                     CPL_TYPE_STRING,
                                     "A constant string header with default");
    muse_processing_prepare_property(aHeader, "ADOUBLE",
                                     CPL_TYPE_DOUBLE,
                                     "A double value");
  } else if (!strcmp(aFrametag, "processing_pixel_table")) {
    cpl_propertylist_update_string(aHeader, "PIXEL TABLE TYPE", "SOME TYPE");
    muse_processing_prepare_property(aHeader, "PIXEL TABLE TYPE",
                                     CPL_TYPE_STRING,
                                     "Some type by default");
  } else if (!strcmp(aFrametag, "processing_data_cube")) {
    cpl_propertylist_update_string(aHeader, "OBJECT", "THE DEFAULT OBJECT");
    muse_processing_prepare_property(aHeader, "OBJECT",
                                     CPL_TYPE_STRING,
                                     "An object header with some default");
  } else if (!strcmp(aFrametag, "processing_mask")) {
    cpl_propertylist_update_string(aHeader, "OBJECT", "THE DEFAULT MASK");
    muse_processing_prepare_property(aHeader, "OBJECT",
                                     CPL_TYPE_STRING,
                                     "An object header with some default");
  } else {
    cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
    return CPL_ERROR_ILLEGAL_INPUT;
  }
  return CPL_ERROR_NONE;
} /* muse_test_processing_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_test_processing_get_frame_level(const char *aFrametag)
{
  if (!aFrametag) {
    return CPL_FRAME_LEVEL_NONE;
  }
  if (!strcmp(aFrametag, "processing_muse_image")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_cpl_image")) {
    return CPL_FRAME_LEVEL_TEMPORARY;
  }
  if (!strcmp(aFrametag, "processing_cpl_table")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_muse_table")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_header")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_pixel_table")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_data_cube")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  if (!strcmp(aFrametag, "processing_mask")) {
    return CPL_FRAME_LEVEL_FINAL;
  }
  return CPL_FRAME_LEVEL_NONE;
} /* muse_test_processing_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_test_processing_get_frame_mode(const char *aFrametag)
{
  if (!aFrametag) {
    return MUSE_FRAME_MODE_ALL;
  }
  if (!strcmp(aFrametag, "processing_muse_image")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_cpl_image")) {
    return MUSE_FRAME_MODE_ALL;
  }
  if (!strcmp(aFrametag, "processing_cpl_table")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_muse_table")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_header")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_pixel_table")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_data_cube")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  if (!strcmp(aFrametag, "processing_mask")) {
    return MUSE_FRAME_MODE_MASTER;
  }
  return MUSE_FRAME_MODE_ALL;
} /* muse_test_processing_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_test_processing_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_test_processing_new_recipeconfig(),
                               muse_test_processing_prepare_header,
                               muse_test_processing_get_frame_level,
                               muse_test_processing_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;
      
  /* --nifu: IFU number to handle, and an integer */
  p = cpl_parameter_new_value("muse.muse_test_processing.nifu",
                              CPL_TYPE_INT,
                             "IFU number to handle, and an integer",
                              "muse.muse_test_processing",
                              (int)1);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "nifu");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "nifu");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --adouble: A double value */
  p = cpl_parameter_new_value("muse.muse_test_processing.adouble",
                              CPL_TYPE_DOUBLE,
                             "A double value",
                              "muse.muse_test_processing",
                              (double)3);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "adouble");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "adouble");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --aboolean: A boolean value */
  p = cpl_parameter_new_value("muse.muse_test_processing.aboolean",
                              CPL_TYPE_BOOL,
                             "A boolean value",
                              "muse.muse_test_processing",
                              (int)FALSE);
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "aboolean");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "aboolean");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --astring: A string value */
  p = cpl_parameter_new_value("muse.muse_test_processing.astring",
                              CPL_TYPE_STRING,
                             "A string value",
                              "muse.muse_test_processing",
                              (const char *)"DEF");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "astring");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "astring");

  cpl_parameterlist_append(recipe->parameters, p);
      
  /* --anenum: An enumeration value */
  p = cpl_parameter_new_enum("muse.muse_test_processing.anenum",
                             CPL_TYPE_STRING,
                             "An enumeration value",
                             "muse.muse_test_processing",
                             (const char *)"value1",
                             3,
                             (const char *)"value1",
                             (const char *)"value2",
                             (const char *)"value3");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "anenum");
  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "anenum");

  cpl_parameterlist_append(recipe->parameters, p);
    
  return 0;
} /* muse_test_processing_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_test_processing_params_fill(muse_test_processing_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_test_processing.nifu");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->nifu = cpl_parameter_get_int(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_test_processing.adouble");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->adouble = cpl_parameter_get_double(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_test_processing.aboolean");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->aboolean = cpl_parameter_get_bool(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_test_processing.astring");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->astring = cpl_parameter_get_string(p);
      
  p = cpl_parameterlist_find(aParameters, "muse.muse_test_processing.anenum");
  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
  aParams->anenum_s = cpl_parameter_get_string(p);
  aParams->anenum =
    (!strcasecmp(aParams->anenum_s, "value1")) ? MUSE_TEST_PROCESSING_PARAM_ANENUM_VALUE1 :
    (!strcasecmp(aParams->anenum_s, "value2")) ? MUSE_TEST_PROCESSING_PARAM_ANENUM_VALUE2 :
    (!strcasecmp(aParams->anenum_s, "value3")) ? MUSE_TEST_PROCESSING_PARAM_ANENUM_VALUE3 :
      MUSE_TEST_PROCESSING_PARAM_ANENUM_INVALID_VALUE;
  cpl_ensure_code(aParams->anenum != MUSE_TEST_PROCESSING_PARAM_ANENUM_INVALID_VALUE,
                  CPL_ERROR_ILLEGAL_INPUT);
    
  return 0;
} /* muse_test_processing_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_test_processing_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_test_processing_params_t params;
  muse_test_processing_params_fill(&params, recipe->parameters);

  cpl_errorstate prestate = cpl_errorstate_get();

  if (params.nifu < -1 || params.nifu > kMuseNumIFUs) {
    cpl_msg_error(__func__, "Please specify a valid IFU number (between 1 and "
                  "%d), 0 (to process all IFUs consecutively), or -1 (to "
                  "process all IFUs in parallel) using --nifu.", kMuseNumIFUs);
    return -1;
  } /* if invalid params.nifu */

  cpl_boolean donotmerge = CPL_FALSE; /* depending on nifu we may not merge */
  int rc = 0;
  if (params.nifu > 0) {
    muse_processing *proc = muse_processing_new("muse_test_processing",
                                                recipe);
    rc = muse_test_processing_compute(proc, &params);
    cpl_frameset_join(usedframes, proc->usedframes);
    cpl_frameset_join(outframes, proc->outframes);
    muse_processing_delete(proc);
    donotmerge = CPL_TRUE; /* after processing one IFU, merging cannot work */
  } else if (params.nifu < 0) { /* parallel processing */
    int *rcs = cpl_calloc(kMuseNumIFUs, sizeof(int));
    int nifu;
    // FIXME: Removed default(none) clause here, to make the code work with
    //        gcc9 while keeping older versions of gcc working too without
    //        resorting to conditional compilation. Having default(none) here
    //        causes gcc9 to abort the build because of __func__ not being
    //        specified in a data sharing clause. Adding a data sharing clause
    //        makes older gcc versions choke.
    #pragma omp parallel for                                                   \
            shared(outframes, params, rcs, recipe, usedframes)
    for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
      muse_processing *proc = muse_processing_new("muse_test_processing",
                                                  recipe);
      muse_test_processing_params_t *pars = cpl_malloc(sizeof(muse_test_processing_params_t));
      memcpy(pars, &params, sizeof(muse_test_processing_params_t));
      pars->nifu = nifu;
      int *rci = rcs + (nifu - 1);
      *rci = muse_test_processing_compute(proc, pars);
      if (rci && (int)cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
        *rci = 0;
      }
      cpl_free(pars);
      #pragma omp critical(muse_processing_used_frames)
      cpl_frameset_join(usedframes, proc->usedframes);
      #pragma omp critical(muse_processing_output_frames)
      cpl_frameset_join(outframes, proc->outframes);
      muse_processing_delete(proc);
    } /* for nifu */
    /* non-parallel loop to propagate the "worst" return code;       *
     * since we only ever return -1, any non-zero code is propagated */
    for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
      if (rcs[nifu-1] != 0) {
        rc = rcs[nifu-1];
      } /* if */
    } /* for nifu */
    cpl_free(rcs);
  } else { /* serial processing */
    for (params.nifu = 1; params.nifu <= kMuseNumIFUs && !rc; params.nifu++) {
      muse_processing *proc = muse_processing_new("muse_test_processing",
                                                  recipe);
      rc = muse_test_processing_compute(proc, &params);
      if (rc && (int)cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
        rc = 0;
      }
      cpl_frameset_join(usedframes, proc->usedframes);
      cpl_frameset_join(outframes, proc->outframes);
      muse_processing_delete(proc);
    } /* for nifu */
  } /* else */
  UNUSED_ARGUMENT(donotmerge); /* maybe this is not going to be used below */

  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_test_processing_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_test_processing_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_test_processing_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_test_processing_help,
                           muse_test_processing_help_esorex);
  } else {
    helptext = cpl_sprintf("%s", muse_test_processing_help);
  }

  /* Initialize the CPL plugin stuff for this module */
  cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
                  CPL_PLUGIN_TYPE_RECIPE,
                  "muse_test_processing",
                  "Dummy test recipe",
                  helptext,
                  "Ole Streicher",
                  "usd-help@eso.org",
                  muse_get_license(),
                  muse_test_processing_create,
                  muse_test_processing_exec,
                  muse_test_processing_destroy);
  cpl_pluginlist_append(aList, plugin);
  cpl_free(helptext);

  return 0;
} /* cpl_plugin_get_info() */

/**@}*/