/* -LICENSE-START-
 ** Copyright (c) 2009 Blackmagic Design
 **
 ** Permission is hereby granted, free of charge, to any person or organization
 ** obtaining a copy of the software and accompanying documentation covered by
 ** this license (the "Software") to use, reproduce, display, distribute,
 ** execute, and transmit the Software, and to prepare derivative works of the
 ** Software, and to permit third-parties to whom the Software is furnished to
 ** do so, all subject to the following:
 ** 
 ** The copyright notices in the Software and this entire statement, including
 ** the above license grant, this restriction and the following disclaimer,
 ** must be included in all copies of the Software, in whole or in part, and
 ** all derivative works of the Software, unless such copies or derivative
 ** works are solely in the form of machine-executable object code generated by
 ** a source language processor.
 ** 
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 ** DEALINGS IN THE SOFTWARE.
 ** -LICENSE-END-
 */

/* DeckLinkKeyerMovieView.m */

#import "DeckLinkKeyerMovieView.h"
#import "DeckLinkKeyerController.h"

// Functions missing from the 10.7+ SDKs
#if !defined(__QUICKDRAWAPI__)
extern "C" {
	extern void SetRect(Rect * r,    short  left,    short  top,    short  right,    short  bottom)    __attribute__((weak_import));
}
#endif /* __QUICKDRAWAPI__ */

#if !defined(__QDOFFSCREEN__)
extern "C" {
	extern QDErr NewGWorldFromPtr( GWorldPtr* offscreenGWorld, UInt32 PixelFormat,    const Rect *  boundsRect, CTabHandle cTable,/* can be NULL */ GDHandle aGDevice,/* can be NULL */ GWorldFlags flags, Ptr           newBuffer, SInt32 rowBytes) __attribute__((weak_import));
	extern void DisposeGWorld(GWorldPtr offscreenGWorld) __attribute__((weak_import));
	extern GDHandle GetGWorldDevice(GWorldPtr offscreenGWorld) __attribute__((weak_import));
}
#endif /* __QDOFFSCREEN__ */

@implementation DeckLinkKeyerMovieView

