//
//  MyMovie.m
//  Cocoa - SGDataProc
//
//  Created by wilson on Tue Aug 10 2004.
//  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
//

#import "MyVideo.h"


@implementation MyVideo

#define BailErr(x) {err = x; if(err != noErr) return err;}

/////////////////////////////////////////////////////////////
//
// Constants
//
/////////////////////////////////////////////////////////////

#define kMyCreatorType		FOUR_CHAR_CODE('TVOD')
#define kMyErrorCode		-22222

// Might delete later ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define 	kVideoTimeScale		600
#define		kNumVideoFrames 	70
#define		kPixelDepth 		8	/* use 8-bit depth */
#define		kNoOffset 		0
#define		kMgrChoose		0
#define		kSyncSample 		0
#define		kAddOneVideoSample	1
#define		kSampleDuration 	60	/* frame duration = 1/10 sec */
#define		kTrackStart		0
#define		kMediaStart		0



/**********************************************************************
*
* FUNCTION: init
* PURPOSE : initialise this object
*
***********************************************************************/

-(id)init
{
    //jj =2323;
    
    if (self = [super init])
    {
	gTheMovie = nil;
	gResRefNum = 0;
    }

    return self;
}

/*************************************************************************
* FUNCTION: promptMovieFilename
* PURPOSE : Gets the filename of the movie to be saved
* OUTPUT  : Returns the full path of the file in NSString, OR
*	    nil on error
*
**************************************************************************/
- (NSString *)promptMovieFilename
{
    NSSavePanel *sp;
    int runResult;
    NSString *movieFilename = nil;
    //OSErr err = kMyErrorCode;

    /* create or get the shared instance of NSSavePanel */
    sp = [NSSavePanel savePanel];


    /* set up new attributes */
    /*
     [sp setAccessoryView:newView];

     [sp setRequiredFileType:@"txt"];
     */

    /* display the NSSavePanel */
    runResult = [sp runModalForDirectory:NSHomeDirectory() file:nil];


    /* if successful, save file under designated name */
    
    if (runResult == NSOKButton)
    {
        NSString *tempFilename;
        NSRange range;

        tempFilename = [sp filename];

        range = [tempFilename rangeOfString:@".mov" options:NSCaseInsensitiveSearch];

        if ((range.location == NSNotFound) && (range.length == 0)) // no .mov extension
            movieFilename = [tempFilename stringByAppendingString:@".mov"];
        else
            movieFilename = [tempFilename copy];
    }

    return movieFilename;
}

- (OSErr)createMovie:(Rect)trackFrame gWorld:(GWorldPtr)theGWorld
		   filename:(NSString *)myFilename
{
    OSErr err = noErr;
    FSSpec mySpec;

/*
    err = [self promptMovieFilename];
    [self checkError:err message:@"promptMovieFilename failed"];
    BailErr(err);
  */
    
    // Get FSSpec representation of the entered filename
//    err = [self myMakeFSSpec: &mySpec fromPath: myFilename];
[MyVideo myMakeFSSpec:&mySpec fromPath:myFilename];
    [self checkError:err message:@"myMakeFSSpec failed"];
    BailErr(err);    
    
    // 1.
    // Create and open the movie file, this call creates an empty movie which
    // references the file, and opens the movie file with write permission
    
    err = CreateMovieFile(&mySpec,				// FSSpec specifier
			  kMyCreatorType,			// file creator type
			  smCurrentScript,
			  createMovieFileDeleteCurFile |
			  createMovieFileDontCreateResFile |
			  newMovieActive,			// movie file creation flags
			  &gResRefNum,				// file ref num
			  &gTheMovie);				// field to receive movie specification

    [self checkError:err message:@"Could not create movie file"];
    BailErr(err);

    // 2.
    // Create the movie track
    gTrackFrame = trackFrame;
    NSLog(@"trackFrame = %d, %d, %d, %d", trackFrame.top, trackFrame.left, trackFrame.bottom, trackFrame.right);
    gTheTrack = NewMovieTrack(gTheMovie,				// movie specifier
			     FixRatio(gTrackFrame.right, 1),	// width
			     FixRatio(gTrackFrame.bottom, 1),	// height
			     kNoVolume);			// track volume

//    NSLog(@"GetMoviesError() returns = %d", GetMoviesError());
    err = GetMoviesError();
    [self checkError:err message:@"NewMovieTrack failed"];
    BailErr(err);

    // 3.
    // Create the media for the tract
    gTheMedia = NewTrackMedia(gTheTrack,		// track identifier
			      VideoMediaType,		// type of media
			      kVideoTimeScale,		// time coordinate system
			      nil, 			// data reference, use the file associated with the movie
			      0);			// data reference type

    err = GetMoviesError();
    [self checkError:err message:@"NewTrackMedia failed"];
    BailErr(err);

    // 4.
    // Establish a media-editing session
    err = BeginMediaEdits(gTheMedia);
    [self checkError:err message:@"BeginMediaEdits failed"];
    BailErr(err);


    // 5.
    // Add media samples to movie
    //[self myAddVideoSampleToMedia];
    err = [self initCompression:theGWorld];
    [self checkError:err message:@"initCompression failed"];

    return err;
}

