/* 
 * $Id$
 *
 * WKTRaster - Raster Types for PostGIS
 * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
 *
 * Copyright (C) 2009  Sandro Santilli <strk@keybit.net>
 * 
 * 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 3 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
 *
 */

#ifndef RT_API_H
#define RT_API_H

#include <stdlib.h> /* For size_t */
#include <stdint.h> /* For C99 int types */

#include "liblwgeom.h"

typedef struct rt_context_t* rt_context;
typedef struct rt_raster_t* rt_raster;
typedef struct rt_band_t* rt_band;


/*- rt_context -------------------------------------------------------*/

typedef void* (*rt_allocator)(size_t size);
typedef void* (*rt_reallocator)(void *mem, size_t size);
typedef void  (*rt_deallocator)(void *mem);
typedef void  (*rt_message_handler)(const char* string, ...);

/* Initialize a context object
 * @param allocator memory allocator to use, 0 to use malloc
 * @param reallocator memory reallocator to use, 0 to use realloc
 * @param deallocator memory deallocator to use, 0 to use free
 * @return an opaque rt_context, or 0 on failure (out of memory)
 */
rt_context rt_context_new(rt_allocator allocator,
                          rt_reallocator reallocator,
                          rt_deallocator deallocator);

/* Destroy context */
void rt_context_destroy(rt_context ctx);

/* Set message handlers */
void rt_context_set_message_handlers(rt_context ctx,
                                    rt_message_handler error_handler,
                                    rt_message_handler warning_handler,
                                    rt_message_handler info_handler);

/*- rt_pixtype --------------------------------------------------------*/

/* Pixel types */
typedef enum {
    PT_1BB=0,     /* 1-bit boolean            */
    PT_2BUI=1,    /* 2-bit unsigned integer   */
    PT_4BUI=2,    /* 4-bit unsigned integer   */
    PT_8BSI=3,    /* 8-bit signed integer     */
    PT_8BUI=4,    /* 8-bit unsigned integer   */
    PT_16BSI=5,   /* 16-bit signed integer    */
    PT_16BUI=6,   /* 16-bit unsigned integer  */
    PT_32BSI=7,   /* 32-bit signed integer    */
    PT_32BUI=8,   /* 32-bit unsigned integer  */
    PT_16BF=9,    /* 16-bit float             */
    PT_32BF=10,   /* 32-bit float             */
    PT_64BF=11,   /* 64-bit float             */
    PT_END=13
} rt_pixtype;

/**
 * Return size in bytes of a value in the given pixtype
 */
int rt_pixtype_size(rt_context ctx, rt_pixtype pixtype);

/**
 * Return alignment requirements for data in the given pixel type.
 * Fast access to pixel values of this type must be aligned to as
 * many bytes as returned by this function.
 */
int rt_pixtype_alignment(rt_context ctx, rt_pixtype pixtype);

/* Return human-readable name of pixel type */
const char* rt_pixtype_name(rt_context ctx, rt_pixtype pixtype);

/*- rt_band ----------------------------------------------------------*/

/**
 * Create an in-buffer rt_band with no data
 *
 * @param ctx       : context, for thread safety
 * @param width     : number of pixel columns
 * @param height    : number of pixel rows
 * @param pixtype   : pixel type for the band
 * @param nodataval : the nodata value, will be appropriately
 *                    truncated to fit the pixtype size.
 * @param data      : pointer to actual band data, required to
 *                    be aligned accordingly to
 *                    rt_pixtype_aligment(pixtype) and big enough
 *                    to hold raster width*height values.
 *                    Data will NOT be copied, ownership is left
 *                    to caller which is responsible to keep it
 *                    allocated for the whole lifetime of the returned
 *                    rt_band.
 *
 * @return an rt_band, or 0 on failure
 */
rt_band rt_band_new_inline(rt_context ctx,
                           uint16_t width, uint16_t height,
                           rt_pixtype pixtype, double nodataval,
                           uint8_t* data);

/**
 * Create an on-disk rt_band
 *
 * @param ctx       : context, for thread safety
 * @param width     : number of pixel columns
 * @param height    : number of pixel rows
 * @param pixtype   : pixel type for the band
 * @param nodataval : the nodata value, will be appropriately
 *                    truncated to fit the pixtype size.
 * @param bandNum   : 0-based band number in the external file
 *                    to associate this band with.
 * @param path      : NULL-terminated path string pointing to the file
 *                    containing band data. The string will NOT be
 *                    copied, ownership is left to caller which is
 *                    responsible to keep it allocated for the whole
 *                    lifetime of the returned rt_band.
 *
 * @return an rt_band, or 0 on failure
 */
rt_band rt_band_new_offline(rt_context ctx, 
                            uint16_t width, uint16_t height,
                            rt_pixtype pixtype, double nodataval,
                            uint8_t bandNum, const char* path);

