/* $Id: hdrl_imagelist_basic.c,v 1.1 2013-10-16 11:31:03 cgarcia Exp $
 *
 * This file is part of the HDRL
 * Copyright (C) 2013 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  02110-1301  USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-10-16 11:31:03 $
 * $Revision: 1.1 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "hdrl_imagelist.h"
#include "hdrl_imagelist_view.h"
#include "hdrl_imagelist_defs.h"
#include "hdrl_image.h"
#include "hdrl_image_defs.h"
#include "hdrl_collapse.h"

#include <cpl.h>
#include <assert.h>

/*-----------------------------------------------------------------------------
                                   Define
 -----------------------------------------------------------------------------*/

#define HDRL_IMLIST_BASIC_IMLIST    0
#define HDRL_IMLIST_BASIC_IMAGE     1
#define HDRL_IMLIST_BASIC_SCALAR    2

/*-----------------------------------------------------------------------------
                            Static Prototypes
 -----------------------------------------------------------------------------*/
static cpl_error_code hdrl_imagelist_collapse_interface(const
        hdrl_imagelist *, hdrl_collapse_imagelist_to_image_t *,
        hdrl_image **, cpl_image **) ;

/**@{*/

/*-----------------------------------------------------------------------------
                            Function codes
 -----------------------------------------------------------------------------*/

