//  Created by Allen Ingling on Wed Mar 31 2004.
//  Copyright (c) 2004 New York University. All rights reserved.



#import "HipsHelpers.h"
#import "HipsMovie.h"
		

@implementation HipsMovie

- init
{
    if (self = [super init]) {
		numFrames=0;
		targetPort=0;
		fps=0;
		currentScaleMode=khpsNoneSpecified;
		isPixelMaxMinValid=FALSE;
		targetMode=khpsNoTarget;
		//textureMode=khpsNoTextureRangeAndNoMemcpy;
		textureMode=khpsTextureRangeAndMemcpy;
		useProgressBars=FALSE;
		progressWindow=nil;
		progressBar=nil;
		progressText=nil;
		scaleL=1;
		offsetL=0;
		isMeanPixelLValid=FALSE;
		isMeanEdgePixelLValid=FALSE;
		//isCGTargetPortValid=FALSE;
		//dynamic memory.
		fullFileName=NULL;
		headerString=NULL;
		floatMovieMemory=NULL;
		textureFramesMemory8=NULL;
		textureFramesMemory32=NULL;
		textureBlitBuffer8=NULL;
		textureBlitBuffer32=NULL;
		textureNumber8=1;
		textureNumber32=2;
	}
    return(self);
}



  - (void)blitFrame32:(int)frameNumber
{
	unsigned long		frameSizeBytes;
	void*				frame;
//	NSRect				fooRect;
	// texture mode options:
	//  khpsTextureRangeAndMemcpy, khpsTextureRangeAndNoMemcpy, khpsNoTextureRangeAndNoMemcpy

//	printf("Frame Number:%d\n",frameNumber);
	
	//copy the current frame into the accelerated texture memory area.
	frameSizeBytes= 4 * movieRect.size.width * movieRect.size.height;
	frame=(void*)textureFramesMemory32 + frameSizeBytes*frameNumber;
	
	//fooRect=targetRect;
	//fooRect=movieRect;
	
	if(textureMode==khpsTextureRangeAndMemcpy)
		memcpy(textureBlitBuffer32, frame, frameSizeBytes);
	
	glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1);
	if(textureMode==khpsTextureRangeAndMemcpy  || textureMode==khpsTextureRangeAndNoMemcpy)
		glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, movieRect.size.width, movieRect.size.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureBlitBuffer32);
	else if(textureMode==khpsNoTextureRangeAndNoMemcpy)
		glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, movieRect.size.width, movieRect.size.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, frame);
	glBegin(GL_QUADS);
		//lower left
		glTexCoord2f(0.0f, 0.0f);
		glVertex2f(targetRect.origin.x, targetRect.origin.y + targetRect.size.height);
		
		//upper left
		glTexCoord2f(0.0f, targetRect.size.height);
		glVertex2f(targetRect.origin.x, targetRect.origin.y);
		
		//upper right
		glTexCoord2f(targetRect.size.width, targetRect.size.height);
		glVertex2f(targetRect.origin.x + targetRect.size.width, targetRect.origin.y );
		
		//lower right
		glTexCoord2f(targetRect.size.width, 0.0f);
		glVertex2f(targetRect.origin.x + targetRect.size.width, targetRect.origin.y + targetRect.size.height);
	glEnd();
	[openglContext flushBuffer];
}


