/* $Id: uves_plugin.h,v 1.1 2008-03-28 08:56:45 amodigli Exp $
 *
 * This file is part of the UVES package 
 * Copyright (C) 2002,2003 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 St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: amodigli $
 * $Date: 2008-03-28 08:56:45 $
 * $Revision: 1.1 $
 * $Name: not supported by cvs2svn $
 */

#ifndef UVES_PLUGIN_H
#define UVES_PLUGIN_H

/*-----------------------------------------------------------------------------
                                   Includes
 -----------------------------------------------------------------------------*/

#include <irplib_utils.h>  /* irplib_reset() */

#include <cpl.h>

/*-----------------------------------------------------------------------------
                                   Define
 -----------------------------------------------------------------------------*/

/* Needed to concatenate two macro arguments */
#define UVES_CONCAT(a,b) a ## _ ## b
#define UVES_CONCAT2X(a,b) UVES_CONCAT(a,b)

/* A macro to generate the pipeline copyright and license */
#ifdef cpl_get_license
#define uves_irplib_get_license cpl_get_license
#else
/* FIXME: Drop once CPL 4.0 is no longer supported */
#define uves_irplib_get_license(PACKAGE_NAME, YEAR)                                 \
    "This file is part of the " PACKAGE_NAME "\n"                              \
    "Copyright (C) " YEAR " European Southern Observatory\n"                   \
    "\n"                                                                       \
    "This program is free software; you can redistribute it and/or modify\n"   \
    "it under the terms of the GNU General Public License as published by\n"   \
    "the Free Software Foundation; either version 2 of the License, or\n"      \
    "(at your option) any later version.\n"                                    \
    "\n"                                                                       \
    "This program is distributed in the hope that it will be useful,\n"        \
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"         \
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"          \
    "GNU General Public License for more details.\n"                           \
    "\n"                                                                       \
    "You should have received a copy of the GNU General Public License\n"      \
    "along with this program; if not, write to the Free Software\n"            \
    "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, \n"                \
    "MA  02111-1307  USA"
#endif

#ifdef CPL_RECIPE_DEFINE
#define UVES_IRPLIB_RECIPE_DEFINE CPL_RECIPE_DEFINE
#else
/* FIXME: Drop once CPL 4.0 is no longer supported */
/*----------------------------------------------------------------------------*/
/**
   @brief   Define a standard CPL recipe
   @param   RECIPE_NAME        The name as an identifier
   @param   RECIPE_VERSION     The binary version number
   @param   RECIPE_FILL_PARAMS A function call to fill the recipe parameterlist.
                               Must evaluate to zero if and only if successful
   @param   RECIPE_AUTHOR      The author as a string literal
   @param   RECIPE_YEAR        The copyright year as a string literal
   @param   RECIPE_SYNOPSIS    The synopsis as a string literal
   @param   RECIPE_DESCRIPTION The man-page as a string literal

   A CPL-based pipeline recipe may use this macro to define its four mandatory
   functions: cpl_plugin_get_info(), <recipe>_create(), <recipe>_exec() and
   <recipe>_destroy(), as well as declaring the actual data reduction
   function, <recipe>() as
   @code

     static int <recipe>(cpl_frameset *, const cpl_parameterlist *);

   @endcode

   This may be done by defining a pipeline specific macro, e.g. in
   mypipe_recipe.h:

   @code

   #define MYPIPE_RECIPE_DEFINE(NAME, FILL_PARAMS_FUNC, SYNOPSIS, DESCRIPTION) \
     UVES_IRPLIB_RECIPE_DEFINE(NAME, MYPIPE_BINARY_VERSION,                         \
     FILL_PARAMS_FUNC(recipe->parameters),                                     \
     "Firstname Lastname", "2002, 2003, 2005", SYNOPSIS, DESCRIPTION)          \

   @endcode

   - and then by invoking this macro in each recipe:

   @code

   #include "mypipe_recipe.h"

   MYPIPE_RECIPE_DEFINE(naco_img_dark,
                        naco_img_dark_fill_parameterlist,
                       "Dark recipe",
                       "naco_img_dark -- NACO imaging dark recipe.\n"
                       " ... recipe man-page\n")                       
   @endcode

   where naco_img_dark_fill_parameterlist() is a recipe specific function, that
   takes an empty parameterlist and fills it with those parameters that the
   recipe supports.

*/
/*----------------------------------------------------------------------------*/