/**
 * Return non-zero if the given band data is on   
 * the filesystem.
 *  
 * @param ctx : context, for thread safety
 * @param band : the band 
 *
 * @return non-zero if the given band data is on   
 *         the filesystem.
 */
int rt_band_is_offline(rt_context ctx, rt_band band);

/**
 * Return bands' external path (only valid when rt_band_is_offline
 * returns non-zero).
 */
const char* rt_band_get_ext_path(rt_context ctx, rt_band band);

/**
 * Return bands' external band number (only valid when
 * rt_band_is_offline returns non-zero).
 */
uint8_t rt_band_get_ext_band_num(rt_context ctx, rt_band band);
 

/* Get pixeltype of this band */
rt_pixtype rt_band_get_pixtype(rt_context ctx, rt_band band);

/* Get width of this band */
uint16_t rt_band_get_width(rt_context ctx, rt_band band);

/* Get height of this band */
uint16_t rt_band_get_height(rt_context ctx, rt_band band);

/* Get pointer to inline raster band data
 * @@deprecate ?
 */
void* rt_band_get_data(rt_context ctx, rt_band band);

/* Destroy a raster band */
void rt_band_destroy(rt_context ctx, rt_band band);

/* Set nodata value
 * @param ctx : context, for thread safety
 * @param band : the band to set nodata value to
 * @param val : the nodata value, must be in the range
 *              of values supported by this band's pixeltype
 *              or a warning will be printed and non-zero
 *              returned.
 *
 * @return 0 on success, -1 on error (value out of valid range).
 *
 */
int rt_band_set_nodata(rt_context ctx, rt_band band, double val);

/* Get nodata value
 * @param ctx : context, for thread safety
 * @param band : the band to set nodata value to
 * @return nodata value
 */
double rt_band_get_nodata(rt_context ctx, rt_band band);

/* Set pixel value
 * @param ctx : context, for thread safety
 * @param band : the band to set nodata value to
 * @param x : x ordinate
 * @param y : x ordinate
 * @param val : the pixel value, must be in the range
 *              of values supported by this band's pixeltype
 *              or a warning will be printed and non-zero
 *              returned.
 *
 * @return 0 on success, -1 on error (value out of valid range).
 */
int rt_band_set_pixel(rt_context ctx, rt_band band,
                      uint16_t x, uint16_t y, double val);

/* Get pixel value
 *
 * @param ctx : context, for thread safety
 * @param band : the band to set nodata value to
 * @param x : x ordinate
 * @param y : x ordinate
 * @return the pixel value, as a double.
 */
double rt_band_get_pixel(rt_context ctx, rt_band band,
                         uint16_t x, uint16_t y);

/*- rt_raster --------------------------------------------------------*/

/**
 * Construct a raster with given dimensions.
 *
 * Transform will be set to identity.
 * Will contain no bands.
 * 
 * @param ctx : context, for thread safety
 * @param width : number of pixel columns
 * @param height : number of pixel rows
 *
 * @return an rt_raster or 0 if out of memory
 */
rt_raster rt_raster_new(rt_context ctx, uint16_t width, uint16_t height);

/**
 * Construct an rt_raster from a binary WKB representation
 *
 * @param ctx : context, for thread safety
 * @param wkb : an octet stream
 * @param wkbsize : size (in bytes) of the wkb octet stream
 *
 * @return an rt_raster or 0 on error (out of memory or
 *         malformed WKB).
 *
 */
rt_raster rt_raster_from_wkb(rt_context ctx, const uint8_t* wkb,
                             uint32_t wkbsize);

/**
 * Construct an rt_raster from a text HEXWKB representation
 *
 * @param ctx : context, for thread safety
 * @param hexwkb : an hex-encoded stream
 * @param hexwkbsize : size (in bytes) of the hexwkb stream
 *
 * @return an rt_raster or 0 on error (out of memory or
 *         malformed WKB).
 *
 */
rt_raster rt_raster_from_hexwkb(rt_context ctx, const char* hexwkb,
                             uint32_t hexwkbsize);

/**
 * Return this raster in WKB form
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster
 * @param wkbsize : will be set to the size of returned wkb form
 */
uint8_t *rt_raster_to_wkb(rt_context ctx, rt_raster raster,
                                    uint32_t *wkbsize);

/**
 * Return this raster in HEXWKB form (null-terminated hex)
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster
 * @param hexwkbsize : will be set to the size of returned wkb form,
 *                     not including the null termination
 */
char *rt_raster_to_hexwkb(rt_context ctx, rt_raster raster,
                                    uint32_t *hexwkbsize);

/**
 * Release memory associated to a raster
 *
 * Note that this will not release data
 * associated to the band themselves (but only
 * the one associated with the pointers pointing
 * at them).
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to destroy
 */
void rt_raster_destroy(rt_context ctx, rt_raster raster);