- (void)blitFrame8:(int)frameNumber
{
	unsigned long		frameSizeBytes;
	void*				frame;
	
	// texture mode options:
	//  khpsTextureRangeAndMemcpy, khpsTextureRangeAndNoMemcpy, khpsNoTextureRangeAndNoMemcpy


	//copy the current frame into the accelerated texture memory area.
	frameSizeBytes= movieRect.size.width * movieRect.size.height;
	frame=(void*)textureFramesMemory8 + frameSizeBytes*frameNumber;
	
	if(textureMode==khpsTextureRangeAndMemcpy)
		memcpy(textureBlitBuffer8, frame, frameSizeBytes);
	
	glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1);
	if(textureMode==khpsTextureRangeAndMemcpy  || textureMode==khpsTextureRangeAndNoMemcpy)
		glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_LUMINANCE8, movieRect.size.width, movieRect.size.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, textureBlitBuffer8);
	else if(textureMode==khpsNoTextureRangeAndNoMemcpy)
		glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_LUMINANCE8, movieRect.size.width, movieRect.size.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame);
	glBegin(GL_QUADS);
		//lower left
		glTexCoord2f(0.0f, 0.0f);
		glVertex2f(targetRect.origin.x, targetRect.origin.y + targetRect.size.height);
		
		//upper left
		glTexCoord2f(0.0f, targetRect.size.height);
		glVertex2f(targetRect.origin.x, targetRect.origin.y);
		
		//upper right
		glTexCoord2f(targetRect.size.width, targetRect.size.height);
		glVertex2f(targetRect.origin.x + targetRect.size.width, targetRect.origin.y );
		
		//lower right
		glTexCoord2f(targetRect.size.width, 0.0f);
		glVertex2f(targetRect.origin.x + targetRect.size.width, targetRect.origin.y + targetRect.size.height);
	glEnd();
	[openglContext flushBuffer];

}


