/* $Id: visir_util_destripe.c,v 1.9 2013-05-14 12:59:23 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2012 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: jtaylor $
 * $Date: 2013-05-14 12:59:23 $
 * $Revision: 1.9 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "visir_recipe.h"

#include "visir_spc_distortion.h"
#include "visir_destripe.h"
#ifdef _OPENMP
#include <omp.h>
#endif

/*-----------------------------------------------------------------------------
                                Defines
 -----------------------------------------------------------------------------*/

#define RECIPE_STRING   "visir_old_util_destripe"
// FIXME:
#define VISIR_UTIL_DESTRIPE_PROCATG "DESTRIPED"

/*-----------------------------------------------------------------------------
                            Private Functions prototypes
 -----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_destripe_one(cpl_frameset *,
                                        irplib_framelist *, int,
                                        const cpl_parameterlist *,
                                        const int,
                                        const cpl_boolean);

#ifdef VISIR_CHAIN
#define cpl_plugin_get_info visir_old_util_destripe_get_info
#endif
cpl_recipe_define(visir_old_util_destripe, VISIR_BINARY_VERSION,
                  "Julian Taylor", PACKAGE_BUGREPORT, "2012",
                  "Old DRS detector: Attempt to remove stripes in spectral data",
                  "The files listed in the Set Of Frames (sof-file) "
                  "must be tagged:\n"
                  "VISIR-chopnod-corrected-file.fits " VISIR_UTIL_CORRECTED
                  "\nThe product(s) will have a FITS card\n"
                  "'HIERARCH ESO PRO CATG' with a value of:\n"
                  VISIR_UTIL_DESTRIPE_PROCATG);

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_util_destripe   remove stripes
 */
/*----------------------------------------------------------------------------*/

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


/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Fill the recipe parameterlist
  @param    self  The parameterlist
  @return   0 iff everything is ok

 */
/*----------------------------------------------------------------------------*/
static cpl_error_code
visir_old_util_destripe_fill_parameterlist(cpl_parameterlist * self)
{

    return visir_parameter_set(self, RECIPE_STRING, VISIR_PARAM_STRIPITE |
                               VISIR_PARAM_STRIPMOR)
        ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
}