- (id)initWithCoder:(NSCoder*)coder
{
	if (self = [super initWithCoder:coder])
	{
		highlight = NO;
		acceptDrags = YES;
		frameGWorld = NULL;
		frameGWorldStorage = NULL;
		[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
	}
	return self;
}

- (void)setAcceptDrags:(BOOL)flag
{
	acceptDrags = flag;
}

- (BOOL)setMovieFile:(NSString*)filename forMode:(IDeckLinkDisplayMode*)displayMode
{
	NSURL*			fileURL;
	FSRef			movieRef;
	FSSpec			movieSpec;
	short			movieFileRefNum = 0;
	short			movieResID = 0;
	//
	int				movieWidth;
	int				movieHeight;
	//
	Rect			qdRect;
	Movie			newMovie = NULL;
	OSErr			error;
	//
	NSBitmapImageRep* ourRep = NULL;
	unsigned char * bitmapPtr = NULL;
	NSImage *		ourImage = NULL;
	NSSize			ourImageSize;
	//
	BOOL			success = NO;
	
	fileURL = [NSURL fileURLWithPath:filename];
	// Convert the document's path to a FSRef
	if (CFURLGetFSRef((CFURLRef)fileURL, &movieRef) == false)
		goto bail;
	
	// Convert the movie's FSRef to a FSSpec
	if (FSGetCatalogInfo(&movieRef, kFSCatInfoNone, NULL, NULL, &movieSpec, NULL) != noErr)
		goto bail;
	
	// Open the movie from the FSSpec
	error = OpenMovieFile(&movieSpec, &movieFileRefNum, fsRdPerm);
    if (error != noErr)
		goto bail;

	if (NewMovieFromFile(&newMovie, movieFileRefNum, &movieResID, NULL, newMovieActive, NULL) != noErr)
		goto bail;
	
	
	GetMovieBox(newMovie, &qdRect);
	movieWidth = (qdRect.right - qdRect.left);
	movieHeight = (qdRect.bottom - qdRect.top);
	
	// If the movie dimensions are larger than the display mode dimensions,
	// scale the frame (preserving its aspect ratio) to fit within the
	// display mode dimensions.
	if ((movieWidth > displayMode->GetWidth()) || (movieHeight > displayMode->GetHeight()))
	{
		// -- First attempt to resize to the display mode width
		frameGWorldWidth = displayMode->GetWidth();
		frameGWorldHeight = ((movieHeight * displayMode->GetWidth()) / movieWidth);
		// -- If the height exceeds the display mode height, resize to the display mode height
		if (frameGWorldHeight > displayMode->GetHeight())
		{
			frameGWorldWidth = ((displayMode->GetHeight() * movieWidth) / movieHeight);
			frameGWorldHeight = displayMode->GetHeight();
		}
	}
	else
	{
		// Centre smaller image in output window
		frameGWorldWidth = movieWidth;
		frameGWorldHeight = movieHeight;
	}
	
	if (frameGWorld)
	{
		DisposeGWorld(frameGWorld);
		frameGWorld = NULL;
	}
	
	frameGWorldStorage = (Ptr) [controller getFrameBuffer:frameGWorldWidth height:frameGWorldHeight];
	if(frameGWorldStorage == NULL)
		goto bail;
			
	SetRect(&qdRect, 0, 0, frameGWorldWidth, frameGWorldHeight);
	error = QTNewGWorldFromPtr(&frameGWorld, k32ARGBPixelFormat, &qdRect, NULL, NULL, 0, frameGWorldStorage, frameGWorldWidth*4*sizeof(uint8_t));
	if (error)
		goto bail;

	
	// Draw to offscreen GWorld
	SetMovieGWorld(newMovie, frameGWorld, GetGWorldDevice(frameGWorld));
	SetMovieBox(newMovie, &qdRect);
	UpdateMovie(newMovie);
	MoviesTask(newMovie, 0);
	
	
	ourRep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:frameGWorldWidth pixelsHigh:frameGWorldHeight
																 bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES
																  isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:frameGWorldWidth*4
																  bitsPerPixel:32] autorelease];
	bitmapPtr = [ourRep bitmapData];
	int x, y;
	for (x = 0; x < frameGWorldWidth; x++)
	{
		for (y = 0; y < frameGWorldHeight; y++)
		{
			bitmapPtr[(y * frameGWorldWidth + x) * 4 + 0] = ((unsigned char*)frameGWorldStorage)[(y * frameGWorldWidth + x) * 4 + 1];
			bitmapPtr[(y * frameGWorldWidth + x) * 4 + 1] = ((unsigned char*)frameGWorldStorage)[(y * frameGWorldWidth + x) * 4 + 2];
			bitmapPtr[(y * frameGWorldWidth + x) * 4 + 2] = ((unsigned char*)frameGWorldStorage)[(y * frameGWorldWidth + x) * 4 + 3];
			bitmapPtr[(y * frameGWorldWidth + x) * 4 + 3] = ((unsigned char*)frameGWorldStorage)[(y * frameGWorldWidth + x) * 4 + 0];
		}
	}
	
	ourImageSize.width = frameGWorldWidth;
	ourImageSize.height = frameGWorldHeight;
	ourImage = [[[NSImage alloc] initWithSize:ourImageSize] autorelease];
	[ourImage addRepresentation:ourRep];
	[self setImage:ourImage];
	
	
	[self setNeedsDisplay:YES];
	success = YES;
	
bail:
	// Close the movie file, even if the movie was successfully opened
	if (movieFileRefNum != 0)
	{
		CloseMovieFile(movieFileRefNum);
		movieFileRefNum = 0;
	}
	if (newMovie != NULL)
	{
		DisposeMovie(newMovie);
	}
	
	return success;
}

- (void)getImageGWorldStorage:(Ptr*)storage width:(int*)outWidth height:(int*)outHeight
{
	*storage = 	frameGWorldStorage;
	*outWidth = frameGWorldWidth;
	*outHeight = frameGWorldHeight;
}

- (void)drawRect:(NSRect)rect
{
	[super drawRect:rect];
	
	if (highlight)
	{
        // Highlight by overlaying a gray border
        [[NSColor grayColor] set];
        [NSBezierPath setDefaultLineWidth:5];
        [NSBezierPath strokeRect:rect];
    }
}

/* Dragging destination methods */
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
	NSPasteboard*		pboard;
	
	// Examine the contents
	pboard = [sender draggingPasteboard];
	if ((acceptDrags == YES) && ([[pboard types] containsObject:NSFilenamesPboardType]))
	{
		highlight = YES;
		[self setNeedsDisplay:YES];
		
		return NSDragOperationGeneric;		// Accept data as a generic operation
	}
	
	return NSDragOperationNone;
}

- (void)draggingExited:(id <NSDraggingInfo>)sender
{
	highlight = NO;
	[self setNeedsDisplay:YES];
}

- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
	highlight = NO;
	[self setNeedsDisplay:YES];
	return YES;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
	NSPasteboard*		pboard;
	
	// Do the work here
	// -- Examine the contents
	pboard = [sender draggingPasteboard];
	if ([[pboard types] containsObject:NSFilenamesPboardType])
	{
		NSArray*		files;
		
		files = [pboard propertyListForType:NSFilenamesPboardType];
		if ([files count] > 0)
		{
			[controller setMovieFile:[files objectAtIndex:0]];
		}
		
		return YES;
    }
	
	return NO;
}

- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
{
	
}

@end

