/* $Id: hdrl_image.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_elemop.h"
#include "hdrl_image.h"
#include "hdrl_image_defs.h"
#include "hdrl_utils.h"

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

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

static void _hdrl_image_delete(hdrl_image * himg);

/* ---------------------------------------------------------------------------*/
/**
 * @brief check data and its errors for consistency
 *
 * will warn if error has a different bpm than image
 */
/* ---------------------------------------------------------------------------*/
static cpl_error_code
hdrl_image_check_consistent(const cpl_image * image,
                            const cpl_image * error)
{
    cpl_ensure_code(image, CPL_ERROR_NULL_INPUT);
    if (error) {
        cpl_size inx = cpl_image_get_size_x(image);
        cpl_size iny = cpl_image_get_size_y(image);
        cpl_size enx = cpl_image_get_size_x(error);
        cpl_size eny = cpl_image_get_size_y(error);
        const cpl_mask * ibpm = cpl_image_get_bpm_const(image);
        const cpl_mask * ebpm = cpl_image_get_bpm_const(error);
        cpl_ensure_code(inx == enx, CPL_ERROR_INCOMPATIBLE_INPUT);
        cpl_ensure_code(iny == eny, CPL_ERROR_INCOMPATIBLE_INPUT);

        if (ibpm && ebpm) {
            const cpl_binary * dibpm = cpl_mask_get_data_const(ibpm);
            const cpl_binary * debpm = cpl_mask_get_data_const(ebpm);
            if (memcmp(dibpm, debpm, inx * iny) != 0) {
                cpl_msg_warning(cpl_func, "Image and error bad pixel mask "
                                "not equal, ignoring mask of error image");
            }
        }
        else if (ibpm == NULL && ebpm) {
            cpl_msg_warning(cpl_func, "Image and error bad pixel mask "
                            "not equal, ignoring mask of error image");
        }
    }

    return CPL_ERROR_NONE;
}


/* ---------------------------------------------------------------------------*/
/**
 * @internal
 * @brief create hdrl image by wrapping two cpl images without any copies
 * @note no consistency checks the two images
 * @see hdrl_image_check_consistent
 */
/* ---------------------------------------------------------------------------*/
hdrl_image * hdrl_image_wrap(cpl_image * img, cpl_image * err)
{
    hdrl_image * himg = cpl_malloc(sizeof(*himg));
    himg->image = img;
    himg->error = err;
    himg->fp_free = (hdrl_free*)&_hdrl_image_delete;

    return himg;
}


/* ---------------------------------------------------------------------------*/
/**
 * @internal
 * @brief unwrap hdrl image, will not delete wrapped cpl images
 */
/* ---------------------------------------------------------------------------*/
void hdrl_image_unwrap(hdrl_image * himg)
{
    cpl_free(himg);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief create a new hdrl_image from to existing images by copying them
 *
 * @param image  data to copy
 * @param error  errors to copy
 *
 * @return hdrl_image
 */
/* ---------------------------------------------------------------------------*/
hdrl_image * hdrl_image_create(const cpl_image * image,
                               const cpl_image * error)
{
    cpl_image * himage = NULL, * herror = NULL;

    if (hdrl_image_check_consistent(image, error)) {
        return NULL;
    }

    himage = cpl_image_cast(image, CPL_TYPE_DOUBLE);
    if (error) {
        herror = cpl_image_cast(error, CPL_TYPE_DOUBLE);
    }
    else {
        /* set error to zero */
        herror = cpl_image_duplicate(image);
        cpl_image_multiply_scalar(herror, 0.);
    }

    /* sync image and error bpm ignoring what is in error before */
    if (cpl_image_get_bpm_const(image)) {
        cpl_image_reject_from_mask(herror,
                                   cpl_image_get_bpm_const(image));
    }
    else {
        cpl_image_accept_all(herror);
    }

    return hdrl_image_wrap(himage, herror);
}

/* ---------------------------------------------------------------------------*/
/**
 * @brief create new zero filled hdrl image
 *
 * @param nx  size in x
 * @param ny  size in y
 *
 * @return hdrl_image or NULL on error
 */
/* ---------------------------------------------------------------------------*/
hdrl_image * hdrl_image_new(cpl_size nx, cpl_size ny)
{
    cpl_image * himage = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
    cpl_image * herror = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);

    if (cpl_error_get_code()) {
        cpl_image_delete(himage);
        cpl_image_delete(herror);
        return NULL;
    }

    return hdrl_image_wrap(himage, herror);
}

/* ---------------------------------------------------------------------------*/
/**
 * @brief delete hdrl_image
 */
/* ---------------------------------------------------------------------------*/
static void _hdrl_image_delete(hdrl_image * himg)
{
    if (himg) {
        cpl_image_delete(himg->image);
        cpl_image_delete(himg->error);
        cpl_free(himg);
    }
}

/* ---------------------------------------------------------------------------*/
/**
 * @brief delete hdrl_image
 * @note may be used on views in which case the original memory is kept
 */
/* ---------------------------------------------------------------------------*/
void hdrl_image_delete(hdrl_image * himg)
{
    if (himg) {
        himg->fp_free(himg);
    }
}

/* ---------------------------------------------------------------------------*/
/**
 * @brief copy hdrl_image
 */
/* ---------------------------------------------------------------------------*/
hdrl_image * hdrl_image_duplicate(const hdrl_image * himg)
{
    return hdrl_image_create(himg->image, himg->error);
}