/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here
  @param    framelist   the frames list
  @param    parlist     the parameters list
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static int visir_old_util_destripe(cpl_frameset            * framelist,
                                const cpl_parameterlist * parlist)
{
#ifdef _OPENMP
    cpl_errorstate     cleanstate = cpl_errorstate_get();
#endif
    cpl_error_code     didfail    = CPL_ERROR_NONE;
    irplib_framelist * allframes  = NULL;
    irplib_framelist * rawframes  = NULL;
    int                n;

#ifdef _OPENMP
    omp_set_num_threads(visir_get_num_threads(CPL_FALSE));
#endif

    skip_if (0);

    int ndestripe = visir_parameterlist_get_int(parlist, RECIPE_STRING,
                                                VISIR_PARAM_STRIPITE);
    bug_if (0);

    cpl_boolean morpho_destripe = ndestripe <= 0 ? CPL_FALSE :
        visir_parameterlist_get_bool(parlist, RECIPE_STRING,
                                     VISIR_PARAM_STRIPMOR);
    bug_if (0);

    /* Objects observation */
    allframes = irplib_framelist_cast(framelist);
    skip_if(allframes == NULL);
    rawframes = irplib_framelist_extract_regexp(allframes, "^("
                                                VISIR_UTIL_CORRECTED"|"
                                                VISIR_UTIL_UNDISTORT_PROCATG
                                                ")$",
                                                CPL_FALSE);
    skip_if (rawframes == NULL);
    n = irplib_framelist_get_size(rawframes);

#ifdef _OPENMP
#pragma omp parallel for
#endif
    for (cpl_size i = 0; i < n; i++) {
        if (!didfail) {

            /* The total number of iterations must be pre-determined for the
               parallelism to work. In case of an error we can therefore not
               break, so instead we skip immediately to the next iteration.
               FIXME: This check on didfail does not guarantee that only one
               iteration can cause an error to be dumped, but it is not
               worse than checking on a thread-local state, e.g. errori. */

            if (visir_util_destripe_one(framelist, rawframes, i, parlist,
                                         ndestripe, morpho_destripe)) {
                const cpl_error_code errori = cpl_error_set_where(cpl_func);
#ifdef _OPENMP
                /* Cannot access these errors after the join,
                   so dump them now. :-(((((((((((((((((((( */
                cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
                cpl_errorstate_set(cleanstate);
#pragma omp critical(visir_util_destripe)
#endif
                didfail = errori;
            }
        }
    }

    error_if(didfail, didfail, "Failed to destripe images in %d frame(s)", n);

    end_skip;

    irplib_framelist_delete(allframes);
    irplib_framelist_delete(rawframes);

    return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Process one frame
  @param    framelist       The frameset to append products to
  @param    rawframes       The frames list (will load the propertylist)
  @param    i               The frame to process (0 for first)
  @param    parlist         The parameters list
  @param    ndestripe       number of destripe iterations
  @param    morpho_destripe morphological cleaning during destripe
  @return   CPL_ERROR_NONE iff OK, otherwise the relevant CPL error code.
  @see visir_destripe_image()
 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_destripe_one(cpl_frameset * framelist,
                                        irplib_framelist * rawframes, int iframe,
                                        const cpl_parameterlist * parlist,
                                        const int ndestripe,
                                        const cpl_boolean morpho_destripe)
{

    cpl_frameset  * products   = cpl_frameset_new();
    cpl_frameset  * usedframes = cpl_frameset_new();
    const cpl_frame * rawframe  = irplib_framelist_get_const(rawframes, iframe);
    const char      * filename  = cpl_frame_get_filename(rawframe);
    cpl_image       * img = NULL;
    char            * proname   = cpl_sprintf(RECIPE_STRING "_%d" CPL_DFS_FITS,
                                              1+iframe);
    cpl_propertylist * plist = NULL;
    visir_data_type dtype;
    cpl_size next;

    cpl_frame_set_group(irplib_framelist_get(rawframes, iframe),
                        CPL_FRAME_GROUP_RAW);
    bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate
                               (irplib_framelist_get_const(rawframes,
                                                           iframe))));

    irplib_framelist_load_propertylist(rawframes, iframe, 0, ".?", CPL_FALSE);
    plist = irplib_framelist_get_propertylist(rawframes, iframe);

    skip_if(visir_get_data_type(rawframe, plist, &dtype, &next));
    skip_if(irplib_dfs_save_propertylist(products, parlist, usedframes,
                                         RECIPE_STRING,
                                         VISIR_UTIL_DESTRIPE_PROCATG, plist,
                                         NULL,
                                         visir_pipe_id, proname));

    for (cpl_size iext = 0; iext < 1 + next; iext++) {
        cpl_errorstate prestate = cpl_errorstate_get();

        img = cpl_image_load(filename, CPL_TYPE_FLOAT, 0, iext);
        if (img == NULL) {
            cpl_msg_info(cpl_func, "No image-data in extension %d", (int)iext);
            cpl_errorstate_set(prestate);
            continue;
        }

        /* FIXME: handle aqu data? */
        if (visir_data_is_drs(dtype)) {
            if(visir_destripe_image(img, ndestripe,
                                    VISIR_DESTRIPE_DETECT,
                                    VISIR_DESTRIPE_DETECT_THRESHOLD,
                                    morpho_destripe)) {
                cpl_error_set_message(cpl_func,
                                      cpl_error_get_code(),
                                      "Failure for extension %d/%d",
                                      (int)iext, (int)next);
                break;
            }
        }

        cpl_image_save(img, proname, CPL_BPP_IEEE_FLOAT,
                       NULL, CPL_IO_EXTEND);
    }

    FOR_EACH_FRAMESET_C(frame, products) {
        cpl_frame * copy = cpl_frame_duplicate(frame);
        cpl_error_code error;

#ifdef _OPENMP
#pragma omp critical(visir_util_destripe_one)
#endif
        error = cpl_frameset_insert(framelist, copy);

        skip_if(error);
    }


    end_skip;

    cpl_image_delete(img);
    cpl_frameset_delete(usedframes);
    cpl_frameset_delete(products);
    cpl_free(proname);

    return cpl_error_get_code();
}
