/* $Id: visir_util_find_beam.c,v 1.2 2011/12/21 14:42:52 llundin Exp $
 *
 * This file is part of the VISIR Pipeline
 * 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: llundin $
 * $Date: 2011/12/21 14:42:52 $
 * $Revision: 1.2 $
 * $Name: visir-3_5_1 $
 */

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

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

#include "visir_recipe.h"

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

#define RECIPE_STRING   "visir_util_find_beam"

#ifndef VISIR_UTIL_FIND_BEAM_HMEAN
#define VISIR_UTIL_FIND_BEAM_HMEAN 3
#endif


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

static
cpl_error_code visir_util_find_beam_one_refine(cpl_bivector *,
                                               const cpl_imagelist *,
                                               const cpl_propertylist *,
                                               const cpl_parameterlist *);

static cpl_error_code visir_util_find_beam_one(cpl_frameset *,
                                               irplib_framelist *,
                                               irplib_framelist *,
                                               int, cpl_boolean,
                                               const cpl_parameterlist *);

cpl_recipe_define(visir_util_find_beam, VISIR_BINARY_VERSION,                
                  "Lars Lundin", PACKAGE_BUGREPORT, "2011",
                  "Find the positive beam in a nodded image",
                  "The files listed in the Set Of Frames (sof-file) "
                  "must be tagged pair-wise:\n"
                  "VISIR-raw-file.fits " VISIR_UTIL_NODDED "\n"
                  "VISIR-bpm-file.fits " VISIR_CALIB_BPM "\n"
                  "\nThe product(s) will have a FITS card\n"
                  "'HIERARCH ESO PRO CATG' with a value of:\n"
                  VISIR_UTIL_NODDED_REFINED_POS "\n");

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_util_find_beam   Find the beam in nodded images
 */
/*----------------------------------------------------------------------------*/

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

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

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

    const char * context = PACKAGE "." RECIPE_STRING;
    cpl_error_code err;

    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);

    /* Fill the parameters list */

    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "hmean", VISIR_UTIL_FIND_BEAM_HMEAN,
                                       NULL, context, "The number of frames in "
                                       "the running mean used to make the "
                                       "object visible is 1 + twice this "
                                       "number");
    cpl_ensure_code(!err, err);

    return 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_util_find_beam(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;
    irplib_framelist * bpmframes = NULL;
    int                i, n, nbad;
    

    /* 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_NODDED);
    skip_if (rawframes == NULL);
    bpmframes = irplib_framelist_extract(allframes, VISIR_CALIB_BPM);
    skip_if (bpmframes == NULL);
    
    n = irplib_framelist_get_size(rawframes);
    nbad = irplib_framelist_get_size(bpmframes);
    error_if(nbad != n && nbad != 1, CPL_ERROR_INCOMPATIBLE_INPUT,
             "%d raw-frames <=> %d bpm frames", n, nbad);

#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_find_beam_one(framelist, rawframes, bpmframes, i,
                                         nbad == 1, 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_find_beam)
#endif
                didfail = errori;
            }
        }
    }

    error_if(didfail, didfail, "Failed to find beam in %d frame(s)", n);

    end_skip;

    irplib_framelist_delete(allframes);
    irplib_framelist_delete(rawframes);
    irplib_framelist_delete(bpmframes);

    return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Process one frame
  @param    framelist   The frameset to append products to
  @param    rawframes   The raw frames list (will load the propertylist)
  @param    bpmframes   The bpm frames list (will load the propertylist)
  @param    i           The frame to process (0 for first)
  @param    bshared     CPL_TRUE iff the bpm is shared among all raw frames
  @param    parlist     The parameters list
  @return   CPL_ERROR_NONE iff OK, otherwise the relevant CPL error code.
 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_find_beam_one(cpl_frameset * framelist,
                                        irplib_framelist * rawframes,
                                        irplib_framelist * bpmframes,
                                        int i, cpl_boolean bshared,
                                        const cpl_parameterlist * parlist)
{

    cpl_frameset  * products   = cpl_frameset_new();
    cpl_frameset  * usedframes = cpl_frameset_new();
    const cpl_frame * frame;

    char * findname = cpl_sprintf(RECIPE_STRING "_%d" CPL_DFS_FITS, i);

    const int            n = irplib_framelist_get_size(rawframes);
    cpl_frameset       * rawone = cpl_frameset_new();
    cpl_frameset       * bpmone = cpl_frameset_new();
    cpl_frame * rawframe =
        cpl_frame_duplicate(irplib_framelist_get_const(rawframes, i));
    cpl_frame * bpmframe =
        cpl_frame_duplicate(irplib_framelist_get_const(bpmframes,
                                                       bshared ? 0 : i));
    const cpl_error_code errr = cpl_frameset_insert(rawone, rawframe);
    const cpl_error_code errb = cpl_frameset_insert(bpmone, bpmframe);
    const char * rawname = cpl_frame_get_filename(rawframe);
    cpl_propertylist * plist =
        cpl_propertylist_load_regexp(rawname, 0, "("
                                     "ESO DRS CUMOFFSETX" "|"
                                     "ESO DRS CUMOFFSETY" "|"
                                     VISIR_PFITS_DOUBLE_CHOP_THROW "|"
                                     VISIR_PFITS_STRING_PIXSCALE ")",
                                     CPL_FALSE);
    cpl_imagelist * rawlist =
        cpl_imagelist_load_frameset(rawone, CPL_TYPE_FLOAT, 0, -1);
    cpl_imagelist * bpmlist =
        cpl_imagelist_load_frameset(bpmone, CPL_TYPE_INT, 0, -1);
    cpl_mask      * bpm       = NULL;
    cpl_image     * newbpmimg = NULL;
    cpl_propertylist * qclist = cpl_propertylist_new();

    const int       m = cpl_imagelist_get_size(rawlist);
    cpl_bivector  * offsets = cpl_bivector_new(m);
    int             j;

    skip_if(rawlist == NULL);
    skip_if(bpmlist == NULL);

    bug_if(errr);
    bug_if(errb);
    skip_if(plist == NULL);

    error_if(cpl_imagelist_get_size(bpmlist) != m, CPL_ERROR_INCOMPATIBLE_INPUT,
             "Frame-pair %d/%d: %d image(s) <=> %d bad pixel map(s)", 1+i, n,
             m, (int)cpl_imagelist_get_size(bpmlist));

    bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(rawframe)));
    bug_if(cpl_frameset_insert(usedframes, cpl_frame_duplicate(bpmframe)));

    cpl_msg_info(cpl_func, "F(%d): %s", i, rawname);

    /* Apply the bad pixel maps to the images */
    for (j = 0; j < m; j++) {
        const cpl_image * bpmimg = cpl_imagelist_get_const(bpmlist, j);
        cpl_image * rawimg       = cpl_imagelist_get(rawlist, j);

        cpl_mask_delete(bpm);
        bpm = cpl_mask_threshold_image_create(bpmimg, 0.5, FLT_MAX);

        bug_if(cpl_image_reject_from_mask(rawimg, bpm));

    }

    skip_if(visir_util_find_beam_one_refine(offsets, rawlist, plist, parlist));


    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_find_beam_one)
