//
//  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

#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 	20	/* frame duration = 1/20 sec */
#define		kTrackStart		0
#define		kMediaStart		0



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

-(id)init
{
    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;

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

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


    /* if successful, save file under designated name */
    
    if (runResult == NSOKButton)
    {
       movieFilename = [sp filename];
    }

    return movieFilename;
}

/*************************************************************************
* FUNCTION: createMovie
* PURPOSE : Creates a movie, a video track and a media
**************************************************************************/
- (OSErr)createMovie:(Rect)trackFrame gWorld:(GWorldPtr)theGWorld
		   filename:(NSString *)myFilename
{
    OSErr err = noErr;
    FSSpec mySpec;

    // Get FSSpec representation of the entered filename
    err = [self 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;
    gTheTrack = NewMovieTrack(gTheMovie,				// movie specifier
			     FixRatio(gTrackFrame.right, 1),	// width
			     FixRatio(gTrackFrame.bottom, 1),	// height
			     kNoVolume);			// track volume

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

    // 3.
    // Create the media for the track
    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.
    // Set up the environment for movie compression
    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);
       [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"];
    }
}

/**************************************************************************
*  FUNCTION: initCompression 
*  PURPOSE : Initialises and sets up the environment for movie compression
***************************************************************************/
-(OSErr)initCompression:(GWorldPtr)theGWorld
{
    OSErr err = noErr;

    // Calculate the max size after compression
    err = GetMaxCompressionSize(GetPortPixMap(theGWorld),	// Handle to the source image
				&gTrackFrame,			// bounds
				kMgrChoose,			// Let ICM choose color depth
				codecNormalQuality,		// 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;
    
}

/**************************************************************************
*  FUNCTION: endCompression 
*  PURPOSE : Frees resources used by compression
***************************************************************************/
-(void)endCompression
{
    // Dealocate our previously alocated handles
    if (gImageDesc)
    {
       DisposeHandle ((Handle)gImageDesc);
    }

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

/**************************************************************************
*  FUNCTION: addGWorldToMedia 
*  PURPOSE : Compresses the image in 'theGWorld' and adds it to the media
***************************************************************************/
-(void)addGWorldToMedia:(GWorldPtr)theGWorld
{
    OSErr err = noErr;

    // Use the ICM to compress the image
    err = CompressImage(GetPortPixMap(theGWorld), /* source image to compress */
			&gTrackFrame, 		  /* bounds */
			codecNormalQuality,	  /* 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 */
    [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: 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
{
    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
       result = [fileManager createFileAtPath:inPath contents:nil attributes:nil];
    }

    if (result)
    {
       status = FSPathMakeRef ([inPath fileSystemRepresentation],
             &myFSRef,
             NULL);


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

    }
    return status;
}

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