- (void)waitForBlank
{

	[openglContext flushBuffer];
}
 
 
 - (void)resetClampRule:(ScaleModeType)ScaleMode
 {
	NSAssert(floatMovieMemory!=NULL, @"Attempt to change clamping rule without film.");
	[self load32BitTexturesFromHipsFloatFrames:ScaleMode];
 }
 
 
 
 - (void)loadFloatFramesBufferFromHipsFile:(NSString*)fileName
 {
 	const UTF8Char		*fileNameUTF8;
	FILE				*fp;
	char				*headerCString;
	struct header		hipsOrigHeader, hipsNewHeader;
	int					hipsImageTypes[]={PFFLOAT, LASTTYPE};
	int					hipsFormatConversionMethod;
	float				frameSizePixels, *currentFloatFrame;
	unsigned long		floatFrameSizeBytes, totalFloatFramesMemorySizeBytes;
	int					i;



	//open the file
	NSAssert(floatMovieMemory==NULL, @"Attempt to reload file file without previous unload.");	
 	[self setFullFileName:fileName];
	fileNameUTF8=[fileName UTF8String];
	fp = hfopenr(fileNameUTF8);  //add error checking here if we fail to open the file
	//read the header and store in in an instance variable
	fread_hdr_a(fp, &hipsOrigHeader, fileNameUTF8);
	headerCString = formatheader(&hipsOrigHeader);   //how do we deallocate headerStringC ? does free_hdrcon do this ?
	headerString=[[NSString alloc] initWithCString:headerCString];
	//load paramters from HIPS header into instance variables.
	hipsFormatConversionMethod=fset_conversion(&hipsOrigHeader, &hipsNewHeader, hipsImageTypes, fileNameUTF8);
	numColors=hipsNewHeader.numcolor;
	NSAssert(numColors==1 || numColors==3, @"Movie header reports and unexpected number of colors");
	if(numColors==3)
		isMovieColor=TRUE;
	else
		isMovieColor=FALSE;
	numFrames=hipsNewHeader.num_frame;
	movieRect=NSMakeRect(0, 0, hipsNewHeader.cols, hipsNewHeader.rows);
	frameSizePixels= movieRect.size.width * movieRect.size.height;
	
	//setup the progress indicator
	if(useProgressBars)
		[self activateProgressBar:khpsLoadingFile];

	//copy the movie into our own buffer with float resolution.  From this we generate GWorld frames according to scaling rules.
	floatFrameSizeBytes= frameSizePixels * sizeof(float);
	totalFloatFramesMemorySizeBytes= numFrames * floatFrameSizeBytes;
	floatMovieMemory=(float*)(malloc(totalFloatFramesMemorySizeBytes));
	NSAssert(floatMovieMemory !=NULL, @"Faild to allocate memory for floating-point image array");
	for(i=0;i<numFrames;i++){
		fread_imagec(fp, &hipsOrigHeader, &hipsNewHeader, hipsFormatConversionMethod, i, fileNameUTF8);
		currentFloatFrame = floatMovieMemory + i * (int)frameSizePixels;
		memcpy(currentFloatFrame, hipsNewHeader.firstpix, floatFrameSizeBytes);
		if(useProgressBars){
			[progressBar setDoubleValue:(double)i+1];
			[progressBar displayIfNeeded];
		}	
	}
	free_hdrcon(&hipsNewHeader);
	
	if(useProgressBars)
		[self activateProgressBar:khpsProgressComplete];
 }
 
		

 - (void)load32BitTexturesFromHipsFloatFrames:(ScaleModeType)ScaleMode
 {
 
	unsigned long   i, numTotalMoviePixels, textureRowSizeBytes, textureFrameSizeBytes, movieSizeBytes;
	double			guageStepSizePixels, guageStepAccumulator;

	//calculate scaling and offset values according to gui settings and original movie luminances
	numTotalMoviePixels=movieRect.size.width * movieRect.size.height * numFrames;
	if(!isPixelMaxMinValid){
		numTotalMoviePixels=movieRect.size.width * movieRect.size.height * numFrames;
		FindMovieLuminanceBounds(floatMovieMemory, numTotalMoviePixels, &maxMoviePixel, &minMoviePixel);
	}
	if(ScaleMode!=currentScaleMode){
		FindScalerAndOffsetFromPixelRangeAndScalingMode(maxMoviePixel, minMoviePixel, ScaleMode, &scaleL, &offsetL);
		currentScaleMode=ScaleMode;
	}
	
	//allocate memory to store texture
	if(textureFramesMemory32==NULL){
		textureRowSizeBytes=sizeof(GLuint) * movieRect.size.width;
		textureFrameSizeBytes=textureRowSizeBytes * movieRect.size.height;
		movieSizeBytes=textureFrameSizeBytes * numFrames;
		textureFramesMemory32=(UInt32*)malloc(movieSizeBytes);
	}
	
	//setup the progress indicator
	if(useProgressBars)
		[self activateProgressBar:khpsMakingTextures];

	//copy reformated and scaled float pixels into texture memory
	guageStepSizePixels=movieRect.size.width * movieRect.size.height;	 
	guageStepAccumulator=guageStepSizePixels;
	for(i=0;i<numTotalMoviePixels;i++){
		PackFloatColorMonoIntoInt(floatMovieMemory[i], (GLuint*)(textureFramesMemory32+i), scaleL, offsetL);
		if(useProgressBars){
			if(i+1 == guageStepAccumulator){
				[progressBar incrementBy:(double)1];
				[progressBar displayIfNeeded];
				guageStepAccumulator=guageStepAccumulator+guageStepSizePixels;
			}
		}	
	}
	
	if(useProgressBars)
		[self activateProgressBar:khpsProgressComplete];
	
}


 - (void)load8BitTexturesFromHipsFloatFrames:(ScaleModeType)ScaleMode
 {
	unsigned long   i, numTotalMoviePixels, textureRowSizeBytes, textureFrameSizeBytes, movieSizeBytes;
	double			guageStepSizePixels, guageStepAccumulator;

	//calculate scaling and offset values according to gui settings and original movie luminances
	numTotalMoviePixels=movieRect.size.width * movieRect.size.height * numFrames;
	if(!isPixelMaxMinValid){
		numTotalMoviePixels=movieRect.size.width * movieRect.size.height * numFrames;
		FindMovieLuminanceBounds(floatMovieMemory, numTotalMoviePixels, &maxMoviePixel, &minMoviePixel);
	}
	if(ScaleMode!=currentScaleMode){
		FindScalerAndOffsetFromPixelRangeAndScalingMode(maxMoviePixel, minMoviePixel, ScaleMode, &scaleL, &offsetL);
		currentScaleMode=ScaleMode;
	}
	
	//allocate memory to store texture
	if(textureFramesMemory8==NULL){
		textureRowSizeBytes=sizeof(GLubyte) * movieRect.size.width;
		textureFrameSizeBytes=textureRowSizeBytes * movieRect.size.height;
		movieSizeBytes=textureFrameSizeBytes * numFrames;
		textureFramesMemory8=(void*)malloc(movieSizeBytes);
	}
	
	//setup the progress indicator
	if(useProgressBars)
		[self activateProgressBar:khpsMakingTextures];

	//copy reformated and scaled float pixels into texture memory
	guageStepSizePixels=movieRect.size.width * movieRect.size.height;	 
	guageStepAccumulator=guageStepSizePixels;
	for(i=0;i<numTotalMoviePixels;i++){
		PackFloatColorMonoIntoByte(floatMovieMemory[i], (GLubyte*)(((GLubyte*)textureFramesMemory8)+i), scaleL, offsetL);
		if(useProgressBars && i+1 == guageStepAccumulator){
				[progressBar incrementBy:(double)1];
				[progressBar displayIfNeeded];
				guageStepAccumulator=guageStepAccumulator+guageStepSizePixels;
		}	
	}
	if(useProgressBars)
		[self activateProgressBar:khpsProgressComplete];
}



