/* $Id: kmo_priv_sky_mask.c,v 1.2 2012-06-01 10:38:34 aagudo Exp $
 *
 * This file is part of the KMOS 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: aagudo $
 * $Date: 2012-06-01 10:38:34 $
 * $Revision: 1.2 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include <string.h>
#include <math.h>

#include <cpl.h>

#include "kmo_utils.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_priv_stats.h"
#include "kmo_priv_functions.h"
#include "kmo_debug.h"
#include "kmo_cpl_extensions.h"

/*----------------------------------------------------------------------------*/
/**
    @defgroup kmos_priv_sky_mask     Helper functions for recipe kmo_sky_mask.

    @{
 */
/*----------------------------------------------------------------------------*/

/**
    @brief
        Calculates the sky-mask of a cube, i.e. the mask contains ones for sky
        spaxels.

    @param cube              The input cube.
    @param ranges            An even sized vector defining the wavelength ranges
                             (optional).
    @param fraction          The fraction of flagged spaxels to mask.
    @param ifu_crpix         Reference pixel position (CRPIX3 from FITS-header).
    @param ifu_crval         Value of reference pixel (CRVAL3 from FITS-header).
    @param ifu_cdelt         Delta between pixels (CDELT3 from FITS-header).
    @param cpos_rej          The positive rejection threshold.
    @param cneg_rej          The negative rejection threshold.
    @param citer             The number of iterations for rejection.
    @param size_ifu          The number of spectral slices of the IFU.

    @return                  The mask. (0: sky, 1: object)
*/

