/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

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, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
///////////////////////////////////////////////////////////////////////////////
//																			 //
//	plMipmap Class Header													 //
//	Derived bitmap class representing a single mipmap.						 //
//	Cyan, Inc.																 //
//																			 //
//// Version History //////////////////////////////////////////////////////////
//																			 //
//	6.7.2001 mcn - Created.													 //
//																			 //
///////////////////////////////////////////////////////////////////////////////

#ifndef _plMipmap_h
#define _plMipmap_h

#include "plBitmap.h"

#ifdef HS_DEBUGGING
	#define ASSERT_PIXELSIZE(bitmap, pixelsize)		hsAssert((bitmap)->fPixelSize == (pixelsize), "pixelSize mismatch")
	#define ASSERT_XY(bitmap, x, y)					hsAssert(x < (bitmap)->fWidth && y < (bitmap)->fHeight, "bad XY")
	#define ASSERT_UNCOMPRESSED()					hsAssert(fCompressionType!=kDirectXCompression, "Can't operate on compressed map.")

	// Define the following konstant to enable mipmap leak checking. This is because our normal
	// memory manager sucks when trying to track down these problems
	#define MEMORY_LEAK_TRACER
#else
	#define ASSERT_PIXELSIZE(bitmap, pixelsize)
	#define ASSERT_XY(bitmap, x, y)
	#define ASSERT_UNCOMPRESSED()				
#endif

//// Class Definition /////////////////////////////////////////////////////////

class plBitmapCreator;
class plTextGenerator;

class plMipmap : public plBitmap
{
	friend class plBitmapCreator;
	friend class plTextGenerator;

	public:
		//// Public Flags ////


		//// Public Data /////
		
		
		//// Public Members ////


		plMipmap();
		plMipmap( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels = 0, UInt8 compType = kUncompressed, UInt8 format = UncompressedInfo::kRGB8888 );
		plMipmap( plMipmap *bm, hsScalar sig, UInt32 createFlags, 
							hsScalar detailDropoffStart, hsScalar detailDropoffStop, 
							hsScalar detailMax, hsScalar detailMin );
		virtual ~plMipmap();

		CLASSNAME_REGISTER( plMipmap );
		GETINTERFACE_ANY( plMipmap, plBitmap );


		void			Create( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, UInt8 compType = kUncompressed, UInt8 format = UncompressedInfo::kRGB8888 );

		virtual void	Reset();

		// Get the total size in bytes
		virtual UInt32	GetTotalSize() const;