- (void)instantiate32BitTexturesIntoNSGLContext:(NSOpenGLContext*)context frameSize:(NSRect)contextRect
{
	GLenum				textureHint;
	unsigned long		tempTextureMemorySizeBytes;
	
	
	//housekeeping
	NSAssert(floatMovieMemory != NULL, @"Attempt to set display target for unloaded movie.");
	NSAssert(textureFramesMemory32 != NULL, @"Attempt to set display target for a movie without textures.");
	openglContext=context;
	openglContextRect=contextRect;
	targetRect=CenterNSRectInNSRect(movieRect, openglContextRect);

	//choose the texture accleration extension
	textureHint= GL_STORAGE_SHARED_APPLE;  //GL_STORAGE_PRIVATE_APPLE, GL_STORAGE_CACHED_APPLE
	
	//create a surface from which we can blit textures quickly
	tempTextureMemorySizeBytes= movieRect.size.width * movieRect.size.height * 4;
	textureBlitBuffer32 = malloc(tempTextureMemorySizeBytes);


	//setup texturing
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_TEXTURE_RECTANGLE_EXT);
	glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1);
	
//  khpsTextureRangeAndMemcpy, khpsTextureRangeAndNoMemcpy, khpsNoTextureRangeAndNoMemcpy
	if(textureMode==khpsTextureRangeAndMemcpy || textureMode==khpsTextureRangeAndNoMemcpy)				
		glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, movieRect.size.width * movieRect.size.height * 4, textureBlitBuffer32);
	else if(textureMode==khpsNoTextureRangeAndNoMemcpy)
		glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 0, NULL);

	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , textureHint); 
	glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
			
	glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, movieRect.size.width, movieRect.size.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureBlitBuffer32);
	
}


