/** @file  psImage.h
 *
 *  @brief Contains basic image definitions and operations
 *
 *  This file defines the basic type for an image struct and functions useful
 *  in manupulating images.
 *
 *  @ingroup Image
 *
 *  @author Robert DeSonia, MHPCC
 *  @author Ross Harman, MHPCC
 *
 *  @version $Revision: 1.75 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2006-04-17 22:00:03 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */
#ifndef PS_IMAGE_H
#define PS_IMAGE_H

#include <complex.h>
#include <stdio.h>
#include "psType.h"
#include "psArray.h"
#include "psConstants.h"

/// @addtogroup Image
/// @{

/** enumeration of options in interpolation
 *
 */
typedef enum {
    PS_INTERPOLATE_FLAT,               ///< 'flat' interpolation (nearest pixel)
    PS_INTERPOLATE_BILINEAR,           ///< bi-linear interpolation
    PS_INTERPOLATE_LANCZOS2,           ///< Sinc interpolation with 4x4 pixel kernel
    PS_INTERPOLATE_LANCZOS3,           ///< Sinc interpolation with 6x6 pixel kernel
    PS_INTERPOLATE_LANCZOS4,           ///< Sinc interpolation with 8x8 pixel kernel
    PS_INTERPOLATE_BILINEAR_VARIANCE,  ///< Variance version of PS_INTERPOLATE_BILINEAR
    PS_INTERPOLATE_LANCZOS2_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS2
    PS_INTERPOLATE_LANCZOS3_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS3
    PS_INTERPOLATE_LANCZOS4_VARIANCE   ///< Variance version of PS_INTERPOLATE_LANCZOS4
    //    PS_INTERPOLATE_NUM_MODES           ///< enum end-marker; does not coorespond to a interpolation mode
} psImageInterpolateMode;

/** Basic image data structure.
 *
 * Struct for maintaining image data of varying types. It also contains
 * information about image size, parent images and children images.
 *
 */
typedef struct psImage
{
    const psMathType type;             ///< Image data type and dimension.
    const int numCols;                 ///< Number of columns in image
    const int numRows;                 ///< Number of rows in image.
    int col0;                          ///< Column position relative to parent.
    int row0;                          ///< Row position relative to parent.

    union {
        psS8**  S8;                    ///< Signed 8-bit integer data.
        psS16** S16;                   ///< Signed 16-bit integer data.
        psS32** S32;                   ///< Signed 32-bit integer data.
        psS64** S64;                   ///< Signed 64-bit integer data.
        psU8**  U8;                    ///< Unsigned 8-bit integer data.
        psU16** U16;                   ///< Unsigned 16-bit integer data.
        psU32** U32;                   ///< Unsigned 32-bit integer data.
        psU64** U64;                   ///< Unsigned 64-bit integer data.
        psF32** F32;                   ///< Single-precision float data.
        psF64** F64;                   ///< Double-precision float data.
        psC32** C32;                   ///< Single-precision complex data.
        psC64** C64;                   ///< Double-precision complex data.
        psPtr*  V;                     ///< Pointer to data.
    } data;                            ///< Union for data types.
    const struct psImage* parent;      ///< Parent, if a subimage.
    psPtr p_rawDataBuffer;             ///< Raw data buffer for Allocating/Freeing Images; private
    psArray* children;                 ///< Children of this region.
    void *lock;                        ///< Optional lock for thread safety
}
psImage;

#define P_PSIMAGE_SET_NUMCOLS(img,nc) *(int*)&img->numCols = nc
        #define P_PSIMAGE_SET_NUMROWS(img,nr) *(int*)&img->numCols = nr
                #define P_PSIMAGE_SET_TYPE(img,t) *(psMathType*)&img->type = t

                        // XXX moved to psRegion.h in src/math
                        # if 0
                        /** Basic image region structure.
                         *
                         * Struct for specifying a rectangular area in an image.
                         *
                         */
                        typedef struct
                        {
                            float x0;                         ///< the first column of the region.
                            float x1;                         ///< the last column of the region.
                            float y0;                         ///< the first row of the region.
                            float y1;                         ///< the last row of the region.
                        }
                        psRegion;