cpl_image* kmo_calc_sky_mask(const cpl_imagelist *cube,
                             const cpl_vector *ranges,
                             double fraction,
                             double ifu_crpix,
                             double ifu_crval,
                             double ifu_cdelt,
                             double cpos_rej,
                             double cneg_rej,
                             int citer)
{
    int             nx                  = 0,
                    ny                  = 0,
                    nz                  = 0,
                    j                   = 0,
                    i                   = 0,
                    g                   = 0,
                    nr_identified       = 0,
                    ind                 = 0,
                    dummy               = 0;

    kmclipm_vector  *stats              = NULL;
    cpl_vector      *identified_slices  = NULL;

    const cpl_image *tmp_img            = NULL;

    const float     *ptmp_img           = NULL;

    cpl_image       *img_sumflagged     = NULL;

    float           *pimg_sumflagged    = NULL;

    double          threshold           = 0.0,
                    *matrix_mem         = NULL;

    const double    *pmat               = NULL;

    cpl_matrix      *mat                = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(cube != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "No input data is provided!");

        KMO_TRY_ASSURE((cpos_rej >= 0.0) && (cneg_rej >= 0.0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "No negative thresholds!");

        KMO_TRY_ASSURE(citer >= 0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "No negative iterations!");

        KMO_TRY_EXIT_IF_NULL(
            tmp_img = cpl_imagelist_get_const(cube, 0));

        nx = cpl_image_get_size_x(tmp_img);
        ny = cpl_image_get_size_y(tmp_img);
        nz = cpl_imagelist_get_size(cube);

        /* img_sumflagged will contain sum of flagged pixels of each spaxel */
        KMO_TRY_EXIT_IF_NULL(
            img_sumflagged = cpl_image_new(nx, ny, CPL_TYPE_FLOAT));

        KMO_TRY_EXIT_IF_NULL(
            pimg_sumflagged = cpl_image_get_data_float(img_sumflagged));

        /* calc mode and noise */
        KMO_TRY_EXIT_IF_NULL(
            stats = kmo_calc_stats_cube(cube,
                                        NULL,
                                        cpos_rej,
                                        cneg_rej,
                                        citer));

        /* threshold = mode + 2 * sigma */
        threshold = kmclipm_vector_get(stats, 7, &dummy) +
                                       2 * kmclipm_vector_get(stats, 8, &dummy);

        /* identify valid spectral slices according ranges-vector */
        KMO_TRY_EXIT_IF_NULL(
            identified_slices = kmo_identify_slices(ranges,
                                                    ifu_crpix,
                                                    ifu_crval,
                                                    ifu_cdelt,
                                                    nz));
kmclipm_vector *ddd = kmclipm_vector_create(cpl_vector_duplicate(identified_slices));
        nr_identified = floor(kmclipm_vector_get_sum(ddd));
kmclipm_vector_delete(ddd); ddd = NULL;

        /* loop through the whole cube and flag pixels in identified slices of
           the cube where: val < mode + 2*sigma . If a pixel is flagged the
           corresponding spaxel in img_sumflagged is increased by one. */

        for (j = 0; j < ny; j++) {
            for (i = 0; i < nx; i++) {
                pimg_sumflagged[i+j*nx] = 0.0;
                for (g = 0; g < nz; g++) {
                    if (cpl_vector_get(identified_slices, g) == 1.0) {
                        KMO_TRY_EXIT_IF_NULL(
                            tmp_img = cpl_imagelist_get_const(cube, g));

                        KMO_TRY_EXIT_IF_NULL(
                            ptmp_img = cpl_image_get_data_float_const(tmp_img));

                        if (ptmp_img[i+j*nx] < threshold) {
                            pimg_sumflagged[i+j*nx] += 1.0;
                        }
                    }
                }
            }
        }

        /* Determine fraction of flagged pixels per spaxel, considering
           eventually provided wavelength ranges */
        cpl_image_divide_scalar(img_sumflagged, nr_identified);

        /* Copy values (1st column) and positions (x-pos to 2nd column, y-pos to
           3rd column) of spaxels into a matrix, sort it by rows in a way that
           the greater values are on top */
        KMO_TRY_EXIT_IF_NULL(
            matrix_mem = (double*)cpl_calloc(3*nx*ny, sizeof(double)));

        ind = 0;
        for (j = 0; j < ny; j++) {
            for (i = 0; i < nx; i++) {
                matrix_mem[ind] = pimg_sumflagged[i+j*nx];  // col 1: value
                matrix_mem[ind + 1] = (double)i;            // col 2: x-pos
                matrix_mem[ind + 2] = (double)j;            // col 3: y-pos
                ind += 3;
            }
        }

        KMO_TRY_EXIT_IF_NULL(
            mat = cpl_matrix_wrap(nx*ny, 3, matrix_mem));

        KMO_TRY_EXIT_IF_ERROR(
            cpl_matrix_sort_rows(mat, 0));

        /* Set all spaxels in the mask to one if the fraction is greater than
           the provided value
           If the amount of spaxels set to one is less than the provided
           fraction, include as many spaxels until the desired amount is reached
           (img_sumflagged is reused here as mask to be returned) */
        KMO_TRY_EXIT_IF_ERROR(
            kmo_image_fill(img_sumflagged, 1.0));

        KMO_TRY_EXIT_IF_NULL(
            pmat = cpl_matrix_get_data_const(mat));

        threshold = 0;
        for (i = 0; i < nx*ny; i++) {
            if ((pmat[i*3] > 0.95) || (threshold < fraction * nx * ny)) {
                /* pimg_sumflagged[x+y*nx],
                where x=pmat[i*3 + 1]  and y = pmat[i*3 + 2] */
                pimg_sumflagged[(int)(pmat[i*3 + 1] + pmat[i*3 + 2] * nx)] =
                                                                            0.0;
                threshold++;
            } else {
                /* stop the for-loop, since the matrix is sorted */
                break;
            }
        }

        cpl_matrix_unwrap(mat);
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        cpl_image_delete(img_sumflagged); img_sumflagged = NULL;
    }

    cpl_vector_delete(identified_slices); identified_slices = NULL;
    kmclipm_vector_delete(stats); stats = NULL;
    cpl_free(matrix_mem); matrix_mem = NULL;

    return img_sumflagged;
}

/** @{ */