- (void)instantiate8BitTexturesIntoNSGLContext:(NSOpenGLContext*)context frameSize:(NSRect)contextRect;
{
	GLenum				textureHint;
	unsigned long		tempTextureMemorySizeBytes;
	
	//housekeeping
	NSAssert(floatMovieMemory != NULL, @"Attempt to set display target for unloaded movie.");
	NSAssert(textureFramesMemory8 != NULL, @"Attempt to set display target for a movie without textures.");
	openglContext=context;
	openglContextRect=contextRect;
	targetRect=CenterNSRectInNSRect(movieRect, openglContextRect);

	//choose the texture accleration extension
	textureHint= GL_STORAGE_SHARED_APPLE;  //GL_STORAGE_PRIVATE_APPLE, GL_STORAGE_CACHED_APPLE
	
	//create a surface from which we can blit textures quickly
	tempTextureMemorySizeBytes= movieRect.size.width * movieRect.size.height;
	textureBlitBuffer8 = malloc(tempTextureMemorySizeBytes);

	//setup texturing
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_TEXTURE_RECTANGLE_EXT);
	glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 1);
	
//  khpsTextureRangeAndMemcpy, khpsTextureRangeAndNoMemcpy, khpsNoTextureRangeAndNoMemcpy
	if(textureMode==khpsTextureRangeAndMemcpy || textureMode==khpsTextureRangeAndNoMemcpy)				
		glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, movieRect.size.width * movieRect.size.height, textureBlitBuffer8);
	else if(textureMode==khpsNoTextureRangeAndNoMemcpy)
		glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 0, NULL);

	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , textureHint); 
	glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
			
//	glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, movieRect.size.width, movieRect.size.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureBlitBuffer);
	glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_LUMINANCE8, movieRect.size.width, movieRect.size.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, textureBlitBuffer8);
	

}


 - (void)releaseTexturesFromContext
 {
	
	if(textureBlitBuffer8!=NULL){
		glDeleteTextures(1, &textureNumber8);
		free((void*)textureBlitBuffer8);
		textureBlitBuffer8=NULL;
	}
	if(textureBlitBuffer32!=NULL){
		glDeleteTextures(1, &textureNumber32);
		free((void*)textureBlitBuffer32);
		textureBlitBuffer32=NULL;
	}

 }


 - (void)enableProgressBars:(id)caller window:(NSWindow*)theWindow  bar:(NSProgressIndicator*)theBar text:(NSTextField*)theText
 {
	useProgressBars =   TRUE;
	appObject=			caller;
	progressWindow=		theWindow;
	progressBar=		theBar;
	progressText=		theText;
 }
 

 - (void)disableProgressBars
 {
	useProgressBars=FALSE;
 }


  - (void)activateProgressBar:(ProgressActivityType)activity
 {
	if(useProgressBars){
		[progressBar setIndeterminate:FALSE];
		[progressBar setUsesThreadedAnimation:FALSE];
		[progressBar setMinValue:(double)1];
		[progressBar setMaxValue:(double)numFrames];
		[progressBar setDoubleValue:(double)0];
		switch(activity){
			case khpsLoadingFile: 
				[progressWindow setTitle:@"Loading File"];
				[progressText setStringValue:@"loading HIPS file..."];
				[progressWindow makeKeyAndOrderFront:appObject];
			break;
			case khpsMakingTextures:
				[progressWindow setTitle:@"Making Textures"];
				[progressText setStringValue:@"converting HIPS data to OpenGL textures..."];
				[progressWindow makeKeyAndOrderFront:appObject];
			break;
			case khpsFindingLuminanceMean:
				[progressWindow setTitle:@"Calculating"];
				[progressText setStringValue:@"Calculating mean image luminance..."];
				[progressWindow makeKeyAndOrderFront:appObject];
			break;
			case khpsFindingLuminanceEdgeMean:
				[progressWindow setTitle:@"Calculating"];
				[progressText setStringValue:@"Calculating mean image edge luminance..."];
				[progressWindow makeKeyAndOrderFront:appObject];
			break;
			case khpsProgressComplete:
				[progressWindow orderOut:appObject];
			break;
		}
	}
 }
 
 
- (BOOL)getMovieColorFlag
{
	NSAssert(floatMovieMemory!=nil, @"Attempt to query movie color flag when no movie is loaded");
	return(isMovieColor);
}