/*************************************************************************
*
*  FUNCTION: closeMovie 
*
*  PURPOSE : End media editing session and close the movie file
*
***************************************************************************/

-(void)closeMovie
{
    OSErr err = noErr;
    short resId = movieInDataForkResID;

    // 1. Clean up resources used by compression
    // Dealocate our previously alocated handles
    if (gImageDesc)
    {
	DisposeHandle ((Handle)gImageDesc);
    }

    if (gCompressedData)
    {
	DisposeHandle (gCompressedData);
    }

    if (gResRefNum)
    {
	// 2. End media editing session
	err = EndMediaEdits(gTheMedia);
	[self checkError:err message:@"EndMediaEdits failed"];

	// 3. Add Media into track and close movie file
	err = InsertMediaIntoTrack(gTheTrack,
				kTrackStart,
				kMediaStart,
				GetMediaDuration(gTheMedia),
				fixed1);
	NSLog(@"media duration = %d", GetMediaDuration(gTheMedia));
	[self checkError:err message:@"InsertMediaIntoTrack failed"];
    
	err = AddMovieResource(gTheMovie,
			    gResRefNum,
			    &resId,
			    nil);
	[self checkError:err message:@"AddMovieResource failed"];
    
	// Close the movie file
	err = CloseMovieFile(gResRefNum);
	[self checkError:err message:@"CloseMovieFile failed"];

	// Free any memory being used by a movie, including the
	// memory used by the movie's tracks and media structures.
	DisposeMovie(gTheMovie);
	err = GetMoviesError();
	[self checkError:err message:@"DisposeMovie() failed"];
	//NSRunAlertPanel(@"Error", @"DisposeMovie() in MyVideo", @"OK", nil, nil);
    }
}


-(OSErr)initCompression:(GWorldPtr)theGWorld
{
    OSErr err = noErr;

    err = GetMaxCompressionSize(GetPortPixMap(theGWorld),	// Handle to the source image
				&gTrackFrame,			// bounds
				kMgrChoose,			// Let ICM choose color depth
				codecMinQuality,		// desired image quality
				kJPEGCodecType,			// compressor type
				(CompressorComponent)anyCodec,	// compressor identifier
				&gMaxCompressedSize);		// returned size
    [self checkError:err message:@"GetMaxCompression failed"];

    // Create a new handle of the right size for our compressed image data
    gCompressedData = NewHandle(gMaxCompressedSize);
    err = MemError();
    [self checkError:err message:@"NewHandle error"];

    MoveHHi( gCompressedData );
    HLock( gCompressedData );
    gCompressedDataPtr = *gCompressedData;

    // Create a handle for the Image Description Structure
    gImageDesc = (ImageDescriptionHandle)NewHandle(4);
    err = MemError();
    [self checkError:err message:@"NewHandle error"];

    return err;
    
}

-(void)endCompression
{
    // Dealocate our previously alocated handles
    if (gImageDesc)
    {
	DisposeHandle ((Handle)gImageDesc);
    }

    if (gCompressedData)
    {
	DisposeHandle (gCompressedData);
    }
}

