/*  
** 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 "surfimage.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 SurfDecoder
/*"
 * This class is an abstract superclass for all image decoders.  An image
 * decoder need only implement those methods marked as the responsibility
 * of the subclass.
"*/

+ sharedInstance
/*"
 * Returns an instance that can be shared throughout the application.  %{It
 * is the responsibility of the subclass to implement this method!}
"*/
{
	[self subclassResponsibility:_cmd];
	return self;
}

+ (const char *const *)imageUnfilteredFileTypes;
/*"
 * Returns a NULL terminated array of types (file extensions) that an
 * instance of the decoder class can filter to TIFF.  %{It is the
 * responsibility of the subclass to implement this method!}
"*/
{
	[self subclassResponsibility:_cmd];
	return NULL;
}

+ (BOOL)canLoadFromStream:(NXStream *)stream;
/*"
 * Returns YES if an instance of the decoder class can decode the iamge
 * contained in stream. An implementation of this method should read the
 * minnimum number of bytes necessary to determine if it can decode the
 * stream.  The seek position in the stream must be preserved.  %{It is the
 * responsibility of the subclass to implement this method!}
"*/
{
	[self subclassResponsibility:_cmd];
	return NO;
}

- init
/*"
 * Designated initializer.  Disables verbose mode.
"*/
{
	_SDFlags.verboseMode = NO;
	_SDFlags.lastCorrupt = NO;
	return self;
}

- free
{
	return [super free];
}

- decodeFromFile:(const char *) filePath
/*"
 * Decode image contained in filePath.  Returns an instance of NXImage
 * containing the image data.  It is the caller's responsibility to dispose
 * of the iamge object.  Returns nil if unable to decode filePath.  Note
 * that this method may return an image if filePath is corrupt.
"*/
{
	return [self decodeFromFile:filePath intoImage:nil];
}

- decodeFromFile:(const char *) filePath intoImage: image;
/*"
 * Decode image contained in filePath into image.  Image should be an
 * instance of NXImage.  If iamge is undefined, an instance of NXImage will
 * be allocated. It is the caller's responsibility to dispose of the iamge
 * object.  Returns nil if unable to decode filePath.  Note that this
 * method may return an image if filePath is corrupt.
"*/
{
	NXStream *stream = NXMapFile(filePath, NX_READONLY);

	if(!stream)
		return nil;

	VERBOSE {
		METHOD;
		fprintf(stderr, "%s\n", filePath);
	}

	image = [self decodeFromStream: stream intoImage:image];

	NXCloseMemory(stream, NX_FREEBUFFER);

	return image;
}

- decodeFromStream:(NXStream *) stream;
/*"
 * Decode image contained in stream.  Same behaviour as #{-decodeFromFile:}
"*/
{
	return [self decodeFromStream:stream intoImage:nil];
}

- decodeFromStream:(NXStream *) stream intoImage:image;
/*"
 * Decode image contained in stream.  Same behaviour as
 * #{-decodeFromFile:intoImage:}
"*/
{
	BOOL externalImage = image ? YES : NO;

	_SDFlags.lastCorrupt = NO;
	
    /* validate stream
     */
	if(![[self class] canLoadFromStream:stream]) {
		VERBOSE {
			METHOD;
			fprintf(stderr, "+canLoadFromStream: returned NO\n");
		}
		return nil;
	} else	VERBOSE {
		METHOD;
		fprintf(stderr, "Stream validated\n");
	}
	
    /* If no image passed in, then allocate one-- assign to returnImage
       for manipulation by decoder.
     */
	if(!externalImage)
		returnImage = [[NXImage allocFromZone:[self zone]] init];
	else
		returnImage = image;

	image = [self _decodeFromStream:stream];

	VERBOSE {
		METHOD;
		fprintf(stderr, "Image was %scorrupted.\n",
				_SDFlags.lastCorrupt ? "" : "not ");
	}

    /* if decoder returned nil AND returnImage was allocated above,
       then free it.
     */
	if(!image && !externalImage) {
		[returnImage free];
		returnImage = nil;
	}
    /* regardless, reset it's state to nil.
     */
	returnImage = nil;

	return image;
}

- _decodeFromStream:(NXStream *) theStream
/*"
 * This is the method that actually performs the decoding.  This method
 * should never be called directly.
 * 
 * Upon entry, theStream has already been validated as a stream that
 * contains an image that the image decoder can [potentially] decode.  The
 * decoder should decode the image into an instance of some subclass of
 * NXImageRep that is stored in the #returnImage by using the
 * #{-useRepresentation:} method.
 * 
 * %{It is the responsibility of the subclass to implement this method!}
"*/
{
	[self subclassResponsibility:_cmd];
	return self;
}

- (void) setImageDepth:(NXWindowDepth) aDepth;
/*"
 * Sets the decoding depth (%imageDepth) to aDepth.  The decoder can use
 * %imageDepth to optimize the decoding process to the particular display
 * device.
"*/
{
	imageDepth = aDepth;
}

- (NXWindowDepth) imageDepth
/*"
 *  Returns the target decoding depth.
"*/
{
	return imageDepth;
}

- (void) setLastImageCorrupt:(BOOL) aFlag;
/*"
 * This method should %only be called by the decoder.  If is used to mark
 * the last decoded image as being corrupt.
"*/
{
	_SDFlags.lastCorrupt = aFlag;
}

- (BOOL) lastImageCorrupt
/*"
 * Returns YES if the last image decoded was corrupt.
"*/
{
	return _SDFlags.lastCorrupt;
}

- (BOOL) verboseMode;
/*"
 * Returns YES if verbose mode is enabled.  If verbose mode is enabled, the
 * image decoder should dump notes about the decoding process. For example,
 * the GIF decoder dumps information about each step of the decoding
 * process.  Useful for debugging purposes.
"*/
{
	return _SDFlags.verboseMode;
}

- (void) setVerboseMode:(BOOL) aFlag
/*"
 * Enables / disables verbose mode.
"*/
{
	_SDFlags.verboseMode = aFlag;
}

- (void) setErrorDelegate:(id <SurfErrorDelegate>) aDelegate
/*"
 * Sets the error delegate to aDelegate.  aDelegate will receive
 * notification whenever the decoder generates an error message.
"*/
{
	errorDelegate = aDelegate;
}

- errorDelegate
/*"
 * returns the error delegate.
"*/
{
	return errorDelegate;
}

- (void) spewMessage:(const char *) errMsg
		withSeverity:(SurfErrorSeverity) aSeverity
/*"
 * This method should not be overridden.  It should be invoked whenever the
 * decoder wishes to spew a message.  The message will be passed to the
 * errorDelegate via the #{-decoder:spewMessage:withSeverity:} method in
 * the #SurfErrorDelegate protocol.
"*/
{
	if(errorDelegate)
		[errorDelegate decoder:self
					   spewMessage:errMsg
					   withSeverity:aSeverity];
}
@end
