/* $Id: hawki_calib.c,v 1.7 2013-03-25 11:35:06 cgarcia Exp $
 *
 * This file is part of the HAWKI 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-03-25 11:35:06 $
 * $Revision: 1.7 $
 * $Name: not supported by cvs2svn $
 */

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

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

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

#include "hawki_utils.h"
#include "hawki_calib.h"
#include "hawki_pfits.h"
#include "hawki_load.h"

/*----------------------------------------------------------------------------*/
/**
 * @defgroup hawki_calib     Basic calibration Utilities
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the calibration to the images
  @param    ilist   the input image list
  @param    flat    the flat field
  @param    dark    the dark (scaled to exp time of the science images)
  @param    bpm     the bad pixels map
  @return   0 if everything is ok, -1 otherwise
  
  This function takes as an input an image list that contains four images,
  one for each HAWK-I detector and the same for the flat, dark and bpm.
  It applies the 3 corrections (if images are not null) to each detector.
  Take into account that the dark is just subtracted, no scaling is done.
  For dark and flat corrections the formula is as follows:
  \f[ bc = \frac{(raw-dark)}{flat}\f]   
  
 */
/*----------------------------------------------------------------------------*/
int hawki_flat_dark_bpm_imglist_calib
(cpl_imagelist  *  ilist,
 cpl_imagelist  *  flat,
 cpl_imagelist  *  dark,
 cpl_imagelist  *  bpm)
{
    int   idet;

    /* Test entries */
    if (ilist == NULL) return -1 ;

    /* Dark correction */
    if (dark != NULL)
    {
        cpl_msg_info(cpl_func, "Subtracting the dark to each chip image") ;
        /* Apply the dark correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            if (cpl_image_subtract(cpl_imagelist_get(ilist, idet),
                                   cpl_imagelist_get(dark, idet))!=CPL_ERROR_NONE) 
            {
                cpl_msg_error(cpl_func,"Cannot apply the dark to chip %d",
                              idet+1);
                return -1 ;
            }
        }
    }

    /* Flat-field correction */
    if (flat != NULL) 
    {
        cpl_msg_info(cpl_func, "Dividing the flat to each chip image") ;
        /* Apply the flat correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            if (cpl_image_divide(cpl_imagelist_get(ilist, idet),
                                 cpl_imagelist_get(flat, idet))!=CPL_ERROR_NONE) 
            {
                cpl_msg_error(__func__,"Cannot apply the flatfield to chip %d",
                              idet+1);
                return -1 ;
            }
        }
    }

    /* Correct the bad pixels if requested */
    if (bpm != NULL) 
    {
        cpl_msg_info(cpl_func, "Correct the bad pixels to each chip image");
        /* Apply the flat correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            cpl_mask    * bpm_im_bin ;
            /* Convert the map from integer to binary */
            bpm_im_bin = cpl_mask_threshold_image_create
                (cpl_imagelist_get(bpm, idet), -0.5, 0.5) ;
            cpl_mask_not(bpm_im_bin) ;
            cpl_image_reject_from_mask(cpl_imagelist_get(ilist, idet), bpm_im_bin);
            if (cpl_detector_interpolate_rejected
                    (cpl_imagelist_get(ilist, idet)) != CPL_ERROR_NONE) 
            {
                    cpl_msg_error
                        (cpl_func, "Cannot clean the bad pixels in chip %d",
                         idet+1);
                    cpl_mask_delete(bpm_im_bin) ;
                    return -1 ;
            }
            cpl_mask_delete(bpm_im_bin) ;
        }
    }

    /* Return */
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the calibration to the images
  @param    ilist   the input image list
  @param    flat    the flat field
  @param    bpm     the bad pixels map
  @return   0 if everything is ok, -1 otherwise

  This function takes as an input an image list that contains four images,
  one for each HAWK-I detector and the same for the flat and bpm.
  It applies the 2 corrections (if images are not null) to each detector.

 */
/*----------------------------------------------------------------------------*/
int hawki_flat_bpm_imglist_calib(
        cpl_imagelist       *   ilist,
        cpl_imagelist       *   flat,
        cpl_imagelist       *   bpm)
{
    int                 idet;

    /* Test entries */
    if (ilist == NULL) return -1 ;

    /* Flat-field correction */
    if (flat != NULL) 
    {
        cpl_msg_info(cpl_func, "Divide the images by the flatfield") ;
        /* Apply the flat correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            if (cpl_image_divide(cpl_imagelist_get(ilist, idet),
                                 cpl_imagelist_get(flat, idet))!=CPL_ERROR_NONE) 
            {
                cpl_msg_error(cpl_func,"Cannot apply the flatfield to the images");
                return -1 ;
            }
        }
    }

    /* Correct the bad pixels if requested */
    if (bpm != NULL) 
    {
        cpl_msg_info(cpl_func, "Correct the bad pixels in the images") ;
        /* Apply the flat correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            cpl_mask        *   bpm_im_bin ;
            /* Convert the map from integer to binary */
            bpm_im_bin = cpl_mask_threshold_image_create
                (cpl_imagelist_get(bpm, idet), -0.5, 0.5) ;
            cpl_mask_not(bpm_im_bin) ;
            cpl_image_reject_from_mask(cpl_imagelist_get(ilist, idet), bpm_im_bin);
            if (cpl_detector_interpolate_rejected
                    (cpl_imagelist_get(ilist, idet)) != CPL_ERROR_NONE) 
            {
                    cpl_msg_error
                        (cpl_func, "Cannot clean the bad pixels in detector %d",
                         idet+1);
                    cpl_mask_delete(bpm_im_bin) ;
                    return -1 ;
            }
            cpl_mask_delete(bpm_im_bin) ;
        }
    }

    /* Return */
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the background subtraction to the images
  @param    ilist   the input image list
  @param    bkg     the background images
  @return   0 if everything is ok, -1 otherwise
 */
/*----------------------------------------------------------------------------*/
int hawki_bkg_imglist_calib
(cpl_imagelist *       ilist,
 cpl_imagelist *       bkg)
{
    int                 idet;

    /* Test entries */
    if (ilist == NULL) return -1 ;

    /* Background correction */
    if (bkg != NULL) 
    {
        cpl_msg_info(cpl_func, "Subtract the images by the bkg") ;
        /* Apply the bkg correction to the images */
        for(idet = 0; idet < HAWKI_NB_DETECTORS ; ++idet)
        {
            if (cpl_image_subtract(cpl_imagelist_get(ilist, idet),
                                   cpl_imagelist_get(bkg, idet))!=CPL_ERROR_NONE) 
            {
                cpl_msg_error(cpl_func,"Cannot apply the bkg to the images");
                return -1 ;
            }
        }
    }

    /* Return */
    return 0 ;

}

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the calibration to the images
  @param    ilist   the input image list
  @param    flat    the flat field
  @param    dark    the dark (scaled to exp time of the science images)
  @param    bpm     the bad pixels map
  @param    idet    the detector id. 
  @return   0 if everything is ok, -1 otherwise
  
  This function takes as an input a list of images which in principle
  should belong to the same HAWK-I detector. It also takes as an input
  an image for the flat, the dark and bpm for that detector. It is the
  responsibility of the caller to ensure that the detector of all these
  inputs is the same.
  Take into account that the dark is just subtracted, no scaling is done.
  For dark and flat corrections the formula is as follows:
  \f[ bc = \frac{(raw-dark)}{flat}\f]   
  
 */
/*----------------------------------------------------------------------------*/
int hawki_flat_dark_bpm_detector_calib(
        cpl_imagelist       *   ilist,
        cpl_image           *   flat,
        cpl_image           *   dark,
        cpl_image           *   bpm)
{
    cpl_mask        *   bpm_im_bin ;
    int                 i ;

    /* Test entries */
    if (ilist == NULL) return -1 ;

    /* Dark correction */
    if (dark != NULL) 
    {
        cpl_msg_info(cpl_func, "Subtract the images by the dark") ;
        if (cpl_imagelist_subtract_image(ilist, dark)!=CPL_ERROR_NONE) 
        {
            cpl_msg_error(cpl_func,"Cannot apply the dark to the images");
            return -1 ;
        }
    }

    /* Flat-field correction */
    if (flat != NULL) {
        cpl_msg_info(cpl_func, "Divide the images by the flatfield") ;

        /* Apply the flatfield correction to the images */
        if (cpl_imagelist_divide_image(ilist, flat)!=CPL_ERROR_NONE) {
            cpl_msg_error(cpl_func,"Cannot apply the flatfield to the images");
            return -1 ;
        }
    }

    /* Correct the bad pixels if requested */
    if (bpm != NULL) {
        cpl_msg_info(cpl_func, "Correct the bad pixels in the images") ;

        /* Convert the map from integer to binary */
        bpm_im_bin = cpl_mask_threshold_image_create(bpm, -0.5, 0.5) ;
        cpl_mask_not(bpm_im_bin) ;
        /* Apply the bad pixels cleaning */
        for (i=0 ; i<cpl_imagelist_get_size(ilist) ; i++) {
            cpl_image_reject_from_mask(cpl_imagelist_get(ilist, i), bpm_im_bin);
            if (cpl_detector_interpolate_rejected(
                        cpl_imagelist_get(ilist, i)) != CPL_ERROR_NONE) {
                cpl_msg_error(cpl_func, "Cannot clean the bad pixels in obj %d",
                        i+1);
                cpl_mask_delete(bpm_im_bin) ;
                return -1 ;
            }
        }
        cpl_mask_delete(bpm_im_bin) ;
    }

    /* Return */
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the calibration to the images
  @param    ilist   the input image list
  @param    flat    the flat field
  @param    bpm     the bad pixels map
  @param    idet    the detector id 
  @return   0 if everything is ok, -1 otherwise

  This function takes as an input a list of images which in principle
  should belong to the same HAWK-I detector. It also takes as an input
  an image for the flat, the dark and bpm for that detector. It is the
  responsability of the caller to ensure that the detector of all these
  inputs is the same.
  For dark and flat corrections the formula is as follows:
  \f[ bc = \frac{(raw-dark)}{flat}\f]   
  
 */
/*----------------------------------------------------------------------------*/
int hawki_flat_bpm_detector_calib(
        cpl_imagelist       *   ilist,
        cpl_image           *   flat,
        cpl_image           *   bpm)
{
    cpl_mask        *   bpm_im_bin ;
    int                 i ;

    /* Test entries */
    if (ilist == NULL) return -1 ;

    /* Flat-field correction */
    if (flat != NULL) {
        cpl_msg_info(cpl_func, "Divide the images by the flatfield") ;

        /* Apply the flatfield correction to the images */
        if (cpl_imagelist_divide_image(ilist, flat)!=CPL_ERROR_NONE) {
            cpl_msg_error(cpl_func,"Cannot apply the flatfield to the images");
            return -1 ;
        }
    }

    /* Correct the bad pixels if requested */
    if (bpm != NULL) {
        cpl_msg_info(cpl_func, "Correct the bad pixels in the images") ;

        /* Convert the map from integer to binary */
        bpm_im_bin = cpl_mask_threshold_image_create(bpm, -0.5, 0.5) ;
        cpl_mask_not(bpm_im_bin) ;
        /* Apply the bad pixels cleaning */
        for (i=0 ; i<cpl_imagelist_get_size(ilist) ; i++) {
            cpl_image_reject_from_mask(cpl_imagelist_get(ilist, i), bpm_im_bin);
            if (cpl_detector_interpolate_rejected(
                        cpl_imagelist_get(ilist, i)) != CPL_ERROR_NONE) {
                cpl_msg_error(cpl_func, "Cannot clean the bad pixels in obj %d",
                        i+1);
                cpl_mask_delete(bpm_im_bin) ;
                return -1 ;
            }
        }
        cpl_mask_delete(bpm_im_bin) ;
    }

    /* Return */
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Trim a list of hawki images (usually from one detector)
  @param    ilist   The image list
  @param    nborder The number of pixels to trim in each border 
  @return   The trimmed images. NULL otherwise
  
  Not that the original ilist is emptied, but the user is still responsible
  to deallocate it with cpl_imagelist_delete() 
  
 */
cpl_imagelist *  hawki_trim_detector_calib
(cpl_imagelist       *   imalist,
 int                     nborder)
{
    int i = 0;
    /* Discard the pixels on the sides */
    cpl_imagelist * trimmed_images = cpl_imagelist_new();
    while(cpl_imagelist_get_size(imalist) > 0)
    {
        cpl_image * non_trimmed;
        cpl_image * trimmed;
        int         nx;
        int         ny;
        
        non_trimmed = cpl_imagelist_unset(imalist, 0);
        nx = cpl_image_get_size_x(non_trimmed);
        ny = cpl_image_get_size_y(non_trimmed);
        trimmed = cpl_image_extract(non_trimmed, 
                                    nborder+1, nborder+1, 
                                    nx-nborder, ny-nborder);
        cpl_imagelist_set(trimmed_images, trimmed, i);
        ++i;
        cpl_image_delete(non_trimmed);
    }

    return trimmed_images;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Apply the BPM correction to an image
  @param    ima     the image to correct
  @param    idet    the detector the image comes from
  @param    bpm     the bad pixels map
  @return   0 if everything is ok, -1 otherwise
  
 */
/*----------------------------------------------------------------------------*/
int hawki_bpm_calib(
        cpl_image           *   ima,
        const char          *   bpm,
        int                     idet)
{
    cpl_mask        *   bpm_im_bin ;
    cpl_image       *   bpm_im_int ;
    int                 ext_nb ;

    /* Test entries */
    if (ima == NULL) return -1 ;
    if (idet < 1 || idet > HAWKI_NB_DETECTORS) return -1 ;
    if (bpm == NULL) return -1 ;

    /* Get the extension number for this detector */
    if ((ext_nb = hawki_get_ext_from_detector(bpm, idet)) == -1) {
        cpl_msg_error(__func__, "Cannot get the extension with detector %d", idet) ;
        return -1 ;
    }
     /* Load the bad pixels image */
    if ((bpm_im_int = cpl_image_load(bpm, CPL_TYPE_INT, 0, ext_nb)) == NULL) {
        cpl_msg_error(cpl_func, "Cannot load the bad pixel map %s", bpm) ;
        return -1 ;
    }
    /* Convert the map from integer to binary */
    bpm_im_bin = cpl_mask_threshold_image_create(bpm_im_int, -0.5, 0.5) ;
    cpl_mask_not(bpm_im_bin) ;
    cpl_image_delete(bpm_im_int) ;
    /* Apply the bad pixels cleaning */
    cpl_image_reject_from_mask(ima, bpm_im_bin);
    if (cpl_detector_interpolate_rejected(ima) != CPL_ERROR_NONE) {
        cpl_msg_error(cpl_func, "Cannot clean the bad pixels");
        cpl_mask_delete(bpm_im_bin) ;
        return -1 ;
    }
    cpl_mask_delete(bpm_im_bin) ;

    /* Return */
    return 0 ;
}

/**@}*/

