/* $Id: hdrl_image_math.c,v 1.1 2013-10-16 11:31:05 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:05 $
 * $Revision: 1.1 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "hdrl_elemop.h"
#include "hdrl_image.h"
#include "hdrl_collapse.h"
#include "hdrl_image_defs.h"

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

/*-----------------------------------------------------------------------------
                                   Functions
 -----------------------------------------------------------------------------*/


cpl_error_code
hdrl_image_add_image(hdrl_image * self, const hdrl_image * other)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(other, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_add_image(self->image, self->error,
                                        other->image, other->error);
}


hdrl_image *
hdrl_image_add_image_create(const hdrl_image * self, const hdrl_image * other)
{
    hdrl_image * n = hdrl_image_duplicate(self);

    if (hdrl_image_add_image(n, other)) {
        hdrl_image_delete(n);
        return NULL;
    }

    return n;
}


cpl_error_code
hdrl_image_add_scalar(hdrl_image * self,
                       double value, double error)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_add_scalar(self->image, self->error,
                                          value, error);
}


cpl_error_code
hdrl_image_sub_image(hdrl_image * self, const hdrl_image * other)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(other, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_sub_image(self->image, self->error,
                                        other->image, other->error);
}


hdrl_image *
hdrl_image_sub_image_create(const hdrl_image * self, const hdrl_image * other)
{
    hdrl_image * n = hdrl_image_duplicate(self);

    if (hdrl_image_sub_image(n, other)) {
        hdrl_image_delete(n);
        return NULL;
    }

    return n;
}


cpl_error_code
hdrl_image_sub_scalar(hdrl_image * self,
                       double value, double error)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_sub_scalar(self->image, self->error,
                                          value, error);
}


cpl_error_code
hdrl_image_mul_image(hdrl_image * self, const hdrl_image * other)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(other, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_mul_image(self->image, self->error,
                                        other->image, other->error);
}


hdrl_image *
hdrl_image_mul_image_create(const hdrl_image * self, const hdrl_image * other)
{
    hdrl_image * n = hdrl_image_duplicate(self);

    if (hdrl_image_mul_image(n, other)) {
        hdrl_image_delete(n);
        return NULL;
    }

    return n;
}


cpl_error_code
hdrl_image_mul_scalar(hdrl_image * self,
                       double value, double error)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_mul_scalar(self->image, self->error,
                                          value, error);
}


cpl_error_code
hdrl_image_div_image(hdrl_image * self, const hdrl_image * other)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(other, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_div_image(self->image, self->error,
                                        other->image, other->error);
}


hdrl_image *
hdrl_image_div_image_create(const hdrl_image * self, const hdrl_image * other)
{
    hdrl_image * n = hdrl_image_duplicate(self);

    if (hdrl_image_div_image(n, other)) {
        hdrl_image_delete(n);
        return NULL;
    }

    return n;
}


cpl_error_code
hdrl_image_div_scalar(hdrl_image * self,
                       double value, double error)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    /* size check included in elemop */
    return hdrl_elemop_image_div_scalar(self->image, self->error,
                                          value, error);
}


/* TODO copy from overscan */
static cpl_error_code
hdrl_image_collapse(hdrl_collapse_imagelist_to_vector_t * red,
                    const hdrl_image * self, double * result, double * error)
{
    cpl_imagelist * ld = cpl_imagelist_new();
    cpl_imagelist * le = cpl_imagelist_new();
    cpl_vector * od, * oe;
    cpl_array * oc;
    cpl_error_code fail;
    cpl_imagelist_set(ld, self->image, 0);
    cpl_imagelist_set(le, self->error, 0);


    fail = hdrl_collapse_imagelist_to_vector_call(red, ld, le, &od, &oe, &oc);
    cpl_imagelist_unwrap(ld);
    cpl_imagelist_unwrap(le);

    if (fail == CPL_ERROR_NONE) {
         *result = cpl_vector_get(od, 0);
         *error = cpl_vector_get(oe, 0);
    }

    cpl_vector_delete(od);
    cpl_vector_delete(oe);
    cpl_array_delete(oc);

    return fail;
}


cpl_error_code
hdrl_image_get_mean(const hdrl_image * self, double * result, double * error)
{
    hdrl_collapse_imagelist_to_vector_t * red =
        hdrl_collapse_imagelist_to_vector_mean();
    cpl_error_code err = hdrl_image_collapse(red, self, result, error);
    hdrl_collapse_imagelist_to_vector_delete(red);
    return err;
}


cpl_error_code
hdrl_image_get_median(const hdrl_image * self, double * result, double * error)
{
    hdrl_collapse_imagelist_to_vector_t * red =
        hdrl_collapse_imagelist_to_vector_median();
    cpl_error_code err = hdrl_image_collapse(red, self, result, error);
    hdrl_collapse_imagelist_to_vector_delete(red);
    return err;
}


double
hdrl_image_get_stdev(const hdrl_image * self)
{
    return cpl_image_get_stdev(self->image);
}

cpl_error_code
hdrl_image_get_sum(const hdrl_image * self, double * result, double * error)
{
    /* TODO should be collapse object */
    cpl_ensure_code(result, CPL_ERROR_NULL_INPUT);
    *result = cpl_image_get_flux(self->image);
    if (error) {
        return cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
                                     "Error of sum not supported yet");
    }
    return cpl_error_get_code();
}

cpl_error_code
hdrl_image_get_sqsum(const hdrl_image * self, double * result, double * error)
{
    /* TODO should be collapse object */
    cpl_ensure_code(result, CPL_ERROR_NULL_INPUT);
    *result = cpl_image_get_sqflux(self->image);
    if (error) {
        return cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
                                     "Error of square sum not supported yet");
    }
    return cpl_error_get_code();
}

cpl_error_code
hdrl_image_power(hdrl_image * self, const double exponent)
{
    long iexp = (long)exponent;
    if (ceil(exponent) != iexp) {
        return cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
                                     "Non integer powers not supported yet");
    }
    /* TODO should be moved to elemop */

    if (iexp == 0) {
        hdrl_image_mul_scalar(self, 0, 0);
        hdrl_image_add_scalar(self, 1, 0);
    }
    else {
        for (long i = 0; i < abs(iexp) - 1; i++) {
            /* inefficient but works as due to self correlation in elemop */
            hdrl_image_mul_image(self, self);
        }
    }

    if (iexp < 0) {
        hdrl_image * cpy = hdrl_image_duplicate(self);
        hdrl_image_mul_scalar(self, 0, 0);
        hdrl_image_add_scalar(self, 1, 0);
        hdrl_image_div_image(self, cpy);
        hdrl_image_delete(cpy);
    }

    return cpl_error_get_code();
}


hdrl_image *
hdrl_image_power_create(const hdrl_image * self, const double exponent)
{
    hdrl_image * n = hdrl_image_duplicate(self);

    if (hdrl_image_power(n, exponent)) {
        hdrl_image_delete(n);
        return NULL;
    }

    return n;
}

