/*  
** Copyright (c) 1995 Netsurfer Inc.  All Rights Reserved.
**
** Author: <bbum@friday.com>
*/

/*  This object is included in the MiscKit by permission from the author
**  and its use is governed by the MiscKit license, found in the file
**  "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
**  for a list of all applicable permissions and restrictions.
*/

#import <appkit/appkit.h>

#import "SurfGIFSupport.h"
#import "SurfGIFDecoder.Internal.h"

#ifdef VERBOSE
#undef VERBOSE
#undef METHOD
#undef METHODnl
#endif

#define VERBOSE 		if(_SDFlags.verboseMode)
#define METHOD  		fprintf(stderr, "[%s %s%s] ", \
                                object_getClassName(self), \
                 		        (self == [self class]) ? "+" : "-", \
				                SELNAME(_cmd))
#define METHODnl  		fprintf(stderr, "[%s %s%s]\n", \
                                object_getClassName(self), \
                 		        (self == [self class]) ? "+" : "-", \
				                SELNAME(_cmd))
	
@implementation SurfGIFDecoder
/*"
 *    This class encapsulates all state necessary to decode a particular
 * GIF stream into an instance of NXImage.  The instance of NXImage may
 * contain multiple NXBitmapImageReps-- each one containing one of the
 * images decoded from the GIF stream.  This level of abstraction [a class
 * encapsulating the decoder] is necessary to fully support the features of
 * GIF in a manner compatible with NXImage and also support the move to a
 * threaded environment.
 * 
 * The GIF specification defines more than an image encapsulation schema;
 * the decoder must be able to "remember" some state between decoding
 * images.  For example, a valid GIF stream is one that contains nothing
 * but a Global Color Table -- as well, a valid GIF stream is one that
 * doesn't contain any Color Tables at all; when encountered, it
 * effectively inherits the color table used to decode the last image
 * [hence the usefulness of a stream that is nothing but a color table].
"*/

+ initialize
{
#ifdef DEBUG
	if(sizeof(SurfColorEntry32) != 4) {
		NXLogError(
				"SurfColorEntry32 NOT 4 bytes/32 bits is (%d bytes)\n",
				sizeof(SurfColorEntry32));
		exit(1);
	}
	if(sizeof(SurfColorEntry16) != 2) {
		NXLogError(
				"SurfColorEntry16 NOT 2 bytes/16 bits is (%d bytes)\n",
				sizeof(SurfColorEntry16));
		exit(1);
	}
#endif
	return self;
}

+ sharedInstance
/*"
 * This method is provided for convenience and should only be used in a
 * single-threaded conversion environment.  In a multiple threaded
 * environment, it is much safer to not use this method and to manage the
 * multiple iinstances using the normal alloc/init/free paradigm.
"*/
{
	static id sharedInstance = nil;

	if (!sharedInstance) {
		sharedInstance = [self alloc];
		[sharedInstance init];
	}
	return sharedInstance;
}

+ (const char *const *)imageUnfilteredFileTypes
/*"
 * Returns a NULL terminated array of file extensions that this decoder
 * class can decode.
"*/
{
	static const char *const gifTypes[]	= {"gif", "GIF", NULL};
	return gifTypes;
}

+ (BOOL)canLoadFromStream:(NXStream *)stream
/*"
 * Returns YES if the first few bytes of stream indicate an image that an
 * instance of this decoder can likely decode successfully.
"*/
{
	long startingPosition = NXTell(stream);
	BOOL canRead		  = NO;
	char magic[3];

    /* read the first three characters -- these should be 'GIF' for
       a valid GIF stream.  As per the specification, we ignore the
       version number;  regardless of the version, we are supposed
       to read the encapsulated data and do our best.
     */
	if(!NXRead(stream, magic, 3))
		goto resetAndReturn;

    /* First three characters should be 'GIF'
     */
	if( (magic[0] == 'G') &&
		(magic[1] == 'I') &&
		(magic[2] == 'F') )
		canRead = YES;

resetAndReturn:
    /* Reset stream back to the starting postiion.
     */
	NX_DURING
	NXSeek(stream, startingPosition, NX_FROMSTART);
	NX_HANDLER
	NXLogError("Attempted to NXSeek() a stream that cannot be NXSeek()ed.");
	canRead = NO;
	NX_ENDHANDLER
	
	return canRead;
}

- (void)printForDebugger:(NXStream *)stream
{
	[super printForDebugger:stream];
}

- init
{
	_SGIFDFlags.gctZeroUnused	  = YES;
	_SGIFDFlags.produceSilhouette = NO;

	[self _resetDecoder];
	
	decompressionState = NXZoneMalloc([self zone], sizeof(LWZState));
	
	return self;
}

- free
{
	NX_FREE(decompressionState);
	
	return [super free];
}

/* set/query
 */
- (BOOL) produceSilhouette
/*"
 * Returns YES if decoder will produce a silhouette of the image.  A
 * silhuette is transparent wherever the gif is transparent and is solid
 * white wherever the image is non-transparent.
"*/
{
	return _SGIFDFlags.produceSilhouette;
}

- (void) setProduceSilhouette:(BOOL) aFlag
/*"
 * Enables/disables silhouette mode.
 * 
 * see #{-produceSilhouette}.
"*/
{
	_SGIFDFlags.produceSilhouette = aFlag;
}

- (BOOL) gctZeroUnused
/*"
 * Returns YES if the decoder will zero all unused global color table
 * entries beyond the last entry for the current image.  Basically, this
 * ensures that if the image references an entry in the color table beyond
 * the color table size (since the global color table is large enough to
 * hold the maximum sized color table, overrunning the current global color
 * table size CANOT cause a memory exception), the resulting pixel will be
 * black.
"*/
{
	return _SGIFDFlags.gctZeroUnused;
}

- (void) setGctZeroUnused:(BOOL) aFlag
/*"
 * Enables/disables zeroing of unused global color table entries.
 * 
 * see #{-gctZeroUnused}.
"*/
{
	_SGIFDFlags.gctZeroUnused = aFlag;
}

- (void) setShowColorTable:(BOOL) aFlag
/*"
 * If enabled, each color table will be printed in ASCII form to stderr.
"*/
{
	_SGIFDFlags.showColorTable = aFlag;
}

- (BOOL) showColorTable
/*"
 * Returns YES if each decoded color table is printed to stderr.
"*/
{
	return _SGIFDFlags.showColorTable;
}
@end