#define UVES_IRPLIB_RECIPE_DEFINE(RECIPE_NAME, RECIPE_VERSION, RECIPE_FILL_PARAMS,  \
                             RECIPE_AUTHOR, RECIPE_AUTHOR_EMAIL, RECIPE_YEAR,  \
                             RECIPE_SYNOPSIS, RECIPE_DESCRIPTION)              \
                                                                               \
   /* The prototypes of the recipe create, exec and destroy functions */       \
static int UVES_CONCAT2X(RECIPE_NAME,create) (cpl_plugin * plugin);          \
static int UVES_CONCAT2X(RECIPE_NAME,exec)   (cpl_plugin * plugin);          \
static int UVES_CONCAT2X(RECIPE_NAME,destroy)(cpl_plugin * plugin);          \
                                                                               \
   /* The prototype of the function called by the recipe exec function */      \
static int RECIPE_NAME(cpl_frameset *, const cpl_parameterlist *);             \
                                                                               \
int cpl_plugin_get_info(cpl_pluginlist * list)                                 \
{                                                                              \
    cpl_recipe * recipe;                                                       \
    cpl_plugin * plugin;                                                       \
    /* Verify that the compile-time and run-time versions of CPL match */      \
    /* - this will work for run-time versions going back to CPL 3.0    */      \
    /* - earlier versions will cause an exit with an unresolved symbol */      \
    const unsigned vruntime = CPL_VERSION(cpl_version_get_major(),             \
                                          cpl_version_get_minor(),             \
                                          cpl_version_get_micro());            \
    /* The version number of the first major version */                        \
    const unsigned vmruntime = CPL_VERSION(cpl_version_get_major(), 0, 0);     \
                                                                               \
                                                                               \
    if (vruntime < CPL_VERSION_CODE) {                                         \
        cpl_msg_warning(cpl_func, "Run-time version %s of CPL is lower than "  \
                        "the compile-time version", cpl_version_get_version());\
    } else if (vmruntime > CPL_VERSION_CODE) {                                 \
        cpl_msg_warning(cpl_func, "Run-time version %s of CPL has a newer "    \
                        "major version than the compile-time version",         \
                        cpl_version_get_version());                            \
    } else if (vruntime > CPL_VERSION_CODE) {                                  \
        cpl_msg_info(cpl_func, "Run-time version %s of CPL is higher than "    \
                     "the compile-time version", cpl_version_get_version());   \
    }                                                                          \
                                                                               \
    recipe = cpl_calloc(1, sizeof(*recipe));                                   \
    if (recipe == NULL) {                                                      \
        cpl_msg_error(cpl_func, "Recipe allocation failed");                   \
        (void)cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT);               \
        return 1;                                                              \
    }                                                                          \
                                                                               \
    plugin = &recipe->interface;                                               \
                                                                               \
    if (cpl_plugin_init(plugin,                                                \
                    CPL_PLUGIN_API,                                            \
                    RECIPE_VERSION,                                            \
                    CPL_PLUGIN_TYPE_RECIPE,                                    \
                    #RECIPE_NAME,                                              \
                    RECIPE_SYNOPSIS,                                           \
                    RECIPE_DESCRIPTION,                                        \
                    RECIPE_AUTHOR,                                             \
                    RECIPE_AUTHOR_EMAIL,                                       \
                    uves_irplib_get_license(PACKAGE_NAME, RECIPE_YEAR),             \
                    UVES_CONCAT2X(RECIPE_NAME,create),                       \
                    UVES_CONCAT2X(RECIPE_NAME,exec),                         \
                    UVES_CONCAT2X(RECIPE_NAME,destroy))) {                   \
        cpl_msg_error(cpl_func, "Plugin initialization failed");               \
        (void)cpl_error_set_where(cpl_func);                                   \
        return 1;                                                              \
    }                                                                          \
                                                                               \
    if (cpl_pluginlist_append(list, plugin)) {                                 \
        cpl_msg_error(cpl_func, "Error adding plugin to list");                \
        (void)cpl_error_set_where(cpl_func);                                   \
        return 1;                                                              \
    }                                                                          \
                                                                               \
    return 0;                                                                  \
}                                                                              \
                                                                               \
   /* The definition of the recipe create function */                          \