- (int)getNumFrames
{
	return(numFrames);
}

 - (float)getFrameRate
{
	return(fps);
}

 - (NSRect)getMovieRect
{
	return(movieRect);
}

 - (NSRect)getTargetRect
{
	return(targetRect);
}

 - (NSString*)getFullFileName
{
	return(fullFileName);
}

 - (void)setFullFileName:(NSString*)newFileName
 {
	fullFileName=[[NSString alloc] initWithString:newFileName];
 }
 
 - (ScaleModeType)getScalingMode
 {
	return(currentScaleMode);
 }

 
 - (void)deallocMovie
 {
	[self releaseTexturesFromContext];
	if(floatMovieMemory!=NULL){
		free((void*)floatMovieMemory);
		floatMovieMemory=NULL;
		[fullFileName release];
		fullFileName=NULL;
		[headerString release];
		headerString=NULL;
	}
	if(textureFramesMemory8!=NULL){
		//tear down the texture here
		free((void*)textureFramesMemory8);
		textureFramesMemory8=NULL;
	}
	if(textureFramesMemory32!=NULL){
		//tear down the texture here
		free((void*)textureFramesMemory32);
		textureFramesMemory32=NULL;
	}
	
	
}


 - (void)dealloc
{
	[self deallocMovie];
	[super dealloc];
}
 

 - (NSString*)getHeaderString;
{
	return(headerString);
}

 - (float)getMeanPixelValue
 {
	double			sumPixelValue, totalPixels;
	unsigned long   i;
	double			guageStepSizePixels, guageStepAccumulator;

	
	if(!isMeanPixelLValid){
		totalPixels= movieRect.size.width *  movieRect.size.height * numFrames;
		guageStepSizePixels=movieRect.size.width *  movieRect.size.height;
		if(useProgressBars)
			[self activateProgressBar:khpsFindingLuminanceMean];
		sumPixelValue=0;
		guageStepAccumulator=guageStepSizePixels;
		for(i=0;i<totalPixels;i++){
			sumPixelValue= sumPixelValue + floatMovieMemory[i];
			if(useProgressBars && i+1 == guageStepAccumulator){
					[progressBar incrementBy:(double)1];
					[progressBar displayIfNeeded];
					guageStepAccumulator=guageStepAccumulator+guageStepSizePixels;
			}	
		}
		if(useProgressBars)
			[self activateProgressBar:khpsProgressComplete];
		meanPixelL=sumPixelValue/totalPixels * scaleL + offsetL;
		isMeanPixelLValid=TRUE;
	}
	return(meanPixelL);
 }
 
 
 - (float)getMeanEdgePixelValue
 { 	//	We could make this faster by iterating only over the border pixels

	double			totalPixels, sumPixelValue;
	unsigned long   iFrame, x, y, pixIndex;

	if(!isMeanEdgePixelLValid){
		totalPixels=0;
		sumPixelValue=0;
		if(useProgressBars)
			[self activateProgressBar:khpsFindingLuminanceEdgeMean];
		for(iFrame=0;iFrame < numFrames; iFrame++){
			for(y=0;y<movieRect.size.height;y++){
				for(x=0;x<movieRect.size.width;x++){
					if(x==0 || y==0 || x==movieRect.size.width-1 || y==movieRect.size.height-1){
						++totalPixels;
						pixIndex= movieRect.size.width * movieRect.size.height * iFrame + movieRect.size.width * y + x;
						sumPixelValue=sumPixelValue+floatMovieMemory[pixIndex];
					}
				}
			}
			if(useProgressBars){
				[progressBar setDoubleValue:(double)iFrame+1];
				[progressBar displayIfNeeded];
			}	
		}
		if(useProgressBars)
			[self activateProgressBar:khpsProgressComplete];
		meanPixelEdgeL=sumPixelValue/totalPixels * scaleL + offsetL;
		isMeanEdgePixelLValid=TRUE;
	}
	return(meanPixelEdgeL);
		
 }

@end