-(void)addGWorldToMedia:(GWorldPtr)theGWorld
{
    OSErr err = noErr;

    // Use the ICM to compress the image
    err = CompressImage(GetPortPixMap(theGWorld), /* source image to compress */
			&gTrackFrame, 		  /* bounds */
			codecMinQuality,	  /* desired image quality */
			kJPEGCodecType,		  /* compressor identifier */
			gImageDesc, 		  /* handle to Image Description Structure; will be resized by call */
			gCompressedDataPtr);	  /* pointer to a location to recieve the compressed image data */
    //NSLog(@"sample #: %d", curSample);
    [self checkError:err message:@"CompressImage error"];


    // Add sample data and a description to a media
    err = AddMediaSample(gTheMedia,		 /* media specifier */
			gCompressedData,	 /* handle to sample data - dataIn */
			kNoOffset,		 /* specifies offset into data reffered to by dataIn handle */
			(**gImageDesc).dataSize, /* number of bytes of sample data to be added */
			kSampleDuration,	 /* frame duration = 1/10 sec */
			(SampleDescriptionHandle)gImageDesc,	/* sample description handle */
			kAddOneVideoSample,	/* number of samples */
			kSyncSample,		/* control flag indicating self-contained samples */
			nil);			/* returns a time value where sample was insterted */
    [self checkError:err message:@"AddMediaSample error"];
    
}

/*************************************************************************
*
*  FUNCTION: myAddVideoSampleToMedia
*
*  PURPOSE : Creates video samples and add them to the media
*
***************************************************************************/

-(void) myAddVideoSampleToMedia
{
    GWorldPtr theGWorld = nil;
    long maxCompressedSize;
    long curSample;
    Handle compressedData = nil;
    Ptr compressedDataPtr;
    ImageDescriptionHandle imageDesc = nil;
    CGrafPtr oldPort;
    GDHandle oldGDeviceH;
    OSErr err = noErr;

    // Create a graphics world
    err = NewGWorld(&theGWorld,
		    k32ARGBPixelFormat,
		    &gTrackFrame,
		    NULL,
		    NULL,
		    0);

    NSLog(@"gTrackFrame bottom = %d, %d, %d, %d", gTrackFrame.top, gTrackFrame.left, gTrackFrame.bottom, gTrackFrame.right);
    //CheckError (err, "NewGWorld error");
    [self checkError:err message:@"NewGWorld failed"];

    // Lock the pixels
    LockPixels (GetPortPixMap(theGWorld));

    // Determine the maximum size the image will be after compression.
    // Specify the compression characteristics, along with the image.
    err = GetMaxCompressionSize(GetPortPixMap(theGWorld),		/* Handle to the source image */
				&gTrackFrame, 					/* bounds */
				kMgrChoose, 					/* let ICM choose depth */
				codecNormalQuality,				/* desired image quality */
				kAnimationCodecType,			/* compressor type */
				(CompressorComponent)anyCodec,  	/* compressor identifier */
				&maxCompressedSize);		  	/* returned size */
    //CheckError (err, "GetMaxCompressionSize error" );
    [self checkError:err message:@"GetMaxCompressionSize error"];
    
    // Create a new handle of the right size for our compressed image data
    compressedData = NewHandle(maxCompressedSize);
    //CheckError( MemError(), "NewHandle error" );
    err = MemError();
    [self checkError:err message:@"GetMaxCompressionSize error"];


    MoveHHi( compressedData );
    HLock( compressedData );
    compressedDataPtr = *compressedData;

    // Create a handle for the Image Description Structure
    imageDesc = (ImageDescriptionHandle)NewHandle(4);
    //CheckError( MemError(), "NewHandle error" );
    err = MemError();
    [self checkError:err message:@"GetMaxCompressionSize error"];


    // Change the current graphics port to the GWorld
    GetGWorld(&oldPort, &oldGDeviceH);
    SetGWorld(theGWorld, nil);

    // For each sample...
    for (curSample = 1; curSample <= kNumVideoFrames; curSample++) {

	// Call DrawFrame to actually do the drawing of our image
	[self QTVideo_DrawFrame:curSample];

	// Use the ICM to compress the image
	err = CompressImage(GetPortPixMap(theGWorld), /* source image to compress */
			    &gTrackFrame, 			  /* bounds */
			    codecNormalQuality,		  /* desired image quality */
			    kAnimationCodecType,	  /* compressor identifier */
			    imageDesc, 			  /* handle to Image Description Structure; will be resized by call */
			    compressedDataPtr);		  /* pointer to a location to recieve the compressed image data */
	//CheckError( err, "CompressImage error" );
	NSLog(@"sample #: %d", curSample);
	[self checkError:err message:@"CompressImage error"];


	// Add sample data and a description to a media
	err = AddMediaSample(gTheMedia,				 /* media specifier */
	    compressedData,		 /* handle to sample data - dataIn */
	    kNoOffset,				 /* specifies offset into data reffered to by dataIn handle */
	    (**imageDesc).dataSize, /* number of bytes of sample data to be added */
	    kSampleDuration,		 /* frame duration = 1/10 sec */
	    (SampleDescriptionHandle)imageDesc,	/* sample description handle */
	    kAddOneVideoSample,	/* number of samples */
	    kSyncSample,			/* control flag indicating self-contained samples */
	    nil);					/* returns a time value where sample was insterted */
	//CheckError( err, "AddMediaSample error" );
	[self checkError:err message:@"AddMediaSample error"];

    } // for loop

    SetGWorld (oldPort, oldGDeviceH);

    // Dealocate our previously alocated handles and GWorld
    if (imageDesc)
    {
	DisposeHandle ((Handle)imageDesc);
    }

    if (compressedData)
    {
	DisposeHandle (compressedData);
    }

    if (theGWorld)
    {
	DisposeGWorld (theGWorld);
    }
    
}