		virtual void	Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); this->Read( s ); }
		virtual void	Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); this->Write( s ); }

		virtual UInt8	GetNumLevels() const { return fNumLevels; }
		virtual UInt32	GetLevelSize( UInt8 level );		// 0 is the largest

		virtual void		Colorize();
		virtual plMipmap	*Clone() const;
		virtual void		CopyFrom( const plMipmap *source );

		inline UInt32	GetWidth() const { return fWidth; }
		inline UInt32	GetHeight() const { return fHeight; }
		inline UInt32	GetRowBytes() const { return fRowBytes; }

		void			*GetImage() const { return fImage; }
		void			SetImagePtr( void *ptr ) { fImage = ptr; }
		UInt8			*GetLevelPtr( UInt8 level, UInt32 *width = nil, UInt32 *height = nil, UInt32 *rowBytes = nil );

		// Sets the current level pointer for use with GetAddr*
		virtual void	SetCurrLevel(UInt8 level);
		void			*GetCurrLevelPtr() const { return fCurrLevelPtr; }
		UInt32			GetCurrWidth() const { return fCurrLevelWidth; }
		UInt32			GetCurrHeight() const { return fCurrLevelHeight; }
		UInt32			GetCurrLevelSize() const { return fLevelSizes[ fCurrLevel ]; }
		UInt32			GetCurrLevel() const { return fCurrLevel; }

		//	These methods return the address of the pixel specified by x and y
		//	They are meant to be fast, therefore they are inlined and do not check
		//	the fPixelSize field at runtime (except when debugging)

		UInt8*	GetAddr8(unsigned x, unsigned y) const
				{
					ASSERT_PIXELSIZE(this, 8);
					ASSERT_XY(this, x, y);
					ASSERT_UNCOMPRESSED();
					return (UInt8*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + x);
				}
		UInt16*	GetAddr16(unsigned x, unsigned y) const
				{
					ASSERT_PIXELSIZE(this, 16);
					ASSERT_XY(this, x, y);
					ASSERT_UNCOMPRESSED();
					return (UInt16*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 1));
				}
		UInt32*	GetAddr32(unsigned x, unsigned y) const
				{
					ASSERT_PIXELSIZE(this, 32);
					ASSERT_XY(this, x, y);
					ASSERT_UNCOMPRESSED();
					return (UInt32*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 2));
				}
		void*	GetAddr64(unsigned x, unsigned y) const
				{
					ASSERT_PIXELSIZE(this, 64);
					ASSERT_XY(this, x, y);
					ASSERT_UNCOMPRESSED();
					return (void*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 3));
				}

		//	This sets fPixelSize, fSpace, fFlags, for you
		//	All you need to set is
		//		fWidth, fHeight, fRowBytes, fImage and fColorTable
		enum {
			kColor8Config = 0,
			kGray44Config = 1,
			kGray4Config = 2,
			kGray8Config = 8,		// So we can use bit depths instead
			kRGB16Config = 16,
			kRGB32Config = 24,
			kARGB32Config = 32,
		};

		void	SetConfig( unsigned config );


		//// Really complex creation stuff ////

		enum {
			kCreateDetailAlpha		= 0x1,
			kCreateDetailAdd		= 0x2,
			kCreateDetailMult		= 0x4,
			kCreateDetailMask		= kCreateDetailAlpha | kCreateDetailAdd | kCreateDetailMult,
			kCreateCarryAlpha		= 0x8,
			kCreateCarryWhite		= 0x10,
			kCreateCarryBlack		= 0x20,
			kCreateCarryMask		= kCreateCarryAlpha | kCreateCarryWhite | kCreateCarryBlack
		};
		enum hsGPixelType {
			kPixelARGB4444,
			kPixelARGB1555,
			kPixelAI88,
			kPixelI8
		};
		enum hsGCopyOptions {
			kCopyLODMask,
		};

		enum {
			kColorDataRLE	= 0x1,
			kAlphaDataRLE	= 0x2
		};

		void	SetBitmapAsLevel(UInt8 iDst, plMipmap *bm, hsScalar sig, UInt32 createFlags, 
											  hsScalar detailDropoffStart, hsScalar detailDropoffStop, 
											  hsScalar detailMax, hsScalar detailMin);
		void	ICreateLevelNoDetail(UInt8 iDst, const plFilterMask& mask);
		void	IBlendLevelDetailAlpha(UInt8 iDst, const plFilterMask& mask, 
										  hsScalar detailDropoffStart, hsScalar detailDropoffStop, 
										  hsScalar detailMax, hsScalar detailMin);
		void	IBlendLevelDetailAdd(UInt8 iDst, const plFilterMask& mask, 
										  hsScalar detailDropoffStart, hsScalar detailDropoffStop, 
										  hsScalar detailMax, hsScalar detailMin);
		void	IBlendLevelDetailMult(UInt8 iDst, const plFilterMask& mask, 
										  hsScalar detailDropoffStart, hsScalar detailDropoffStop, 
										  hsScalar detailMax, hsScalar detailMin);
		void	Filter(hsScalar sig);
		UInt32	CopyOutPixels(UInt32 destXSize, UInt32 destYSize, UInt32 dstFormat, void *destPixels, UInt32 copyOptions);

		void	ClipToMaxSize( UInt32 maxDimension );
		void	RemoveMipping();

		void	EnsureKonstantBorder( hsBool clampU, hsBool clampV );

		enum CompositeFlags
		{
			kForceOpaque		= 0x0001,		// Copy src pixels raw, force dest alphas to opaque
			kCopySrcAlpha		= 0x0002,		// Copy the src pixels raw, including alphas, overwrite dest
			kBlendSrcAlpha		= 0x0004,		// Blend src pixels onto dest using src alpha, dest alpha = src alpha
			kMaskSrcAlpha		= 0x0008,		// Same as copySrcAlpha, but dest is untouched when src alpha = 0
			kBlendWriteAlpha	= 0x0010,		// Like default (0), but writes dest alpha values

			kDestPremultiplied	= 0x0020,		// Dest has color premultiplied by alpha
												// (src always assumed nonpremultiplied for now)
		};

		class CompositeOptions
		{
			// Helper class for specifying options to Composite()
			public:
				UInt16		fFlags;
				UInt8		fSrcLevelsToSkip;
				UInt8		fOpacity;
				hsScalar	fRedTint, fGreenTint, fBlueTint;
				UInt16		fSrcClipX, fSrcClipY;			// Clipping is applied AFTER levelSkip
				UInt16		fSrcClipWidth, fSrcClipHeight;	// 0 means max width/height

				CompositeOptions() { fFlags = 0; fSrcLevelsToSkip = 0; fRedTint = fGreenTint = fBlueTint = 1.f;
									fSrcClipX = fSrcClipY = fSrcClipWidth = fSrcClipHeight = 0; fOpacity = 255;}

				CompositeOptions( UInt16 flags, UInt8 srcLevelsToSkip = 0, hsScalar red = 1.f, hsScalar green = 1.f,
									hsScalar blue = 1.f, UInt16 srcClipX = 0, UInt16 srcClipY = 0, 
									UInt16 srcClipWidth = 0, UInt16 srcClipHeight = 0, UInt8 opacity = 255 ) 
				{
					fFlags = flags;
					fSrcLevelsToSkip = srcLevelsToSkip;
					fRedTint = red;
					fGreenTint = green;
					fBlueTint = blue;
					fSrcClipX = srcClipX;
					fSrcClipY = srcClipY;
					fSrcClipWidth = srcClipWidth;
					fSrcClipHeight = srcClipHeight;
					fOpacity = opacity;
				}
		};

		// Compositing function. Take a (smaller) mipmap and composite it onto this one at the given location. Nil options means use default
		virtual void	Composite( plMipmap *source, UInt16 x, UInt16 y, CompositeOptions *options = nil );

		// Scaling function
		enum ScaleFilter
		{
			kBoxFilter,
			kDefaultFilter = kBoxFilter
		};

		virtual void	ScaleNicely( UInt32 *destPtr, UInt16 destWidth, UInt16 destHeight,
								UInt16 destStride, plMipmap::ScaleFilter filter ) const;

		virtual hsBool	ResizeNicely( UInt16 newWidth, UInt16 newHeight, plMipmap::ScaleFilter filter );

	protected:

		//// Protected Members ////

		void		*fImage;
		UInt32		fWidth, fHeight, fRowBytes, fTotalSize;
		UInt8		fNumLevels;
		UInt32		*fLevelSizes;

		void		*fCurrLevelPtr;
		UInt8		fCurrLevel;
		UInt32		fCurrLevelWidth, fCurrLevelHeight, fCurrLevelRowBytes;

		void	IReadRawImage( hsStream *stream );
		void	IWriteRawImage( hsStream *stream );
		plMipmap *ISplitAlpha();
		void	IRecombineAlpha( plMipmap *alphaChannel );
		plMipmap *IReadRLEImage( hsStream *stream );
		void	IWriteRLEImage( hsStream *stream, plMipmap *mipmap );
		void	IReadJPEGImage( hsStream *stream );
		void	IWriteJPEGImage( hsStream *stream );
		void    IReadPNGImage( hsStream *stream );
		void    IWritePNGImage( hsStream *stream );
		void	IBuildLevelSizes();

		void	IColorLevel( UInt8 level, const UInt8 *colorMask );

		hsScalar	IGetDetailLevelAlpha( UInt8 level, hsScalar dropStart, hsScalar dropStop, hsScalar min, hsScalar max );

		void		ICarryZeroAlpha(UInt8 iDst);
		void		ICarryColor(UInt8 iDst, UInt32 col);

		hsBool		IGrabBorderColor( hsBool grabVNotU, UInt32 *color );
		void		ISetCurrLevelUBorder( UInt32 color );
		void		ISetCurrLevelVBorder( UInt32 color );

		virtual UInt32 	Read( hsStream *s );
		virtual UInt32 	Write( hsStream *s );

		friend class plCubicEnvironmap;