/* Get number of bands */
int rt_raster_get_num_bands(rt_context ctx, rt_raster raster);

/* Return Nth band, or 0 if unavailable */
rt_band rt_raster_get_band(rt_context ctx, rt_raster raster, int bandNum);

/* Get number of rows */
uint16_t rt_raster_get_width(rt_context ctx, rt_raster raster);

/* Get number of columns */
uint16_t rt_raster_get_height(rt_context ctx, rt_raster raster);

/**
 * Add band data to a raster.
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to add a band to
 * @param band : the band to add, ownership left to caller.
 *               Band dimensions are required to match with raster ones.
 *
 * @return identifier for the just-added raster, or -1 on error
 */
int32_t rt_raster_add_band(rt_context ctx, rt_raster raster, rt_band band);

/**
 * Set pixel size in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set georeference of
 * @param scaleX : pixel width in projection units
 * @param scaleY : pixel height in projection units
 *
 * NOTE: doesn't recompute offsets
 */
void rt_raster_set_pixel_sizes(rt_context ctx, rt_raster raster,
                               double scaleX, double scaleY);

/**
 * Get pixel width in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to get georeference of
 *
 * @return pixel width in projection units
 */
double rt_raster_get_pixel_width(rt_context ctx, rt_raster raster);

/**
 * Get pixel height in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to get georeference of
 *
 * @return pixel height in projection units
 */
double rt_raster_get_pixel_height(rt_context ctx, rt_raster raster);

/**
 * Set insertion points in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set georeference of
 * @param x : x ordinate of the center of upper-left pixel,
 *            in projection units
 * @param y : y ordinate of the center of upper-left pixel,
 *            in projection units
 */
void rt_raster_set_offsets(rt_context ctx, rt_raster raster,
                           double x, double y);

/**
 * Get raster x offset, in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to get georeference of
 *
 * @return  x ordinate of the center of upper-left pixel,
 *          in projection units
 */
double rt_raster_get_x_offset(rt_context ctx, rt_raster raster);

/**
 * Get raster y offset, in projection units
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to get georeference of
 *
 * @return  y ordinate of the center of upper-left pixel,
 *          in projection units
 */
double rt_raster_get_y_offset(rt_context ctx, rt_raster raster);

/**
 * Set rotations about the X and Y axis 
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set georeference of
 * @param skewX : rotation about the x axis
 * @param skewY : rotation about the y axis
 */
void rt_raster_set_rotations(rt_context ctx, rt_raster raster,
                             double skewX, double skewY);

/**
 * Get rotation about the X axis
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set georeference of
 * @return rotation about the Y axis
 */
double rt_raster_get_x_rotation(rt_context ctx, rt_raster raster);

/**
 * Get rotation about the Y axis
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set georeference of
 * @return rotation about the Y axis
 */
double rt_raster_get_y_rotation(rt_context ctx, rt_raster raster);

/**
 * Set raster's SRID 
 *
 * @param ctx : context, for thread safety
 * @param raster : the raster to set SRID of
 * @param srid : the SRID to set for the raster
 */
void rt_raster_set_srid(rt_context ctx, rt_raster raster, int32_t srid);

/**
 * Get raster's SRID 
 * @param ctx : context, for thread safety
 * @param raster : the raster to set SRID of
 *
 * @return the raster's SRID
 */
int32_t rt_raster_get_srid(rt_context ctx, rt_raster raster);

/**
 * Convert an x,y raster point to an x1,y1 point on map
 *
 * @param ctx : context for thread safety
 * @param raster : the raster to get info from
 * @param x : the pixel's column
 * @param y : the pixel's row
 * @param x1 : output parameter, X ordinate of the geographical point
 * @param y1 : output parameter, Y ordinate of the geographical point
 */
void rt_raster_cell_to_geopoint(rt_context ctx, rt_raster raster,
                                double x, double y,
                                double* x1, double* y1);

/**
 * Get raster's polygon envelope.
 *
 * The envelope is a 4 vertices (5 to be closed) single
 * ring polygon bearing the raster's rotation 
 * and using projection coordinates
 *
 * @param ctx : context for thread safety
 * @param raster : the raster to get info from
 *
 * @return the envelope, or NULL on error.
 *
 */
LWPOLY* rt_raster_get_envelope(rt_context ctx, rt_raster raster);

/**
 * Return this raster in serialized form.
 *
 * Serialized form is documented in doc/RFC1-SerializedFormat.
 *
 */
void* rt_raster_serialize(rt_context ctx, rt_raster raster);

/**
 * Return a raster from a serialized form.
 *
 * Serialized form is documented in doc/RFC1-SerializedFormat.
 *
 * NOTE: the raster will contain pointer to the serialized
 *       form, which must be kept alive.
 */
rt_raster rt_raster_deserialize(rt_context ctx, void* serialized);

#endif /* RT_API_H */