#define HDRL_OPERATION HDRL_IMLIST_BASIC_IMLIST
/*----------------------------------------------------------------------------*/
/**
  @brief    Add two image lists, the first one is replaced by the result.
  @param    himlist1   first input image list (modified)
  @param    himlist2   image list to add
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_add_image()

  The two input lists must have the same size, the image number n in the
  list himlist2 is added to the image number n in the list himlist1.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
  - CPL_ERROR_ILLEGAL_INPUT if the input images have different sizes
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_add_imagelist(
        hdrl_imagelist        *    himlist1,
        const hdrl_imagelist  *    himlist2)
{
#define HDRL_OPERATOR hdrl_image_add_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Subtract two image lists, the first one is replaced by the result.
  @param    himlist1  first input image list (modified)
  @param    himlist2  image list to subtract
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_sub_image()
  @see      hdrl_imagelist_add_imagelist
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_sub_imagelist(
        hdrl_imagelist        *    himlist1,
        const hdrl_imagelist  *    himlist2)
{
#define HDRL_OPERATOR hdrl_image_sub_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Multiply two image lists, the first one is replaced by the result.
  @param    himlist1  first input image list (modified)
  @param    himlist2  image list to multiply
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_mul_image()
  @see      hdrl_imagelist_add_imagelist
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_mul_imagelist(
        hdrl_imagelist        *    himlist1,
        const hdrl_imagelist  *    himlist2)
{
#define HDRL_OPERATOR hdrl_image_mul_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Divide two image lists, the first one is replaced by the result.
  @param    himlist1  first input image list (modified)
  @param    himlist2  image list to divide
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_div_image()
  @see      hdrl_imagelist_add_imagelist
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_div_imagelist(
        hdrl_imagelist        *    himlist1,
        const hdrl_imagelist  *    himlist2)
{
#define HDRL_OPERATOR hdrl_image_div_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}
#undef HDRL_OPERATION

#define HDRL_OPERATION HDRL_IMLIST_BASIC_IMAGE
/*----------------------------------------------------------------------------*/
/**
  @brief    Add an image to an image list.
  @param    himlist input image list (modified)
  @param    himg    image to add
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_add_image()

  The passed image is added to each image of the passed image list.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_add_image(
        hdrl_imagelist      *    himlist,
        const hdrl_image    *    himg)
{
#define HDRL_OPERATOR hdrl_image_add_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Subtract an image from an image list.
  @param    himlist input image list (modified)
  @param    himg    image to subtract
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_sub_image()
  @see      hdrl_imagelist_add_image()

  The passed image is subtracted from each image of the passed image list.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_sub_image(
        hdrl_imagelist      *    himlist,
        const hdrl_image    *    himg)
{
#define HDRL_OPERATOR hdrl_image_sub_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Multiply an image by an image list.
  @param    himlist input image list (modified)
  @param    himg    image to multiply
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_mul_image()
  @see      hdrl_imagelist_add_image()

  The passed image is multiplied by each image of the passed image list.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_mul_image(
        hdrl_imagelist      *    himlist,
        const hdrl_image    *    himg)
{
#define HDRL_OPERATOR hdrl_image_mul_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Divide an image from an image list.
  @param    himlist input image list (modified)
  @param    himg    image to divide
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_div_image()
  @see      hdrl_imagelist_add_image()

  The passed image is used to divide each image of the passed image list.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_div_image(
        hdrl_imagelist      *    himlist,
        const hdrl_image    *    himg)
{
#define HDRL_OPERATOR hdrl_image_div_image
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}
#undef HDRL_OPERATION

#define HDRL_OPERATION HDRL_IMLIST_BASIC_SCALAR
/*----------------------------------------------------------------------------*/
/**
  @brief    Elementwise addition of a scalar to each image in the himlist
  @param    himlist Imagelist to be modified in place.
  @param    value   Value to add to the images
  @param    error   Value to add to the error images
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_add_scalar()
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_add_scalar(
        hdrl_imagelist  *   himlist,
        double              value,
        double              error)
{
#define HDRL_OPERATOR hdrl_image_add_scalar
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Elementwise subtraction of a scalar to each image in the himlist
  @param    himlist Imagelist to be modified in place.
  @param    value   Value to subtract to the images
  @param    error   Value to subtract to the error images
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_sub_scalar()
  @see      hdrl_imagelist_add_scalar()
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_sub_scalar(
        hdrl_imagelist  *   himlist,
        double              value,
        double              error)
{
#define HDRL_OPERATOR hdrl_image_sub_scalar
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Elementwise multiplication of a scalar to each image in the himlist
  @param    himlist Imagelist to be modified in place.
  @param    value   Value to multiply to the images
  @param    error   Value to multiply to the error images
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_mul_scalar()
  @see      hdrl_imagelist_add_scalar()
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_mul_scalar(
        hdrl_imagelist  *   himlist,
        double              value,
        double              error)
{
#define HDRL_OPERATOR hdrl_image_mul_scalar
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Elementwise division by a scalar to each image in the himlist
  @param    himlist Imagelist to be modified in place.
  @param    value   Value to divide to the images
  @param    error   Value to divide to the error images
  @return   the #_cpl_error_code_ or CPL_ERROR_NONE
  @see      hdrl_image_div_scalar()
  @see      hdrl_imagelist_add_scalar()
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_div_scalar(
        hdrl_imagelist  *   himlist,
        double              value,
        double              error)
{
#define HDRL_OPERATOR hdrl_image_div_scalar
#include "hdrl_imagelist_basic_body.h"
#undef HDRL_OPERATOR
}
#undef HDRL_OPERATION

/*----------------------------------------------------------------------------*/
/**
  @brief    Compute the elementwise power of each image in the himlist
  @param    himlist     Imagelist to be modified in place.
  @param    exponent    Scalar exponent
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
  @see      hdrl_image_power()
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/*----------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_power(
        hdrl_imagelist  *   himlist,
        double              exponent)
{
    cpl_size    nima ;
    cpl_size    i ;

    /* Check inputs */
    cpl_ensure_code(himlist != NULL, CPL_ERROR_NULL_INPUT);

    nima = hdrl_imagelist_get_size(himlist) ;

    for (i=0 ; i<nima ; i++) {
        hdrl_image  *   himg = hdrl_imagelist_get(himlist, i) ;
        cpl_ensure_code(!hdrl_image_power(himg, exponent), 
                cpl_error_get_code());
    }
    return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
  @brief Mean collapsing of image list
  @param himlist    input image list
  @param hout       output combined image
  @param contrib    output contribution mask
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_collapse_mean(
        const hdrl_imagelist    *   himlist,
        hdrl_image              **  out,
        cpl_image               **  contrib) 
{
    hdrl_collapse_imagelist_to_image_t * method =
        hdrl_collapse_imagelist_to_image_mean();
    hdrl_imagelist_collapse_interface(himlist, method, out, contrib) ;
    hdrl_collapse_imagelist_to_image_delete(method) ;
    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief Weighted Mean collapsing of image list
  @param himlist    input image list
  @param hout       output combined image
  @param contrib    output contribution mask
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_collapse_weighted_mean(
        const hdrl_imagelist    *   himlist,
        hdrl_image              **  out,
        cpl_image               **  contrib) 
{
    hdrl_collapse_imagelist_to_image_t * method =
        hdrl_collapse_imagelist_to_image_weighted_mean();
    hdrl_imagelist_collapse_interface(himlist, method, out, contrib) ;
    hdrl_collapse_imagelist_to_image_delete(method) ;
    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief Median collapsing of image list
  @param himlist    input image list
  @param hout       output combined image
  @param contrib    output contribution mask
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_collapse_median(
        const hdrl_imagelist    *   himlist,
        hdrl_image              **  out,
        cpl_image               **  contrib) 
{
    hdrl_collapse_imagelist_to_image_t * method =
        hdrl_collapse_imagelist_to_image_median();
    hdrl_imagelist_collapse_interface(himlist, method, out, contrib) ;
    hdrl_collapse_imagelist_to_image_delete(method) ;
    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief Sigma-clipped collapsing of image list
  @param himlist    input image list
  @param kappa_low  low sigma bound
  @param kappa_high high sigma bound
  @param niter      number of clipping iterators
  @param hout       output combined image
  @param contrib    output contribution mask
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_imagelist_collapse_sigclip(
        const hdrl_imagelist    *   himlist,
        double                      kappa_low,
        double                      kappa_high,
        int                         niter,
        hdrl_image              **  out,
        cpl_image               **  contrib) 
{
    hdrl_collapse_imagelist_to_image_t * method =
        hdrl_collapse_imagelist_to_image_sigclip(kappa_low, kappa_high, niter);
    hdrl_imagelist_collapse_interface(himlist, method, out, contrib) ;
    hdrl_collapse_imagelist_to_image_delete(method) ;
    return cpl_error_get_code();
}

/**@}*/

/*----------------------------------------------------------------------------*/
/**
  @brief Generic hdrl_imagelist_collapse іnterface to hdrl_collapse
  @param himlist    input image list
  @param niter      number of clipping iterators
  @param hout       output combined image
  @param contrib    output contribution mask
  @return   CPL_ERROR_NONE or the relevant the #_cpl_error_code_ on error
 
  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if an input pointer is NULL
 */
/* ---------------------------------------------------------------------------*/
static cpl_error_code hdrl_imagelist_collapse_interface(
        const hdrl_imagelist                *   himlist,
        hdrl_collapse_imagelist_to_image_t  *   collapse_method,
        hdrl_image                          **  out,
        cpl_image                           **  contrib) 
{
    cpl_imagelist       *   data ;
    cpl_imagelist       *   errors ;
    const hdrl_image    *   hima ;
    cpl_size                nima ;
    cpl_size                y = 1;

    /* Check inputs */
    cpl_ensure_code(himlist != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(out != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(contrib != NULL, CPL_ERROR_NULL_INPUT);

    cpl_size nz = hdrl_imagelist_get_size(himlist);
    cpl_size nx = hdrl_image_get_size_x(hdrl_imagelist_get_const(himlist, 0));
    cpl_size ny = hdrl_image_get_size_y(hdrl_imagelist_get_const(himlist, 0));
    /* process in 2MB blocks (O(L2 cache)) */
    cpl_size blocksize = 2 * (1<<20) / (nz * nx * sizeof(double));
    *out = hdrl_image_new(nx, ny);
    *contrib = cpl_image_new(nx, ny, CPL_TYPE_INT);

    hdrl_collapse_imagelist_to_image_disable_extra_out(collapse_method);
    hdrl_iter_t * it = hdrl_imagelist_get_iter_row_slices(himlist, blocksize, 0);
    for (hdrl_imagelist * v = hdrl_iter_next(it);
         v != NULL;
         v = hdrl_iter_next(it)) {
        cpl_image * out_data ;
        cpl_image * out_errors ;
        cpl_image * out_contrib ;

        /* Build the Inputs Interface */
        nima = hdrl_imagelist_get_size(v);
        data = cpl_imagelist_new();
        errors = cpl_imagelist_new() ;
        for (cpl_size i=0 ; i<nima ; i++) {
            hima = hdrl_imagelist_get_const(v, i) ;
            cpl_imagelist_set(data, hima->image, i) ;
            cpl_imagelist_set(errors, hima->error, i) ;
        }
        /* Call the actual collapsing */
        hdrl_collapse_imagelist_to_image_call(collapse_method, data, errors,
                                              &out_data, &out_errors,
                                              &out_contrib);

        /* TODO make use of output image views to avoid copying */
        cpl_image_copy((*out)->image, out_data, 1, y);
        cpl_image_copy((*out)->error, out_errors, 1, y);
        cpl_image_copy(*contrib, out_contrib, 1, y);
        y += cpl_image_get_size_y(out_data);

        /* Destroy the Inputs Interface */
        cpl_image_delete(out_data);
        cpl_image_delete(out_errors);
        cpl_image_delete(out_contrib);
        cpl_imagelist_unwrap(data) ;
        cpl_imagelist_unwrap(errors) ;
        hdrl_imagelist_delete(v);
    }

    hdrl_imagelist_iter_delete(it);

    return cpl_error_get_code();
}

/**@}*/