static int UVES_CONCAT2X(RECIPE_NAME,create)(cpl_plugin * plugin)            \
{                                                                              \
    cpl_recipe * recipe;                                                       \
                                                                               \
    /* Do not create the recipe if an error code is already set */             \
    if (cpl_error_get_code() != CPL_ERROR_NONE) {                              \
        cpl_msg_error(cpl_func, "%s():%d: An error is already set: %s",        \
                      cpl_func, __LINE__, cpl_error_get_where());              \
        return (int)cpl_error_get_code();                                      \
    }                                                                          \
                                                                               \
    if (plugin == NULL) {                                                      \
        cpl_msg_error(cpl_func, "Null plugin");                                \
        cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);                         \
    }                                                                          \
                                                                               \
    /* Verify plugin type */                                                   \
    if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {               \
        cpl_msg_error(cpl_func, "Plugin is not a recipe");                     \
        cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);                      \
    }                                                                          \
                                                                               \
    /* Get the recipe */                                                       \
    recipe = (cpl_recipe *)plugin;                                             \
                                                                               \
    /* Create the parameters list in the cpl_recipe object */                  \
    recipe->parameters = cpl_parameterlist_new();                              \
    if (recipe->parameters == NULL) {                                          \
        cpl_msg_error(cpl_func, "Parameter list allocation failed");           \
        cpl_ensure_code(0, (int)CPL_ERROR_ILLEGAL_OUTPUT);                     \
    }                                                                          \
                                                                               \
    /* Fill the parameters list */                                             \
    return(RECIPE_FILL_PARAMS);                                                \
}                                                                              \
                                                                               \
   /* The definition of the recipe exec function */                            \
static int UVES_CONCAT2X(RECIPE_NAME,exec)(cpl_plugin * plugin)              \
{                                                                              \
    cpl_recipe * recipe;                                                       \
    int recipe_status;                                                         \
    cpl_errorstate initial_errorstate = cpl_errorstate_get();                  \
                                                                               \
    /* Return immediately if an error code is already set */                   \
    if (cpl_error_get_code() != CPL_ERROR_NONE) {                              \
        cpl_msg_error(cpl_func, "%s():%d: An error is already set: %s",        \
                      cpl_func, __LINE__, cpl_error_get_where());              \
        return (int)cpl_error_get_code();                                      \
    }                                                                          \
                                                                               \
    if (plugin == NULL) {                                                      \
        cpl_msg_error(cpl_func, "Null plugin");                                \
        cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);                         \
    }                                                                          \
                                                                               \
    /* Verify plugin type */                                                   \
    if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {               \
        cpl_msg_error(cpl_func, "Plugin is not a recipe");                     \
        cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);                      \
    }                                                                          \
                                                                               \
    /* Get the recipe */                                                       \
    recipe = (cpl_recipe *)plugin;                                             \
                                                                               \
    /* Verify parameter and frame lists */                                     \
    if (recipe->parameters == NULL) {                                          \
        cpl_msg_error(cpl_func, "Recipe invoked with NULL parameter list");    \
        cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);                         \
    }                                                                          \
    if (recipe->frames == NULL) {                                              \
        cpl_msg_error(cpl_func, "Recipe invoked with NULL frame set");         \
        cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);                         \
    }                                                                          \
                                                                               \
    /* Reset the UVES internal state before actually starting */             \
    irplib_reset();                                                            \
                                                                               \
    /* Invoke the recipe */                                                    \
    recipe_status = RECIPE_NAME(recipe->frames, recipe->parameters);           \
                                                                               \
    if (!cpl_errorstate_is_equal(initial_errorstate)) {                        \
        /* Dump the error history since recipe execution start.                \
           At this point the recipe cannot recover from the error */           \
        cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);              \
    }                                                                          \
                                                                               \
    return recipe_status;                                                      \
}                                                                              \
                                                                               \
   /* The definition of the recipe destroy function */                         \
static int UVES_CONCAT2X(RECIPE_NAME,destroy)(cpl_plugin * plugin)           \
{                                                                              \
    cpl_recipe * recipe;                                                       \
                                                                               \
    if (plugin == NULL) {                                                      \
        cpl_msg_error(cpl_func, "Null plugin");                                \
        cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);                         \
    }                                                                          \
                                                                               \
    /* Verify plugin type */                                                   \
    if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {               \
        cpl_msg_error(cpl_func, "Plugin is not a recipe");                     \
        cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);                      \
    }                                                                          \
                                                                               \
    /* Get the recipe */                                                       \
    recipe = (cpl_recipe *)plugin;                                             \
                                                                               \
    if (recipe->parameters != NULL)                                            \
        cpl_parameterlist_delete(recipe->parameters);                          \
                                                                               \
    return  0;                                                                 \
}                                                                              \
                                                                               \
  /* This dummy declaration requires the macro to be invoked as if it was      \
     a kind of function definition, with a terminating ; */                    \
extern int uves_plugin_end
#endif

/*-----------------------------------------------------------------------------
                                   Function prototypes
 -----------------------------------------------------------------------------*/


#endif
