/* $Id: visir_util_weight2error.c,v 1.6 2011/09/26 09:23:27 llundin Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2011 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: llundin $
 * $Date: 2011/09/26 09:23:27 $
 * $Revision: 1.6 $
 * $Name: visir-3_5_1 $
 */

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

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

#include "visir_recipe.h"

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

#define RECIPE_STRING   "visir_util_weight2error"


/*-----------------------------------------------------------------------------
                            Private Functions prototypes
 -----------------------------------------------------------------------------*/

static cpl_error_code visir_util_weight2error_one(cpl_frameset *,
                                                  irplib_framelist *,
                                                  const cpl_frame *, int,
                                                  const cpl_parameterlist *);

VISIR_RECIPE_DEFINE(visir_util_weight2error, 0,
                    "Conversion of a swarp weight map to an error map",
                    "The files listed in the Set Of Frames (sof-file) "
                    "must be tagged:\n"
                    "VISIR-weight-map.fits " VISIR_UTIL_WEIGHT2ERROR "\n"
                    "VISIR-bpm-file.fits " VISIR_CALIB_BPM " (optional)\n"
                    "\nThe product(s) will have a FITS card\n"
                    "'HIERARCH ESO PRO CATG' with a value of:\n"
                    VISIR_UTIL_WEIGHT2ERROR_PROCATG);

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_util_weight2error   Conversion of weights to errors
 */
/*----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------*/
/**
  @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_util_weight2error(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;
    const cpl_frame  * bpmframe  = NULL;
    int                i, n;
    

    /* Identify the RAW and CALIB frames in the input frameset */
    skip_if (visir_dfs_set_groups(framelist));

    /* Objects observation */
    allframes = irplib_framelist_cast(framelist);
    skip_if(allframes == NULL);
    rawframes = irplib_framelist_extract(allframes, VISIR_UTIL_WEIGHT2ERROR);
    skip_if (rawframes == NULL);
    bpmframe = cpl_frameset_find_const(framelist, VISIR_CALIB_BPM);

    any_if("Propagating error");
    
    n = irplib_framelist_get_size(rawframes);
#ifdef _OPENMP
#pragma omp parallel for private(i)
#endif
    for (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_weight2error_one(framelist, rawframes, bpmframe,
                                            i, parlist)) {
                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_weight2error)