# endif

/** Create an image of the specified size and type.
 *
 * Uses psLib memory allocation functions to create an image struct of the
 * specified size and type.
 *
 * @return psImage* : Pointer to psImage.
 *
 */
psImage* psImageAlloc(
    int numCols,                       ///< Number of rows in image.
    int numRows,                       ///< Number of columns in image.
    psElemType type                    ///< Type of data for image.
)
;

/** Checks the type of a particular pointer.
 *
 *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
 *
 *  @return bool:       True if the pointer matches a psImage structure, false otherwise.
 */
bool psMemCheckImage(
    psPtr ptr                          ///< the pointer whose type to check
);


// XXX these have been moved to psRegion* in src/math
# if 0
/** Create a psRegion with the specified attributes.
 *
 *  @return psRegion : a cooresponding psRegion.
 */
psRegion psRegionSet(
    float x0,                          ///< the first column of the region.
    float x1,                          ///< the last column of the region + 1.
    float y0,                          ///< the first row of the region.
    float y1                           ///< the last row of the region + 1.
);

/** Create a psRegion with the attribute values given as a string.
 *
 *  Create a psRegion with the attribute values given as a string.  The format
 *  shall be of the standard IRAF form '[x0:x1,y0:y1]'
 *
 *  @return psRegion:  A new psRegion struct, or NULL is not successful.
 */
psRegion psRegionFromString(
    const char* region                 ///< image rectangular region in the form '[x0:x1,y0:y1]'
);

/** Create a string of the standard IRAF form '[x0:x1,y0:y1]' from a psRegion.
 *
 *  @return psString:  A new string representing the psRegion as text, or NULL
 *                  is not successful.
 */
psString psRegionToString(
    const psRegion region              ///< the psRegion to convert to a string
);

/** Sets an actual region based on image parameters.
 *
 *  An image region defined with negative upper limits may be rationalized for the bounds of a
 *  specific image with psRegionForImage.  The output of this function is a region with negative
 *  upper limits replaced by their corrected value appropriate to the given image.  In addition,
 *  the lower and upper limits are foced to lie within the bounds of the image.  If the lower-
 *  limit coordinates are lewss than the lower bound of the image, they are limited to the lower
 *  bound of the image.  Conversely, if the upper-limit coordinates are greater than the upper
 *  bound of the image, they are truncated to define only valid pixels.  If the lower-limit
 *  coordinates are greater than the upper bounds of the image, or the upper-limit coordinates
 *  are less than the lower bounds of the image, the coordinates should saturate on those limits.
 *
 *  @return psRegion:       A region with negative upper limits replaced by the corrected
 */
psRegion psRegionForImage(
    psImage *image,                    ///< the image for which the region is to be set
    psRegion in                        ///< the image region limits
);

/** Defines a region corresponding to the square with center at coordinate x,y
 *  and with coderadius.  The width of the square is 2radius + 1.
 *
 *  @return psRegion:       the newly defined psRegion.
 */
psRegion psRegionForSquare(
    double x,                           ///< x coordinate at square-center
    double y,                           ///< y coordinate at square-center
    double radius                       ///< radius of square
);
# endif /* commented-out psRegions */

/** Initializes the image with the given value.
 *
 *  The input data is cast to match the image datatype.
 *
 *  @return bool:       True on success, otherwise false.
 */
bool psImageInit(
    psImage *image,                    ///< the image to be initialized
    ...                                ///< Variable argument list for initialization
);

/** Sets the value of the image at the specified x,y position to value.
 *
 *  A negative value for the x or y positions means index from the end.
 *
 *  @return bool:       True on success, otherwise false.
 */
