// VIPS image wrapper

/*

    This file is part of VIPS.
    
    VIPS is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */

/*

    These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifndef IM_VIMAGE_H
#define IM_VIMAGE_H

/* SWIG includes this file directly rather than going through vipscpp.h ... so
 * we have to define these macros here as well.
 */
#ifdef SWIG
# define VIPS_NAMESPACE_START namespace vips {
# define VIPS_NAMESPACE_END }
#endif /*SWIG*/

/* Don't include these when parsing for SWIG.
 */
#ifndef SWIG
# include <list>
# include <complex>
# include <vector>
#endif /*!SWIG*/

/* Wrap pointers to these, but we don't want to import all the old C API. Just 
 * declare them.
 */
extern "C" {
	struct _VipsImage;

	/* Needed by Vargv, see below.
	 */
	struct im__function;
	typedef void *im__object;
}

VIPS_NAMESPACE_START

/* A VIPS callback, our name for im_callback_fn.
 */
typedef int (*VCallback)( void *, void * );

/* VIPS image class.
 *
 * Slightly tricky: we have two sorts of sharing. Several VImage can share one
 * refblock (while results are being returned from functions, for example),
 * and several other refblocks can have IMAGEs which depend upon this IMAGE
 * for their result.
 */
class VImage {
	/* We'd like this to be protected so that user subclasses can define
	 * their own member wrappers. But sadly C++ doesn't work like that:
	 * subclasses of VImage can only refer to protected members via
	 * this->, which isn't what we need. Just make it public and hope no
	 * one touches it.
	 */
public:
/* Doesn't need to be wrapped.
 */
#ifndef SWIG
	// Count ref etc. in one of these. One for each open VIPS image.
	struct refblock {
		_VipsImage *im;			// IMAGE pointer
		int close_on_delete;		// Set if we must im_close()
		int nrefs;			// Number of refs to us
		std::list<refblock*> orefs;	// Refs im makes 

		// Construct/destruct
		refblock();
		virtual ~refblock() throw( VError );

		// Add a ref - this (output image) depends upon IMAGE in
		void addref( refblock *in ) throw( VError );

		// Remove a ref
		void removeref() throw( VError );

		// Debugging
		void debug_print();

		// Linked list needs "==" -- use address equivalence
		friend int operator==( const refblock &left, 
			const refblock &right ) { return( &left == &right ); }
	};

	refblock *_ref;
#endif /*!SWIG*/

public:
#ifdef DEBUG
	/* All the refblocks in the world.
	 */
	static std::list<refblock*> all_refblock;
#endif /*DEBUG*/

	/* Print all refblocks ... debugging. Compile with DEBUG to enable
	 * this.
	 */
	static void print_all();

	/* Typedefs and enums we need.
	 */

	// Type type
	enum TType {
		MULTIBAND      = 0,
		B_W            = 1,
		LUMINACE       = 2,
		XRAY           = 3,
		IR             = 4,
		YUV            = 5,
		RED_ONLY       = 6,
		GREEN_ONLY     = 7,
		BLUE_ONLY      = 8,
		POWER_SPECTRUM = 9,
		HISTOGRAM      = 10,
		LUT            = 11,
		XYZ            = 12,
		LAB            = 13,
		CMC            = 14,
		CMYK           = 15,
		LABQ           = 16,
		RGB            = 17,
		UCS            = 18,
		LCH            = 19,
		LABS           = 21,
		sRGB	       = 22,
		YXY	       = 23,
		FOURIER	       = 24,
		RGB16	       = 25,
		GREY16	       = 26
	};

	// Format type
	enum TBandFmt {
		FMTNOTSET      = -1,
		FMTUCHAR       = 0,
		FMTCHAR        = 1,
		FMTUSHORT      = 2,
		FMTSHORT       = 3,
		FMTUINT        = 4,
		FMTINT         = 5,
		FMTFLOAT       = 6,
		FMTCOMPLEX     = 7,
		FMTDOUBLE      = 8,
		FMTDPCOMPLEX   = 9
	};

	// Coding type
	enum TCoding {
		NOCODING              = 0,
		COLQUANT              = 1,
		LABPACK               = 2,
		LABPACK_COMPRESSED    = 3,
		RGB_COMPRESSED        = 4,
		LUM_COMPRESSED        = 5,
		RAD        	      = 6
	};

	// Compression type
	enum TCompression {
		NO_COMPRESSION        = 0,
		TCSF_COMPRESSION      = 1,
		JPEG_COMPRESSION      = 2
	};

	/* Start of wrappers for iofuncs.
	 */

	// Plain constructors
	VImage( const char *name, const char *mode = "rd" ) throw( VError );
	VImage( void *data, int width, int height, 
		int bands, TBandFmt format ) throw( VError );
	VImage( _VipsImage *image );
	VImage() throw( VError );