#ifdef MEMORY_LEAK_TRACER

	protected:
	
		class plRecord
		{
			public:
				plRecord	*fNext;
				plRecord	**fBackPtr;

				char		fKeyName[ 256 ];
				void		*fImage;
				UInt32		fWidth, fHeight, fRowBytes;
				UInt8		fNumLevels;	
				UInt8		fCompressionType;
				union 
				{
					DirectXInfo			fDirectXInfo;
					UncompressedInfo	fUncompressedInfo;
				};
				enum Method
				{
					kViaCreate,
					kViaRead,
					kViaClipToMaxSize,
					kViaDetailMapConstructor,
					kViaCopyFrom,
					kViaResize
				} fCreationMethod;

				void	Link( plRecord **backPtr )
				{
					fBackPtr = backPtr;
					fNext = *backPtr;
					if( fNext != nil )
						fNext->fBackPtr = &fNext;
					*backPtr = this;
				}

				void	Unlink()
				{
					*fBackPtr = fNext;
					if( fNext != nil )
						fNext->fBackPtr = fBackPtr;
				}
		};

		static plRecord	*fRecords;
		static UInt32	fNumMipmaps;

		static void	IAddToMemRecord( plMipmap *mip, plRecord::Method method );
		static void	IRemoveFromMemRecord( UInt8 *image );
		static void	IReportLeaks();

#endif
};


#endif // _plMipmap_h