/* ---------------------------------------------------------------------------*/
/**
 * @brief set bpm of hdrl_image
 *
 * @param self  image on which to set bpm
 * @param map   bpm to set
 * @see cpl_image_reject_from_mask
 *
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_image_reject_from_mask(hdrl_image * self,
                                           const cpl_mask * map)
{
    return cpl_image_reject_from_mask(self->image, map);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief mark pixel as bad
 *
 * @param self          image
 * @param xpos          x coordinate (FITS CONVENTION)
 * @param ypos          y coordinate (FITS CONVENTION)
 * @see cpl_image_reject
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_image_reject(hdrl_image * self,
                                 cpl_size xpos, cpl_size ypos)
{
    return cpl_image_reject(self->image, xpos, ypos);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief    Reject pixels with the specified special value(s)
 *
 * @param self   Input image to modify
 * @param mode   Bit field specifying which special value(s) to reject
 * @see cpl_image_reject_value
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_image_reject_value(hdrl_image * self, cpl_value mode)
{
    return cpl_image_reject_value(self->image, mode);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief return size of X dimension of image
 *
 * @param self  image
 * @return size of X dimension of image
 * @see cpl_image_get_size_x
 */
/* ---------------------------------------------------------------------------*/
cpl_size hdrl_image_get_size_x(const hdrl_image * self)
{
    return cpl_image_get_size_x(self->image);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief return size of Y dimension of image
 *
 * @param self  image
 * @return size of Y dimension of image
 * @see cpl_image_get_size_y
 */
/* ---------------------------------------------------------------------------*/
cpl_size hdrl_image_get_size_y(const hdrl_image * self)
{
    return cpl_image_get_size_y(self->image);
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief get pixel values of hdrl_image
 *
 * @param self          image
 * @param xpos          x coordinate (FITS CONVENTION)
 * @param ypos          y coordinate (FITS CONVENTION)
 * @param error         double pointer to store error in, may be NULL
 * @param pis_rejected  int pointer to store if pixel is bad, may be NULL
 *
 * @return data value of the pixel
 * @see cpl_image_get
 */
/* ---------------------------------------------------------------------------*/
double hdrl_image_get_pixel(const hdrl_image * self,
                            cpl_size xpos, cpl_size ypos,
                            double * error, int * pis_rejected)
{
    int d;
    double v = cpl_image_get(self->image, xpos, ypos, &d);

    /* NULL allowed, different than CPL */
    if (pis_rejected) {
        *pis_rejected = d;
    }

    if (error) {
        *error = cpl_image_get(self->error, xpos, ypos, &d);
    }

    return v;
}


/* ---------------------------------------------------------------------------*/
/**
 * @brief set pixel values of hdrl_image
 *
 * @param self          image
 * @param xpos          x coordinate (FITS CONVENTION)
 * @param ypos          y coordinate (FITS CONVENTION)
 * @param value         data value to set
 * @param error         error of value (>= 0)
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_image_set_pixel(hdrl_image * self,
                                    cpl_size xpos, cpl_size ypos,
                                    double value, double error) 
{
    cpl_ensure_code(error >= 0, CPL_ERROR_ILLEGAL_INPUT);

    if (cpl_image_set(self->image, xpos, ypos, value)) {
        return cpl_error_get_code();
    }

    return cpl_image_set(self->error, xpos, ypos, error);
}


/* ---------------------------------------------------------------------------*/
/**
 @brief extract copy of window from image
 @param self  image to extract from
 @param rect  rectangular region defining what to extract
 
 @return newly allocated hdrl_image containing the window
 @see cpl_image_extract
 @see hdrl_rect_region_parameter_create
 */
/* ---------------------------------------------------------------------------*/
hdrl_image * hdrl_image_extract(
        const hdrl_image        *   self,
        const hdrl_parameter    *   rect)
{
    cpl_ensure(rect, CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(hdrl_rect_region_parameter_verify(rect, -1, -1)==CPL_ERROR_NONE, 
            CPL_ERROR_ILLEGAL_INPUT, NULL);

    const cpl_size llx = hdrl_rect_region_get_llx(rect);
    const cpl_size lly = hdrl_rect_region_get_lly(rect);
    const cpl_size urx = hdrl_rect_region_get_urx(rect);
    const cpl_size ury = hdrl_rect_region_get_ury(rect);

    cpl_image * img = cpl_image_extract(self->image, llx, lly, urx, ury);
    cpl_image * err = cpl_image_extract(self->error, llx, lly, urx, ury);

    if (cpl_error_get_code()) {
        cpl_image_delete(img);
        cpl_image_delete(err);
        return NULL;
    }

    return hdrl_image_wrap(img, err);
}


/* ---------------------------------------------------------------------------*/
/**
  @brief    Copy one image into another
  @param    im1     the image in which im2 is inserted
  @param    im2     the inserted image
  @param    xpos    the x pixel position in im1 where the lower left pixel of
                    im2 should go (from 1 to the x size of im1)
  @param    ypos    the y pixel position in im1 where the lower left pixel of
                    im2 should go (from 1 to the y size of im1)
  @return   CPL_ERROR_NONE or the relevant #_cpl_error_code_ on error.
  @see cpl_image_copy
  @note The two pixel buffers may not overlap
 */
/* ---------------------------------------------------------------------------*/
cpl_error_code hdrl_image_copy(hdrl_image * other, const hdrl_image * self,
                               cpl_size xpos, cpl_size ypos)
{
    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(other, CPL_ERROR_NULL_INPUT);
    cpl_image_copy(other->image, self->image, xpos, ypos);
    cpl_image_copy(other->error, self->error, xpos, ypos);

    return cpl_error_get_code();
}