	// Convert to a disc file, eg:
	// 	VImage fred = VImage::convert2disc( "im_jpeg2vips", 
	// 		"file.jpg", "temp.v" );
	// Runs im_jpeg2vips to the temp file, then opens that and returns
	// it. Useful for opening very large files without using a lot of RAM.
	// Now superseded by the format API, though that's not yet wrapped in
	// C++
	// Also replaced by the new default "rd" mode
	static VImage convert2disc( const char* convert, 
		const char* in, const char* disc ) throw( VError );

	// Copy constructor 
	VImage( const VImage &a );

	// Assignment - delete old ref
	VImage &operator=( const VImage &a ) throw( VError );

	// Destructor
	virtual ~VImage() throw( VError ) { _ref->removeref(); }

	// Extract underlying IMAGE* pointer
	_VipsImage *image() const { return( _ref->im ); }

	// Extract underlying data pointer
	void *data() const throw( VError );

	// Write this to another VImage, to a file, or to a mem buffer
	VImage write( VImage out ) throw( VError );
	VImage write( const char *name ) throw( VError );
	VImage write() throw( VError );

	// Debugging ... print header fields
	void debug_print();

	// Projection functions to get header fields
	int Xsize();
	int Ysize();
	int Bands();
	TBandFmt BandFmt();
	TCoding Coding();
	TType Type();
	float Xres();
	float Yres();
	int Length();
	TCompression Compression();
	short Level();
	int Xoffset();
	int Yoffset();

	// Derived fields
	const char *filename();
	const char *Hist();

	// metadata
#ifndef SWIG
	// base functionality
	// we don't wrap GValue, so we can't wrap these for now
	void meta_set( const char *field, GValue *value ) throw( VError );
	void meta_get( const char *field, GValue *value_copy ) throw( VError );
#endif /*SWIG*/

	// We can wrap these, fwiw
	gboolean meta_remove( const char *field );
	GType meta_get_typeof( const char *field );

	// convenience functions
	int meta_get_int( const char *field ) throw( VError );
	double meta_get_double( const char *field ) throw( VError );
	const char *meta_get_string( const char *field ) throw( VError );
	void *meta_get_area( const char *field ) throw( VError );
	void *meta_get_blob( const char *field, size_t *length ) 
		throw( VError );

	void meta_set( const char *field, int value ) throw( VError );
	void meta_set( const char *field, double value ) throw( VError );
	void meta_set( const char *field, const char *value ) throw( VError );

#ifndef SWIG
	// we don't wrap callbacks yet, so we can't wrap these for now
	void meta_set( const char *field, 
		VCallback free_fn, void *value ) 
		throw( VError );
	void meta_set( const char *field, 
		VCallback free_fn, void *value, size_t length ) 
		throw( VError );
#endif /*SWIG*/

	// Set header fields
	void initdesc( int, int, int, TBandFmt, TCoding, TType, 
		float = 1.0, float = 1.0, int = 0, int = 0 ) throw( VError );

	/* Insert automatically generated headers.
	 */
#include "vipsc++.h"

/* No point getting SWIG to wrap these ... we do this by hand later so we can
 * handle things like "a + 12" correctly.
 */
#ifndef SWIG
	// And some in-line operator equivalences done by hand
	friend VImage operator+( VImage a, VImage b ) throw( VError ) 
		{ return( a.add( b ) ); }
	friend VImage operator+( double a, VImage b ) throw( VError )
		{ return( b.lin( 1.0, a ) ); }
	friend VImage operator+( VImage a, double b ) throw( VError )
		{ return( a.lin( 1.0, b ) ); }

	friend VImage operator-( VImage a, VImage b ) throw( VError )
		{ return( a.subtract( b ) ); }
	friend VImage operator-( double a, VImage b ) throw( VError )
		{ return( b.lin( -1.0, a ) ); }
	friend VImage operator-( VImage a, double b ) throw( VError )
		{ return( a.lin( 1.0, -b ) ); }

	friend VImage operator*( VImage a, VImage b ) throw( VError )
		{ return( a.multiply( b ) ); }
	friend VImage operator*( double a, VImage b ) throw( VError )
		{ return( b.lin( a, 0.0 ) ); }
	friend VImage operator*( VImage a, double b ) throw( VError )
		{ return( a.lin( b, 0.0 ) ); }

	friend VImage operator/( VImage a, VImage b ) throw( VError )
		{ return( a.divide( b ) ); }
	friend VImage operator/( double a, VImage b ) throw( VError )
		{ return( b.pow( -1.0 ).lin( a, 0.0 ) ); }
	friend VImage operator/( VImage a, double b ) throw( VError )
		{ return( a.lin( 1.0/b, 0.0 ) ); }

