/* VIPS mask class.
 *
 * Just like VImage, but we don't need dependancy stuff. Instead, have a base
 * wrapper over *MASK, derive VMaskD and VMaskI from that, and then put
 * refcounting over all of them.
 */

/*

    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_VMASK_H
#define IM_VMASK_H

#include <stdarg.h>
#include <iostream>

// Private wrapper over *MASK - user does not see this
class VPMask {
	friend class VMask;

public:
	// Track type of mask with this
	enum VMaskType {
		UNASSIGNED,		// Not yet set
		INT,			// mask points to INTMASK
		DOUBLE			// mask points to DOUBLEMASK
	};

protected:
	void *data;			// Mask pointer - INT or DOUBLE
	VMaskType type;			// Track type too, for safety

public:
	virtual ~VPMask() {};

	// Duplicate
	virtual VPMask *dup() const = 0;

	// Projection functions to get MASK fields
	virtual int xsize() const = 0;
	virtual int ysize() const = 0;
	virtual const char *filename() const = 0;

	// Output
	virtual void print( std::ostream & ) const = 0;
	friend std::ostream &operator<<( std::ostream &file, const VPMask &msk )
		{ msk.print( file ); return file; }
};

// Specialise for INTMASK
class VPIMask : public VPMask {
public:
	VPIMask( int xsize, int ysize );
	VPIMask( int xsize, int ysize, int scale, int offset, va_list ap );
	VPIMask( const char * );
	VPIMask( void * );
	VPIMask();
	virtual ~VPIMask();

	VPMask *dup() const;
	void embed( void * );

	int xsize() const;
	int ysize() const;
	int scale() const;
	int offset() const;
	const char *filename() const;

	// Output
	virtual void print( std::ostream & ) const;

	// Extract start of array of ints
	int *array() const;
};

// Specialise for DOUBLEMASK
class VPDMask : public VPMask {
public:
	VPDMask( int xsize, int ysize );
	VPDMask( int xsize, int ysize, 
		double scale, double offset, va_list ap );
	VPDMask( const char * );
	VPDMask( void * );
	VPDMask();
	virtual ~VPDMask();

	VPMask *dup() const;
	void embed( void * );

	int xsize() const;
	int ysize() const;
	double scale() const;
	double offset() const;
	const char *filename() const;

	// Output
	virtual void print( std::ostream & ) const;

	// Extract start of array of doubles
	double *array() const;
};

// Wrapper over VP?Mask with ref counting
class VMask {
protected:
	struct refblock {
		VPMask *pmask;		// Mask: double or int
		int nrefs;		// Refs to us

		refblock() { nrefs = 1; pmask = 0; }
		virtual ~refblock() { delete pmask; }
	};

	refblock *ref;

	// Make sure this is a private copy of pmask --- dup if nrefs != 1
	void make_private();

public:
	// Constructor leaves msk uninitialised
	VMask() { ref = new refblock; }

	// Copy constructor 
	VMask( const VMask &a ) { ref = a.ref; ref->nrefs++; }

	// Assignment
	VMask &operator=( const VMask &a );

	// Destructor
	virtual ~VMask();

	int xsize() const { return( ref->pmask->xsize() ); }
	int ysize() const { return( ref->pmask->ysize() ); }
	int size() const { return( xsize() * ysize() ); }
	const char *filename() const { return( ref->pmask->filename() ); }

	// Print
	friend std::ostream &operator<<( std::ostream &file, const VMask &msk )
		{ file << *(msk.ref->pmask); return file; }

	// Extract underlying type
	VPMask::VMaskType type() const { return( ref->pmask->type ); }

	// Extract underlying VIPS pointer
	void *mask() const { return( ref->pmask->data ); }
};

// Need to forward ref these
class VDMask;
class VImage;

// Wrapper over VPIMask with ref counting
class VIMask : public VMask {
public:
	VIMask( int xsize, int ysize )
	{
		ref->pmask = new VPIMask( xsize, ysize );
	}

	VIMask( int xsize, int ysize, int scale, int offset, ... )
	{
		va_list ap;

		va_start( ap, offset );
		ref->pmask = new VPIMask( xsize, ysize, scale, offset, ap );
		va_end( ap );
	}

	VIMask( const char *name )
	{
		ref->pmask = new VPIMask( name );
	}

	// No mask there yet
	VIMask() {}

	int scale() { return( ((VPIMask *)ref->pmask)->scale() ); }
	int offset() { return( ((VPIMask *)ref->pmask)->offset() ); }

	// Embed INTMASK in VIMask
	void embed( void * );

	// Overload [] to get linear array subscript.
	int &operator[]( int );

	// Overload () to get matrix subscript.
	int &operator()( int x, int y ) 
		{ return( (*this)[x + y*xsize()] ); }

	// Type conversion: INTMASK->DOUBLEMASK
	operator VDMask();

	// Type conversion: INTMASK->image
	operator VImage();

	// VIMask build functions
	static VIMask gauss( double, double );
	static VIMask log( double, double );

	// VIMask manipulation
	VIMask rotate45();
	VIMask rotate90();

	// Arithmetic ... cast to double, and use VDMask funcs. For some
	// reason, the compiler won't let us do casts to VDImage yet, so no
	// inlines.
	VDMask trn() ;
	VDMask inv();
	VDMask cat( VDMask );
	VDMask mul( VDMask );
};

// Wrapper over VPDMask with ref counting
class VDMask : public VMask {
public:
	VDMask( int xsize, int ysize )
	{
		ref->pmask = new VPDMask( xsize, ysize );
	}

	VDMask( int xsize, int ysize, double scale, double offset, ... )
	{
		va_list ap;

		va_start( ap, offset );
		ref->pmask = new VPDMask( xsize, ysize, scale, offset, ap );
		va_end( ap );
	}

	VDMask( const char *name )
	{
		ref->pmask = new VPDMask( name );
	}

	// No mask yet
	VDMask() { }

	// Embed DOUBLEMASK in VDMask
	void embed( void * );

	double scale() { return( ((VPDMask *)ref->pmask)->scale() ); }
	double offset() { return( ((VPDMask *)ref->pmask)->offset() ); }

	// Overload [] to get linear array subscript.
	double &operator[]( int );

	// Overload () to get matrix subscript.
	double &operator()( int x, int y ) 
		{ return( (*this)[x + y*xsize()] ); }

	// Type conversion: double->int
	operator VIMask();

	// Type conversion: DOUBLEMASK->image
	operator VImage();

	// VDMask build functions
	static VDMask gauss( double, double );
	static VDMask log( double, double );

	// VDMask manipulation
	VDMask rotate45();
	VDMask rotate90(); 

	// Scale to intmask
	VIMask scalei();

	// Simple arithmetic
	VDMask trn();
	VDMask inv();
	VDMask cat( VDMask );
	VDMask mul( VDMask );
	VDMask invertlut( int );
};

#endif /*IM_VMASK_H*/