#endif
        error = cpl_frameset_insert(framelist, copy);

        if (error) break;
    }

    bug_if(frame != NULL);


    end_skip;

    cpl_free(findname);

    cpl_frameset_delete(rawone);
    cpl_frameset_delete(bpmone);
    cpl_frameset_delete(usedframes);
    cpl_frameset_delete(products);

    cpl_propertylist_delete(plist);
    cpl_propertylist_delete(qclist);

    cpl_mask_delete(bpm);
    cpl_image_delete(newbpmimg);
    cpl_imagelist_delete(rawlist);
    cpl_imagelist_delete(bpmlist);

    cpl_bivector_delete(offsets);

    return cpl_error_get_code();

}


/*----------------------------------------------------------------------------*/
/**
  @brief    Process one frame
  @param    framelist   The frameset to append products to
  @param    rawframes   The raw frames list (will load the propertylist)
  @param    bpmframes   The bpm frames list (will load the propertylist)
  @param    i           The frame to process (0 for first)
  @param    bshared     CPL_TRUE iff the bpm is shared among all raw frames
  @param    parlist     The parameters list
  @return   CPL_ERROR_NONE iff OK, otherwise the relevant CPL error code.
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code
visir_util_find_beam_one_refine(cpl_bivector * self,
                                const cpl_imagelist * nodded,
                                const cpl_propertylist * plist,
                                const cpl_parameterlist * parlist)
{

    /* Get the chopping throw in pixels */
    const char * sscale       = visir_pfits_get_pixscale(plist);
    const double pscale       = sscale ? atof(sscale) : 0.0;
    const double pthrow       = pscale > 0.0
        ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
    const double x0 = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETX");
    const double y0 = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETY");
    double x2[2], y2[2];

    const int hmean = irplib_parameterlist_get_int(parlist, PACKAGE,
                                                   RECIPE_STRING, "hmean");
    const int nmean = 1 + 2 * hmean;
    cpl_propertylist * qclist = cpl_propertylist_new();
    cpl_image * runsum = cpl_image_duplicate(cpl_imagelist_get_const(nodded,
                                                                      0));
    cpl_image * invsum = NULL;
    int i;

    skip_if(nmean < 1);
    bug_if(self == NULL);

    for (i = 1; i < nmean; i++) {
        skip_if(cpl_image_add(runsum, cpl_imagelist_get_const(nodded, i)));
    }

    for (i = 0; i < nmean; i++) {

        cpl_image_delete(invsum);
        invsum = cpl_image_multiply_scalar_create(runsum, -1.0);

        skip_if(visir_img_find_beam_two(qclist, runsum, invsum, 0.25, pthrow,
                                        x2, y2));

        cpl_msg_info(cpl_func, "BB(%d): %g <=> %g. %g <=> %g", i, x0, x2[0],
                     y0, y2[0]);

        if (i >= hmean && i < nmean - hmean) {
            cpl_image_subtract(runsum, cpl_imagelist_get_const(nodded,
                                                                i-hmean));
            cpl_image_add(runsum, cpl_imagelist_get_const(nodded,
                                                                i+hmean));
        }
    }

    end_skip;

    cpl_image_delete(runsum);
    cpl_image_delete(invsum);
    cpl_propertylist_delete(qclist);

    return cpl_error_get_code();
}