#endif
                didfail = errori;
            }
        }
    }

    error_if(didfail, didfail, "Failed to convert data 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    bpmframe    NULL or a bpm frame
  @param    i           The frame to process (0 for first)
  @param    parlist     The parameters list
  @return   CPL_ERROR_NONE iff OK, otherwise the relevant CPL error code.
 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_weight2error_one(cpl_frameset * framelist,
                                           irplib_framelist * rawframes,
                                           const cpl_frame * bpmframe, int i,
                                           const cpl_parameterlist * parlist)
{

    const int          n = irplib_framelist_get_size(rawframes);
    cpl_errorstate     prestate   = cpl_errorstate_get();
    cpl_frameset     * products   = cpl_frameset_new();
    cpl_frameset     * usedframes = cpl_frameset_new();
    const cpl_frame  * frame      = irplib_framelist_get_const(rawframes, i);
    const char       * filename   = cpl_frame_get_filename(frame);
    const int          nexp       = cpl_fits_count_extensions(filename);
    cpl_imagelist    * map        = NULL;
    const char       * bpmname    = bpmframe ? cpl_frame_get_filename(bpmframe)
        : NULL;
    const int          mext       = bpmname ? cpl_fits_count_extensions(bpmname) : -1;
    cpl_image        * ibpm       = NULL;
    cpl_image        * ione       = NULL;
    cpl_image        * idiv       = NULL;
    cpl_mask         * bpm        = NULL;
    cpl_mask         * bpmi       = NULL;
    cpl_propertylist * plist      = NULL;
    char             * proname    = NULL;
    int                iexp;

    cpl_msg_info(cpl_func, "Converting %d Data unit(s) in frame %d/%d",
                 1+nexp, 1+i, n);

    any_if("Propagating error");

    if (bpmname != NULL) {
        error_if(mext != 1 && mext != nexp, CPL_ERROR_INCOMPATIBLE_INPUT,
                 "Raw file %s has %d extension(s) <=> %d extension(s) of bpm-"
                 "file %s", filename, nexp, mext, bpmname);
    }

    for (iexp = 0; iexp <= nexp; iexp++) {

        cpl_propertylist_delete(plist);
        plist = cpl_propertylist_load(filename, iexp);

        skip_if(plist == NULL);

        cpl_imagelist_delete(map);
        map = cpl_imagelist_load(filename, CPL_TYPE_FLOAT, iexp);
        if (!cpl_errorstate_is_equal(prestate)) {
            cpl_errorstate_set(prestate);
        } else {
            /* FIXME: Use cpl_imagelist_power(map, -0.5);
               once DFS10528 is fixed */
            int j;
            const int m = cpl_imagelist_get_size(map);

            if (iexp <= mext) {
                cpl_image_delete(ibpm);
                ibpm = cpl_image_load(bpmname, CPL_TYPE_UNSPECIFIED, 0, iexp);
                cpl_mask_delete(bpm);
                bpm = cpl_mask_threshold_image_create(ibpm, 0.5, DBL_MAX);
                skip_if(bpm == NULL);
            }

            for (j = 0; j < m; j++) {
                cpl_image * imap = cpl_imagelist_get(map, j);

                /* Reject anything non-positive */
                cpl_mask_delete(bpmi);
                bpmi = cpl_mask_threshold_image_create(imap, 0.0, DBL_MAX);
                bug_if(cpl_mask_not(bpmi));

                /* Reject also any a-priori bad pixels */
                if (bpm)
                    skip_if(cpl_mask_or(bpmi, bpm));

                bug_if(cpl_image_reject_from_mask(imap, bpmi));
                /* FIXME: Protect cpl_image_power() from any bad input pixels */
                bug_if(cpl_image_fill_rejected(imap, 1.0));

                bug_if(cpl_image_power(imap, 0.5));

                if (ione == NULL) {
                    ione = cpl_image_new(cpl_image_get_size_x(imap),
                                         cpl_image_get_size_y(imap),
                                         CPL_TYPE_FLOAT);
                    cpl_image_add_scalar(ione, 1.0);

                }

                idiv = cpl_image_divide_create(ione, imap);

                /* Division should not create bad pixels, but just be sure */
                if (cpl_image_get_bpm_const(idiv))
                    bug_if(cpl_mask_or(bpmi, cpl_image_get_bpm_const(idiv)));

                bug_if(cpl_image_reject_from_mask(idiv, bpmi));

                bug_if(cpl_image_fill_rejected(idiv, FLT_MAX));
                /* Replace image in list */
                cpl_imagelist_set(map, idiv, j);
                idiv = NULL;
            }
        }

        if (iexp == 0) {

            bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate
                                       (frame)));
            if (bpmframe != NULL)
                bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate
                                           (bpmframe)));

            cpl_free(proname);
            proname = cpl_sprintf(RECIPE_STRING "_%d" CPL_DFS_FITS, 1+i);
            if (map == NULL) {
                skip_if(irplib_dfs_save_propertylist(products, parlist,
                                                     usedframes, RECIPE_STRING,
                                                VISIR_UTIL_WEIGHT2ERROR_PROCATG,
                                                     plist, NULL,
                                                     visir_pipe_id,
                                                     proname));
            } else {
                skip_if(irplib_dfs_save_imagelist(products, parlist,
                                                  usedframes, map,
                                                  CPL_BPP_IEEE_FLOAT,
                                                  RECIPE_STRING,
                                                VISIR_UTIL_WEIGHT2ERROR_PROCATG,
                                                  plist, NULL,
                                                  visir_pipe_id, proname));
            }
        } else {
            if (map == NULL) {
                skip_if(cpl_propertylist_save(plist, proname, CPL_IO_EXTEND));
            } else {
                skip_if(cpl_imagelist_save(map, proname, CPL_BPP_IEEE_FLOAT,
                                           plist, CPL_IO_EXTEND));
            }
        }
    }

    for (frame = cpl_frameset_get_first_const(products);
         frame != NULL;
         frame = cpl_frameset_get_next_const(products)) {
        cpl_frame * copy = cpl_frame_duplicate(frame);
        cpl_error_code error;

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

        if (error) break;
    }
    bug_if(frame != NULL);

    end_skip;

    cpl_free(proname);
    cpl_image_delete(ione);
    cpl_image_delete(idiv);
    cpl_image_delete(ibpm);
    cpl_mask_delete(bpm);
    cpl_mask_delete(bpmi);
    cpl_imagelist_delete(map);
    cpl_propertylist_delete(plist);
    cpl_frameset_delete(usedframes);
    cpl_frameset_delete(products);

    return cpl_error_get_code();

}


