/*
 * 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
 */

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

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

#include <sys/stat.h>
#include <math.h>

#include <cpl.h>

#include "kmclipm_math.h"

#include "kmo_utils.h"
#include "kmo_error.h"
#include "kmo_dfs.h"
#include "kmo_constants.h"
#include "kmo_priv_dark.h"
#include "kmo_cpl_extensions.h"
#include "kmo_debug.h"

/**
 * @defgroup kmos_priv_dark    Helper functions for kmo_dark
 */

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Calculate the bad pixel mask from data frames.
  @param data            Image to be examined.
  @param bias            The previously calculated bias (mean or whatever).
  @param readnoise       The previously calculated readnoise (stddev or
                         whatever).
  @param pos_bad_pix_rej Positive rejection threshold.
  @param neg_bad_pix_rej Negative rejection threshold.
  @param bad_pix_mask    Output: The calculated bad pixel mask.
  @return The number of bad pixels in the input data image.

  All values in @c data which lie outside the interval
  [bias - neg_bad_pix_rej * readnoise, bias + pos_bad_pix_rej * readnoise]
  are marked as bad pixels.
  Bad pixels are 0.0, valid pixels are 1.0.
  @c data will contain rejected pixels afterwards.

  Possible cpl_error_code set in this function:
  @li CPL_ERROR_NULL_INPUT    if @c data or @c bad_pix_mask is NULL.
  @li CPL_ERROR_ILLEGAL_INPUT if any of the values is less than zero.
*/
/*----------------------------------------------------------------------------*/
int kmo_create_bad_pix_dark(
        cpl_image   *   data,
        double          bias,
        double          readnoise,
        double          pos_bad_pix_rej,
        double          neg_bad_pix_rej,
        cpl_image   **  bad_pix_mask)
{
    int     nr_bad_pix      = 0,
            nx              = 0,
            ny              = 0;
    float   *pbad_pix_mask  = NULL,
            *pdata          = NULL;
    double  high_thresh     = 0.0,
            low_thresh      = 0.0;

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

        KMO_TRY_ASSURE((readnoise > 0) && (pos_bad_pix_rej > 0) && 
                (neg_bad_pix_rej > 0), CPL_ERROR_ILLEGAL_INPUT,
                "Values must be greater than 0!");

        nx = cpl_image_get_size_x(data);
        ny = cpl_image_get_size_y(data);

        high_thresh = bias + pos_bad_pix_rej * readnoise;
        low_thresh = bias - neg_bad_pix_rej * readnoise;

        KMO_TRY_EXIT_IF_NULL(
            *bad_pix_mask = cpl_image_new(nx, ny, CPL_TYPE_FLOAT));

        KMO_TRY_EXIT_IF_NULL(
            pbad_pix_mask = cpl_image_get_data_float(*bad_pix_mask));

        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_float(data));

        int iy = 0, ix = 0;
        for (iy = 0; iy < ny; iy++) {
            for (ix = 0; ix < nx; ix++) {
                if ((pdata[ix+iy*nx] > high_thresh) ||
                    (pdata[ix+iy*nx] < low_thresh) ||
                    kmclipm_is_nan_or_inf(pdata[ix+iy*nx]) ||
                    cpl_image_is_rejected(data, ix+1, iy+1))
                {
                    pbad_pix_mask[ix+iy*nx] = 0.0;
                    cpl_image_reject(data, ix+1, iy+1);
                    nr_bad_pix++;
                } else {
                    pbad_pix_mask[ix+iy*nx] = 1.0;
                }
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        nr_bad_pix = -1;
    }

    return nr_bad_pix;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Add badpixel border around image.
  @param data   Image to be added the badpixel border.
  @param reject TRUE if newly added border should be rejected (e.g. data
                frames), FALSE otherwise (e.g. badpixel maps)
  @return   Image with added badpixel border.

  The newly added badpixel border will also be rejected.
  Possible cpl_error_code set in this function:
  @li CPL_ERROR_NULL_INPUT    if @c data is NULL.
  @li CPL_ERROR_ILLEGAL_INPUT if @c reject isn't TRUE or FALSE.
*/
/*----------------------------------------------------------------------------*/
cpl_image * kmo_add_bad_pix_border(
        const cpl_image     *   data, 
        int                     reject)
{
    int             nx_old      = 0,
                    ny_old      = 0,
                    nx_new      = 0,
                    ny_new      = 0,
                    ix_old      = 0,
                    iy_old      = 0;
    const float     *pdata      = NULL;
    float           *pdata_out  = NULL;
    cpl_image       *data_out   = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL, CPL_ERROR_NULL_INPUT, 
                "No input data is provided!");
        KMO_TRY_ASSURE((reject == TRUE) || (reject == FALSE),
                CPL_ERROR_ILLEGAL_INPUT, "reject must be TRUE or FALSE!");
        nx_old = cpl_image_get_size_x(data);
        ny_old = cpl_image_get_size_y(data);
        nx_new = nx_old + 2*KMOS_BADPIX_BORDER;
        ny_new = ny_old + 2*KMOS_BADPIX_BORDER;

        KMO_TRY_EXIT_IF_NULL(
            data_out = cpl_image_new(nx_new, ny_new, CPL_TYPE_FLOAT));
        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_const(data));
        KMO_TRY_EXIT_IF_NULL(
            pdata_out = cpl_image_get_data(data_out));

        /* Create 4 pixel border around image (these pixels are used */
         /* internally in KMOS for some electronic calibrations) */
        int iy_new = 0, ix_new = 0;
        for (iy_new = 0; iy_new < ny_new; iy_new++) {
            iy_old = iy_new - KMOS_BADPIX_BORDER;
            for (ix_new = 0; ix_new < nx_new; ix_new++) {
                ix_old = ix_new - KMOS_BADPIX_BORDER;
                if ((ix_new < KMOS_BADPIX_BORDER) ||
                    (ix_new >= nx_new - KMOS_BADPIX_BORDER) ||
                    (iy_new < KMOS_BADPIX_BORDER) ||
                    (iy_new >= ny_new - KMOS_BADPIX_BORDER))
                {
                    /* New badpixel border */
                    pdata_out[ix_new+iy_new*nx_new] = 0.0;
                    if (reject) {
                        cpl_image_reject(data_out, ix_new+1, iy_new+1);
                    }
                } else {
                    /* Inside the image data */
                    if (cpl_image_is_rejected(data, ix_old+1, iy_old+1)) {
                        cpl_image_reject(data_out, ix_new+1, iy_new+1);
                    } else {
                        pdata_out[ix_new+iy_new*nx_new] = 
                            pdata[ix_old+iy_old*nx_old];
                    }
                }
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        cpl_image_delete(data_out); data_out = NULL;
    }
    return data_out;
}
/** @} */