bool psImageSet(
    const psImage *image,              ///< the image to set
    int x,                             ///< x-position
    int y,                             ///< y-position
    double complex value               ///< specified value to set
);

/** Returns the value of the image at the specified x,y position.
 *
 *  A negative value for the x or y positions means index from the end.
 *
 *  @return complex: The value at the specified x,y position.
 */
double complex psImageGet(
    const psImage *image,              ///< the image from which to get
    int x,                             ///< x-position
    int y                              ///< y-position
);

/** Resize a given image to the given size/type.
 *
 *  @return psImage* Resized psImage.
 */
psImage* psImageRecycle(
    psImage* old,                      ///< the psImage to recycle by resizing image buffer
    int numCols,                       ///< the desired number of columns in image
    int numRows,                       ///< the desired number of rows in image
    const psElemType type              ///< the desired datatype of the image
);

/** Copy an image to a new buffer
 *
 *  @return True if image copied or false if error
 */
bool p_psImageCopyToRawBuffer(
    void* buffer,                      ///< the buffer used to copy the image
    const psImage* input,              ///< the input image to be copied
    psElemType type                    ///< the datatype of the image to be copied
);

/** Frees all children of a psImage.
 *
 *  @return int      Number of children freed.
 */
int psImageFreeChildren(
    psImage* image                     ///< psImage in which all children shall be deallocated
);

/** get an element of an image as a psF64.
 *
 *  @return psF64   pixel value at specified location
 */
psF64 p_psImageGetElementF64(
    psImage* image,                    ///< input image
    int col,                           ///< pixel column
    int row                            ///< pixel row
);

/** print image pixel values.
 *
 *  @return bool    TRUE is successful, otherwise FALSE.
 */
bool p_psImagePrint(
    int fd,                            ///< Destination file descriptor
    psImage *a,                        ///< image to print
    char *name                         ///< name of the image (for title)
);

/** Interpolate image pixel value given floating point coordinates.
 *
 *  @return complex    Pixel value interpolated from image or unexposedValue if
 *                   given x,y doesn't coorespond to a valid image location
 */
double complex psImagePixelInterpolate(
    const psImage* input,              ///< input image for interpolation
    float x,                           ///< column location to derive value of
    float y,                           ///< row location ot derive value of
    const psImage* mask,               ///< if not NULL, the mask of the input image
    psMaskType maskVal,                ///< the mask value
    double complex unexposedValue,            ///< return value if x,y location is not in image.
    psImageInterpolateMode mode        ///< interpolation mode
);

#define PIXEL_INTERPOLATE_FCN_PROTOTYPE(SUFFIX, RETURNTYPE) \
inline RETURNTYPE p_psImagePixelInterpolate##SUFFIX( \
        const psImage* input,          /**< input image for interpolation */ \
        float x,                       /**< column location to derive value of */ \
        float y,                       /**< row location ot derive value of */ \
        const psImage* mask,           /**< if not NULL, the mask of the input image */ \
        psU32 maskVal,                 /**< the mask value */ \
        RETURNTYPE unexposedValue      /**< return value if x,y location is not in image. */ \
                                                   );

#define PIXEL_INTERPOLATE_FCNS(MODE) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U8,psF64)  \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U16,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U32,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U64,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S8,psF64)  \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S16,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S32,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S64,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F32,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F64,psF64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C32,psC64) \
PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C64,psC64)

#ifndef SWIG
PIXEL_INTERPOLATE_FCNS(FLAT)
PIXEL_INTERPOLATE_FCNS(BILINEAR)
PIXEL_INTERPOLATE_FCNS(BILINEAR_VARIANCE)
#endif // ! SWIG

#undef PIXEL_INTERPOLATE_FCN_PROTOTYPE
#undef PIXEL_INTERPOLATE_FCNS

/// @}

#endif // PS_IMAGE_H