-(void) QTVideo_DrawFrame:(long) curSample
{
    Str255 numStr;

    EraseRect (&gTrackFrame);
    ForeColor( redColor );
    PaintRect( &gTrackFrame );

    ForeColor( blueColor );
    NumToString (curSample, numStr);
    MoveTo ( (short)(gTrackFrame.right / 2), (short)(gTrackFrame.bottom / 2));
    TextSize ( (short)(gTrackFrame.bottom / 3));
    DrawString (numStr);

    if ( curSample == 70 ) {
	ForeColor( greenColor );
	MoveTo((short)(gTrackFrame.left + 12), (short)(gTrackFrame.bottom / 2 ));
	TextSize( (short)(gTrackFrame.bottom / 4) );
	DrawString("\pWhoa cool!");
    }
}

/*************************************************************************
*
*  FUNCTION: myMakeFSSpec
*            (http://developer.apple.com/documentation/Cocoa/Conceptual/
*	      CarbonCocoaDoc/Articles/CarbonInCocoa.html#//apple_ref/doc/
*	      uid/20002403/CJBHBAII)
*
*  PURPOSE: Converts filename in (NSString *) into FSSpec structure
*
***************************************************************************/

+ (OSStatus) myMakeFSSpec:(FSSpec *) myFSSpecPtr fromPath:(NSString *)inPath
{
//    NSRunAlertPanel(@"Error", inPath , @"OK", nil, nil);

    FSRef myFSRef;
    OSStatus status = noErr;
    NSFileManager *fileManager;
    BOOL result = YES;

    fileManager = [NSFileManager defaultManager];
    
    if ([fileManager fileExistsAtPath:inPath] == NO)
    {	// Create an empty if the file does not exist
	NSLog(@"creating file");
	result = [fileManager createFileAtPath:inPath contents:nil attributes:nil];
    }

    if (result)
    {
	NSLog(@"inside if statement");
	status = FSPathMakeRef ([inPath fileSystemRepresentation],
				&myFSRef,
				NULL);


	if (status == noErr)
	    status = FSGetCatalogInfo (&myFSRef,
					kFSCatInfoNone,
					NULL,
					NULL,
					myFSSpecPtr,
					NULL);

    }
    return status;
}

-(void) checkError:(OSErr)error message:(NSString *)msg
{
    if (error != noErr)
    {
	NSLog(@"Error code = %d", error);
	NSRunAlertPanel(@"Error", msg , @"OK", nil, nil);
    }
}

/*************************************************************************
*
*  FUNCTION: myGetMovieSize
*
*  PURPOSE : Gets the current size (in bytes) of the movie
*
***************************************************************************/

-(double)myGetMovieSize:(NSString *)filename
{
    TimeValue movieDuration;
    double movieDataSize;

/*
    NSNumber *fsize;
    NSFileManager *manager;
    NSDictionary *fileAttributes;

   [filename retain];

    manager = [NSFileManager defaultManager];

Ê Ê fileAttributes = [manager fileAttributesAtPath:filename traverseLink:YES];

Ê Ê fsize = [fileAttributes fileSize];


    movieDataSize = [filesize doubleValue];

    [filename release];
*/


    // get the current duration of the movie
    movieDuration = GetMovieDuration(gTheMovie);

    // get the data size of the movie (in bytes)
    movieDataSize = (double)GetMovieDataSize(gTheMovie, 0, movieDuration);

    return movieDataSize;
}


@end
