/* $Id: hawki_image_stats.c,v 1.10 2013-08-22 15:50:29 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-08-22 15:50:29 $
 * $Revision: 1.10 $
 * $Name: not supported by cvs2svn $
 */

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

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

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

#include "hawki_image_stats.h"
#include "hawki_dfs.h"
#include "hawki_utils.h"
#include "hawki_load.h"

/*----------------------------------------------------------------------------*/
/*        Private functions                                                   */
/*----------------------------------------------------------------------------*/
float hawki_tools_get_kth_float(float * a,
                                int        n,
                                int        k);

/*----------------------------------------------------------------------------*/
/**
 * @defgroup hawki_image_stats   Compute several statistics on images 
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Initialize the table with all the statistics columns 
  @param    image_stats     Table to fill with the statistics 
  @return   0 if everything is ok, -1 otherwise

  This function creates the following columns in the given table:
  MINIMUM, MAXIMUM, MEDIAN, MEAN, RMS, USED
 */
/*----------------------------------------------------------------------------*/
int hawki_image_stats_initialize
(cpl_table ** raw_stats)
{
    int idet;
    /* Error state variables */
    cpl_errorstate  prestate = cpl_errorstate_get();
    
    /* Check inputs */
    if(raw_stats == NULL)
        return -1;
    for( idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        if(raw_stats[idet] == NULL)
            return -1;
    }
    
    /* Creates the proper columns */
    for( idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        cpl_table_new_column
            (raw_stats[idet],HAWKI_COL_STAT_MIN,CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(raw_stats[idet],HAWKI_COL_STAT_MIN,"ADU");
        cpl_table_new_column
            (raw_stats[idet],HAWKI_COL_STAT_MAX,CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(raw_stats[idet],HAWKI_COL_STAT_MAX,"ADU");
        cpl_table_new_column
            (raw_stats[idet],HAWKI_COL_STAT_MED,CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(raw_stats[idet],HAWKI_COL_STAT_MED,"ADU");
        cpl_table_new_column
            (raw_stats[idet],HAWKI_COL_STAT_MEAN,CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(raw_stats[idet],HAWKI_COL_STAT_MEAN,"ADU");
        cpl_table_new_column    
            (raw_stats[idet],HAWKI_COL_STAT_RMS,CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(raw_stats[idet],HAWKI_COL_STAT_RMS,"ADU");
        cpl_table_new_column
            (raw_stats[idet],HAWKI_COL_STAT_USED,CPL_TYPE_INT);

    }
    /* Check error status and exit */
    if(!cpl_errorstate_is_equal(prestate))
        return -1;
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Fills a table with the statistics of an image  
  @param    image_stats    Table to fill with the statistics 
  @param    image          The target image 
  @param    llx            Lower left x position (FITS convention)
  @param    lly            Lower left y position (FITS convention)
  @param    urx            Upper right x position (FITS convention)
  @param    ury            Upper right y position (FITS convention)
  @param    idet           The id of the table to store the data in 
                           (0 to HAWKI_NB_DETECTORS)
  @param    irow           The row in the table to store the stats (begins in 0)
  @return   0 if everything is ok, -1 otherwise

  This function takes the rectangle of the image given by llx, lly, urx, ury
  and computes the image statistics. This statistics are stored in the table
  image_stats, using the columns   MINIMUM, MAXIMUM, MEDIAN, MEAN, RMS, USED.
  The USED column is set to 1. The table must have already these columns
  (call hawki_image_stats_initialize before) 
  The table used to store the data is image_stats[idet], in the row irow.
  
 */
/*----------------------------------------------------------------------------*/
int hawki_image_stats_fill_from_image
(cpl_table       ** image_stats,
 const cpl_image *  image,
 int                llx,
 int                lly,
 int                urx,
 int                ury,
 int                idet,
 int                irow)
{
    /* stats variables */
    double minval;
    double maxval;
    double median;
    double stdev;
    double mean;
    cpl_stats * stats_ima ;
    
    /* Error state variables */
    cpl_errorstate  prestate = cpl_errorstate_get();

    /* Checking input */
    if(image_stats == NULL || image == NULL)
        return -1;

    /* Compute statistics */
    stats_ima = cpl_stats_new_from_image_window
        (image, CPL_STATS_ALL, llx, lly, urx, ury) ;
    if(stats_ima == NULL)
        return -1;

    /* Get the stats from the storage structure */
    minval  = cpl_stats_get_min(stats_ima);
    maxval  = cpl_stats_get_max(stats_ima);
    median  = cpl_stats_get_median(stats_ima);
    stdev   = cpl_stats_get_stdev(stats_ima);
    mean    = cpl_stats_get_mean(stats_ima);
    cpl_stats_delete(stats_ima);

    /* Store in table */
    cpl_table_set_double(image_stats[idet], HAWKI_COL_STAT_MIN,
                         irow, minval);
    cpl_table_set_double(image_stats[idet], HAWKI_COL_STAT_MAX,
                         irow, maxval);
    cpl_table_set_double(image_stats[idet], HAWKI_COL_STAT_MED,
                         irow, median);
    cpl_table_set_double(image_stats[idet], HAWKI_COL_STAT_MEAN,
                         irow, mean);
    cpl_table_set_double(image_stats[idet], HAWKI_COL_STAT_RMS,
                         irow, stdev) ;
    cpl_table_set_int(image_stats[idet], HAWKI_COL_STAT_USED,
                      irow, 1) ;

    /* Check error status and exit */
    if(!cpl_errorstate_is_equal(prestate))
        return -1;
    return 0;
}

int hawki_image_stats_odd_even_column_row_fill_from_image
(cpl_table       ** odd_column_stats,
 cpl_table       ** even_column_stats,
 cpl_table       ** odd_row_stats,
 cpl_table       ** even_row_stats,
 const cpl_image *  image,
 int                idet,
 int                irow)
{
    /* stats variables */
    int    i;
    int    j;
    int    nx;
    int    ny;
    double minval;
    double maxval;
    double median;
    double stdev;
    double mean;
    cpl_stats  * stats_ima;
    cpl_image  * tmp_ima;
    cpl_mask   * mask;

    /* Error state variables */
    cpl_errorstate  prestate = cpl_errorstate_get();

    /* Checking input */
    if(odd_column_stats == NULL  || 
       even_column_stats == NULL || 
       odd_row_stats == NULL     || 
       even_row_stats == NULL    || 
       image == NULL)
        return -1;

    /* Copying the target image */
    tmp_ima = cpl_image_duplicate(image);
    nx = cpl_image_get_size_x(tmp_ima);
    ny = cpl_image_get_size_y(tmp_ima);
    
    /* Compute statistics odd column */
    mask = cpl_image_get_bpm(tmp_ima);
    for(i=0 ; i < nx ; ++i)
    {
        if((i+1) % 2)
            for(j=0 ; j < ny ; ++j)
            {
                cpl_mask_set(mask, i + 1, j + 1, CPL_BINARY_1);
            }
    }
    stats_ima = cpl_stats_new_from_image
        (tmp_ima, CPL_STATS_ALL);
    if(stats_ima == NULL)
    {
        cpl_image_delete(tmp_ima);
        return -1;
    }

    /* Get the stats from the storage structure */
    minval  = cpl_stats_get_min(stats_ima);
    maxval  = cpl_stats_get_max(stats_ima);
    median  = cpl_stats_get_median(stats_ima);
    stdev   = cpl_stats_get_stdev(stats_ima);
    mean    = cpl_stats_get_mean(stats_ima);
    cpl_stats_delete(stats_ima);

    /* Store in table */
    cpl_table_set_double(odd_column_stats[idet], HAWKI_COL_STAT_MIN,
                         irow, minval);
    cpl_table_set_double(odd_column_stats[idet], HAWKI_COL_STAT_MAX,
                         irow, maxval);
    cpl_table_set_double(odd_column_stats[idet], HAWKI_COL_STAT_MED,
                         irow, median);
    cpl_table_set_double(odd_column_stats[idet], HAWKI_COL_STAT_MEAN,
                         irow, mean);
    cpl_table_set_double(odd_column_stats[idet], HAWKI_COL_STAT_RMS,
                         irow, stdev) ;
    cpl_table_set_int(odd_column_stats[idet], HAWKI_COL_STAT_USED,
                      irow, 1) ;
    
    /* Compute statistics even column */
    //cpl_image_reject_from_mask();
    cpl_image_accept_all(tmp_ima);
    mask = cpl_image_get_bpm(tmp_ima);
    for(i=0 ; i < nx ; ++i)
    {
        if(i % 2)
            for(j=0 ; j < ny ; ++j)
            {
                cpl_mask_set(mask, i + 1, j + 1, CPL_BINARY_1);
            }
    }
    stats_ima = cpl_stats_new_from_image
        (tmp_ima, CPL_STATS_ALL);
    if(stats_ima == NULL)
    {
        cpl_image_delete(tmp_ima);
        return -1;
    }

    /* Get the stats from the storage structure */
    minval  = cpl_stats_get_min(stats_ima);
    maxval  = cpl_stats_get_max(stats_ima);
    median  = cpl_stats_get_median(stats_ima);
    stdev   = cpl_stats_get_stdev(stats_ima);
    mean    = cpl_stats_get_mean(stats_ima);
    cpl_stats_delete(stats_ima);

    /* Store in table */
    cpl_table_set_double(even_column_stats[idet], HAWKI_COL_STAT_MIN,
                         irow, minval);
    cpl_table_set_double(even_column_stats[idet], HAWKI_COL_STAT_MAX,
                         irow, maxval);
    cpl_table_set_double(even_column_stats[idet], HAWKI_COL_STAT_MED,
                         irow, median);
    cpl_table_set_double(even_column_stats[idet], HAWKI_COL_STAT_MEAN,
                         irow, mean);
    cpl_table_set_double(even_column_stats[idet], HAWKI_COL_STAT_RMS,
                         irow, stdev) ;
    cpl_table_set_int(even_column_stats[idet], HAWKI_COL_STAT_USED,
                      irow, 1) ;

    /* Compute statistics odd rows */
    cpl_image_accept_all(tmp_ima);
    mask = cpl_image_get_bpm(tmp_ima);
    for(j=0 ; j < ny ; ++j)
    {
        if((j+1) % 2)
            for(i=0 ; i < nx ; ++i)
            {
                cpl_mask_set(mask, i + 1, j + 1, CPL_BINARY_1);
            }
    }
    stats_ima = cpl_stats_new_from_image
        (tmp_ima, CPL_STATS_ALL) ;
    if(stats_ima == NULL)
    {
        cpl_image_delete(tmp_ima);
        return -1;
    }

    /* Get the stats from the storage structure */
    minval  = cpl_stats_get_min(stats_ima);
    maxval  = cpl_stats_get_max(stats_ima);
    median  = cpl_stats_get_median(stats_ima);
    stdev   = cpl_stats_get_stdev(stats_ima);
    mean    = cpl_stats_get_mean(stats_ima);
    cpl_stats_delete(stats_ima);

    /* Store in table */
    cpl_table_set_double(odd_row_stats[idet], HAWKI_COL_STAT_MIN,
                         irow, minval);
    cpl_table_set_double(odd_row_stats[idet], HAWKI_COL_STAT_MAX,
                         irow, maxval);
    cpl_table_set_double(odd_row_stats[idet], HAWKI_COL_STAT_MED,
                         irow, median);
    cpl_table_set_double(odd_row_stats[idet], HAWKI_COL_STAT_MEAN,
                         irow, mean);
    cpl_table_set_double(odd_row_stats[idet], HAWKI_COL_STAT_RMS,
                         irow, stdev) ;
    cpl_table_set_int(odd_row_stats[idet], HAWKI_COL_STAT_USED,
                      irow, 1) ;

    /* Compute statistics even row */
    cpl_image_accept_all(tmp_ima);
    mask = cpl_image_get_bpm(tmp_ima);
    for(j=0 ; j < ny ; ++j)
    {
        if(j % 2)
            for(i=0 ; i < nx ; ++i)
            {
                cpl_mask_set(mask, i + 1, j + 1, CPL_BINARY_1);
            }
    }
    stats_ima = cpl_stats_new_from_image
        (tmp_ima, CPL_STATS_ALL) ;
    if(stats_ima == NULL)
    {
        cpl_image_delete(tmp_ima);
        return -1;
    }

    /* Get the stats from the storage structure */
    minval  = cpl_stats_get_min(stats_ima);
    maxval  = cpl_stats_get_max(stats_ima);
    median  = cpl_stats_get_median(stats_ima);
    stdev   = cpl_stats_get_stdev(stats_ima);
    mean    = cpl_stats_get_mean(stats_ima);
    cpl_stats_delete(stats_ima);

    /* Store in table */
    cpl_table_set_double(even_row_stats[idet], HAWKI_COL_STAT_MIN,
                         irow, minval);
    cpl_table_set_double(even_row_stats[idet], HAWKI_COL_STAT_MAX,
                         irow, maxval);
    cpl_table_set_double(even_row_stats[idet], HAWKI_COL_STAT_MED,
                         irow, median);
    cpl_table_set_double(even_row_stats[idet], HAWKI_COL_STAT_MEAN,
                         irow, mean);
    cpl_table_set_double(even_row_stats[idet], HAWKI_COL_STAT_RMS,
                         irow, stdev) ;
    cpl_table_set_int(even_row_stats[idet], HAWKI_COL_STAT_USED,
                      irow, 1) ;

    /* Free */
    cpl_image_delete(tmp_ima);

    /* Check error status and exit */
    if(!cpl_errorstate_is_equal(prestate))
        return -1;
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Fills a table with the statistics of the four detectors of a frame   
  @param    image_stats     Table to fill with the statistics 
  @param    frame           The target frame 
  @param    llx             Lower left x position (FITS convention)
  @param    lly             Lower left y position (FITS convention)
  @param    urx             Upper right x position (FITS convention)
  @param    ury             Upper right y position (FITS convention)
  @param    irow            The row in the table to store the stats (begin in 0)
  @return   0 if everything is ok, -1 otherwise

  This function takes the rectangle of the image given by llx, lly, urx, ury
  and computes the image statistics. This statistics are stored in the four 
  tables image_stats, one for each detector, using the columns 
  MINIMUM, MAXIMUM, MEDIAN, MEAN, RMS, USED.
  The USED column is set to 1. The table must have already these columns
  (call hawki_image_stats_initialize before) 
  The statistics are stored in the row irow.
  
 */
/*----------------------------------------------------------------------------*/
int hawki_image_stats_fill_from_frame
(cpl_table       ** image_stats,
 const cpl_frame *  frame,
 int                irow)
{
    int              idet;
    cpl_imagelist  * images;

    /* Loading the four chips */
    images = hawki_load_frame(frame, CPL_TYPE_FLOAT);
    if(images == NULL)
    {
        cpl_msg_error(__func__,"Could not read file %s",
                      cpl_frame_get_filename(frame));
        return -1;
    }
    
    for(idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
    {
        int nx, ny;
        nx = cpl_image_get_size_x(cpl_imagelist_get(images,idet));
        ny = cpl_image_get_size_y(cpl_imagelist_get(images,idet));
        hawki_image_stats_fill_from_image
            (image_stats,
             cpl_imagelist_get(images,idet),
             1, 1, nx, ny, idet, irow);
    }
    
    /* Free and exit */
    cpl_imagelist_delete(images);
    return 0;
}

int hawki_image_stats_print
(cpl_table ** table_stats)
{
    int idet;
    int istat;
    
    /* Print header */
    cpl_msg_info(__func__, "Stats summary") ;
    
    /* Loop on detectors */
    cpl_msg_indent_more();
    for( idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
    {
    
        /* Chip header */
        cpl_msg_info(__func__, "Chip number %d", idet+1) ;
        cpl_msg_info(__func__, "image      min        max        med     rms") ;
        cpl_msg_info(__func__, "--------------------------------------------") ;
        
        /* Loop on images */
        for(istat = 0; istat < cpl_table_get_nrow(table_stats[idet]); ++istat)
        {
            cpl_msg_info(__func__, "%02d   %10.2f %10.2f %10.2f %10.2f",
                         istat+1,
                         cpl_table_get_double(table_stats[idet],
                                              HAWKI_COL_STAT_MIN,istat,NULL),
                         cpl_table_get_double(table_stats[idet],
                                              HAWKI_COL_STAT_MAX,istat,NULL),
                         cpl_table_get_double(table_stats[idet],
                                              HAWKI_COL_STAT_MED,istat,NULL ),
                         cpl_table_get_double(table_stats[idet],
                                              HAWKI_COL_STAT_RMS,istat,NULL ));
        }
    }
    cpl_msg_indent_less();
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Make the statistics (over image serie) of the each image statistics   
  @param    stats_stats    Property list where to store the statistics
  @return   0 if everything is ok, -1 otherwise

  This function computes the mean, median, minimum, maximum and stdev of
  each of the columns found in the image_stats table. 
 */
/*----------------------------------------------------------------------------*/

int hawki_image_stats_stats
(cpl_table         ** image_stats,
 cpl_propertylist  ** stats_stats)
{
    cpl_array * col_names;
    int         idet;
    int         icol;

    /* Check entries */
    if(image_stats == NULL || stats_stats == NULL)
        return -1;
    
    /* Fill the name of the interesting columns */
    col_names = cpl_array_new(5, CPL_TYPE_STRING);
    cpl_array_set_string(col_names, 0, HAWKI_COL_STAT_MIN);
    cpl_array_set_string(col_names, 1, HAWKI_COL_STAT_MAX);
    cpl_array_set_string(col_names, 2, HAWKI_COL_STAT_MED);
    cpl_array_set_string(col_names, 3, HAWKI_COL_STAT_MEAN);
    cpl_array_set_string(col_names, 4, HAWKI_COL_STAT_RMS);
    
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; ++idet) 
    {
        for(icol = 0;icol < 5; ++icol)
        {
            const char * this_col_name = cpl_array_get_string(col_names, icol);
            char         mean_col_name[256] = "ESO QC RAW ";
            char         median_col_name[256] = "ESO QC RAW ";
            char         minimum_col_name[256] = "ESO QC RAW ";
            char         maximum_col_name[256] = "ESO QC RAW ";
            char         stdev_col_name[256] = "ESO QC RAW ";
            strncat(mean_col_name, this_col_name, 244);
            strncat(mean_col_name, " MEAN", 5);
            cpl_propertylist_append_double
                (stats_stats[idet], mean_col_name, 
                 cpl_table_get_column_mean(image_stats[idet],this_col_name));
            strncat(median_col_name, this_col_name, 244);
            strncat(median_col_name, " MEDIAN", 7);
            cpl_propertylist_append_double
                (stats_stats[idet], median_col_name, 
                 cpl_table_get_column_median(image_stats[idet],this_col_name));
            strncat(minimum_col_name, this_col_name, 244);
            strncat(minimum_col_name, " MINIMUM", 8);
            cpl_propertylist_append_double
                (stats_stats[idet], minimum_col_name, 
                 cpl_table_get_column_min(image_stats[idet],this_col_name));
            strncat(maximum_col_name, this_col_name, 244);
            strncat(maximum_col_name, " MAXIMUM", 8);
            cpl_propertylist_append_double
                (stats_stats[idet], maximum_col_name, 
                 cpl_table_get_column_max(image_stats[idet],this_col_name));
            strncat(stdev_col_name, this_col_name, 244);
            strncat(stdev_col_name, " STDEV", 6);
            cpl_propertylist_append_double
                (stats_stats[idet], stdev_col_name, 
                 cpl_table_get_column_stdev(image_stats[idet],this_col_name));
        }
    }
    
    /*Free and return */
    cpl_array_delete(col_names);
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get a robust estimation of the sigma based on the quartiles   
  @param    image  The image where to compute the statistic
  @return   The sigma

 */
/*----------------------------------------------------------------------------*/
double hawki_image_float_get_sigma_from_quartile(cpl_image * image)
{
    int      npixels;
    int      ipix_0_25;
    int      ipix_0_75;
    double   first_quartil;
    double   third_quartil;
    double   sigma_from_quartile;
    float  * data;
    
    npixels = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
    data = cpl_image_get_data(image);
    ipix_0_25 = (int)(npixels * 0.25);
    ipix_0_75 = (int)(npixels * 0.75);
        
    first_quartil = hawki_tools_get_kth_float(data, npixels, ipix_0_25);
    third_quartil = hawki_tools_get_kth_float(data, npixels, ipix_0_75);
    sigma_from_quartile = (third_quartil - first_quartil) / 1.35;
    return sigma_from_quartile;
}


/* Swap macro */
#undef SWAP
#define SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }

float hawki_tools_get_kth_float(float * a,
                                int        n,
                                int        k)
{
    register float x;
    register int    i, j, l, m;

    cpl_ensure(a, CPL_ERROR_NULL_INPUT, 0.00);

    l=0; m=n-1;
    while (l<m) {
        x=a[k];
        i=l;
        j=m;
        do {
            while (a[i]<x) i++;
            while (x<a[j]) j--;
            if (i<=j) {
                SWAP(a[i],a[j]);
                i++; j--;
            }
        } while (i<=j);
        if (j<k) l=i;
        if (k<i) m=j;
    }
    return a[k];
}

/**@}*/