	friend VImage operator%( VImage a, VImage b ) throw( VError )
		{ return( a.remainder( b ) ); }
	friend VImage operator%( VImage a, double b ) throw( VError )
		{ return( a.remainder( b ) ); }

	friend VImage operator<( VImage a, VImage b ) throw( VError )
		{ return( a.less( b ) ); }
	friend VImage operator<( double a, VImage b ) throw( VError )
		{ return( b.more( a ) ); }
	friend VImage operator<( VImage a, double b ) throw( VError )
		{ return( a.less( b ) ); }

	friend VImage operator<=( VImage a, VImage b ) throw( VError )
		{ return( a.lesseq( b ) ); }
	friend VImage operator<=( double a, VImage b ) throw( VError )
		{ return( b.moreeq( a ) ); }
	friend VImage operator<=( VImage a, double b ) throw( VError )
		{ return( a.lesseq( b ) ); }

	friend VImage operator>( VImage a, VImage b ) throw( VError )
		{ return( a.more( b ) ); }
	friend VImage operator>( double a, VImage b ) throw( VError )
		{ return( b.less( a ) ); }
	friend VImage operator>( VImage a, double b ) throw( VError )
		{ return( a.more( b ) ); }

	friend VImage operator>=( VImage a, VImage b ) throw( VError )
		{ return( a.moreeq( b ) ); }
	friend VImage operator>=( double a, VImage b ) throw( VError )
		{ return( b.lesseq( a ) ); }
	friend VImage operator>=( VImage a, double b ) throw( VError )
		{ return( a.moreeq( b ) ); }

	friend VImage operator==( VImage a, VImage b ) throw( VError )
		{ return( a.equal( b ) ); }
	friend VImage operator==( double a, VImage b ) throw( VError )
		{ return( b.equal( a ) ); }
	friend VImage operator==( VImage a, double b ) throw( VError )
		{ return( a.equal( b ) ); }

	friend VImage operator!=( VImage a, VImage b ) throw( VError )
		{ return( a.notequal( b ) ); }
	friend VImage operator!=( double a, VImage b ) throw( VError )
		{ return( b.notequal( a ) ); }
	friend VImage operator!=( VImage a, double b ) throw( VError )
		{ return( a.notequal( b ) ); }

	friend VImage operator&( VImage a, VImage b ) throw( VError )
		{ return( a.andimage( b ) ); }
	friend VImage operator&( int a, VImage b ) throw( VError )
		{ return( b.andimage( a ) ); }
	friend VImage operator&( VImage a, int b ) throw( VError )
		{ return( a.andimage( b ) ); }

	friend VImage operator|( VImage a, VImage b ) throw( VError )
		{ return( a.orimage( b ) ); }
	friend VImage operator|( int a, VImage b ) throw( VError )
		{ return( b.orimage( a ) ); }
	friend VImage operator|( VImage a, int b ) throw( VError )
		{ return( a.orimage( b ) ); }

	friend VImage operator^( VImage a, VImage b ) throw( VError )
		{ return( a.eorimage( b ) ); }
	friend VImage operator^( int a, VImage b ) throw( VError )
		{ return( b.eorimage( a ) ); }
	friend VImage operator^( VImage a, int b ) throw( VError )
		{ return( a.eorimage( b ) ); }

	friend VImage operator<<( VImage a, int b ) throw( VError )
		{ return( a.shiftleft( b ) ); }
	friend VImage operator>>( VImage a, int b ) throw( VError )
		{ return( a.shiftright( b ) ); }

	friend VImage operator-( VImage a ) throw( VError )
		{ return( a * -1 ); }

	// Type conversion: VImage to VDMask and VIMask
	operator VDMask() throw( VError ) 
		{ return( this->vips2mask() ); }
	operator VIMask() throw( VError ) 
		{ return( VIMask( VDMask( *this ) ) ); }
#endif /*!SWIG*/
};

/* Don't include these when parsing for SWIG.
 */
#ifndef SWIG

/* Class wrapping up a vargv. Member function wrappers need this. It needs to
 * be part of the public API in case people subclass VImage and add their own
 * members.
 */
class Vargv {
	// Function we are args to
	im__function *fn;

	// Base of object vector
	im__object *base;

public:
	Vargv( const char *name );
	~Vargv();

	// Reference to element of base
	im__object &data( int i = 0 ) { return( base[i] ); };

	// Invoke function
	void call();
};

#endif /*!SWIG*/

VIPS_NAMESPACE_END

// Other VIPS protos we need 
extern "C" {
extern int im_init_world( const char *argv0 ); 
extern void im__print_all(); 
extern void im_col_Lab2XYZ( 
	float, float, float,
	float *, float *, float * );
}

#endif /*IM_VIMAGE_H*/
