#include "memoryMovieMaker.h"



#define kVideoTimeScale       		600              		// 600 units per second
#define kVideoFrameDuration     	kVideoTimeScale/29.97      // each frame is 1/33 second




//--------------------------------------------------------------
memoryMovieMaker::memoryMovieMaker(){
	
	myTrack = NULL;
	myMedia = NULL;
	myResRefNum = 0;
	myResID = 0;
	myDataRef = NULL;
	myHandle = NULL;
	myErr = noErr;
	myGWorld = NULL;
	myPixMap = NULL;
	
	width = 0;
	height = 0;
	
	myMaxComprSize = 0L;
	myComprDataHdl = NULL;
	myComprDataPtr = NULL;
	myImageDesc = NULL;
	
	bOkToMakeMovie = false;
	
	myCodecType = kJPEGCodecType;	// jpeg is default codec
	
}

//--------------------------------------------------------------
Movie * memoryMovieMaker::returnMyMovie(){
	return &myMovie;
}

//--------------------------------------------------------------
void memoryMovieMaker::listCodecs(){
	
	initializeQuicktime();
	
	OSStatus error = noErr;
	CodecNameSpecListPtr list;
	CodecNameSpec * codecNameSpecPtr;
	char typeName[32];
	
	error = GetCodecNameList( &list, 0 );
	if ( error ) return;

	int numCodecs = list->count;
	codecNameSpecPtr = (CodecNameSpec *)((short *)list + 1);

	for (int i = 0; i < numCodecs; i++ ){
		p2cstrcpy( typeName, codecNameSpecPtr->typeName );
		printf("codec (%i) = %s \n", i, typeName);
		codecNameSpecPtr++;
	}
	DisposeCodecNameList( list );
}

//--------------------------------------------------------------
void memoryMovieMaker::setCodecType(int chosenCodec){
	
	initializeQuicktime();
	
	
	OSStatus error = noErr;
	CodecNameSpecListPtr list;
	CodecNameSpec * codecNameSpecPtr;
	char typeName[32];
	
	error = GetCodecNameList( &list, 0 );
	if ( error ) return;

	int numCodecs = list->count;
	codecNameSpecPtr = (CodecNameSpec *)((short *)list + 1);

	for (int i = 0; i < numCodecs; i++ ){
		if (i == chosenCodec){
			p2cstrcpy( typeName, codecNameSpecPtr->typeName );
			printf("trying to set codec type to (%s) \n", typeName);
			myCodecType = codecNameSpecPtr->cType;
		}
		codecNameSpecPtr++;
	}
	
	
}
		

		
//--------------------------------------------------------------
void memoryMovieMaker::startMakingMovieInRam(int w, int h){

	initializeQuicktime();
	
	width 		= w;
  	height 		= h;
  
  	//-------------- video -------------------
	//-------------------------------------------
	// create a new handle to hold the media data
	myHandle = NewHandleClear(0);
	if (myHandle == NULL) return;
	//-------------------------------------------
	// create a data reference to that handle
	PtrToHand(&myHandle, &myDataRef, sizeof(Handle));
	if (myDataRef == NULL) return;
	//-------------------------------------------
	myMovie = NewMovie(newMovieActive);
	if (myMovie == NULL) return;
	//-------------------------------------------
	myErr = SetMovieDefaultDataRef(myMovie, myDataRef, HandleDataHandlerSubType);
	if (myErr != noErr) return;
	//-------------------------------------------
	// create the movie track and media
	myTrack = NewMovieTrack(myMovie, FixRatio(width, 1), FixRatio(height, 1), kNoVolume);
	myErr = GetMoviesError();
	if (myErr != noErr) return;
	//-------------------------------------------
	myMedia = NewTrackMedia(myTrack, VideoMediaType, kVideoTimeScale, NULL, 0);
	myErr = GetMoviesError();
	if (myErr != noErr) return;
	//-------------------------------------------
	// create the media samples
	myErr = BeginMediaEdits(myMedia);
	if (myErr != noErr) return;
	
	//-------------- graphics -------------------
	//-------------------------------------------
	MacSetRect(&myRect, 0, 0, width, height);
	//-------------------------------------------
	myErr = NewGWorld(&myGWorld, 24, &myRect, NULL, NULL, (GWorldFlags)0);
	if (myErr != noErr) return;
	//-------------------------------------------
	myPixMap = GetGWorldPixMap(myGWorld);
	if (myPixMap == NULL) return;
	//-------------------------------------------
	LockPixels(myPixMap);
	myErr = GetMaxCompressionSize(  myPixMap,
	          &myRect, 
	          0,              // let ICM choose depth
	          codecNormalQuality, 
	          myCodecType, 
	          (CompressorComponent)anyCodec,
	          &myMaxComprSize);
	if (myErr != noErr) return;
	//-------------------------------------------
	myComprDataHdl = NewHandle(myMaxComprSize);
	if (myComprDataHdl == NULL) return;
	//-------------------------------------------
	HLockHi(myComprDataHdl);
	#if TARGET_CPU_68K
		myComprDataPtr = StripAddress(*myComprDataHdl);
	#else
		myComprDataPtr = *myComprDataHdl;
	#endif
	//-------------------------------------------
	myImageDesc = (ImageDescriptionHandle)NewHandle(4);
	if (myImageDesc == NULL) return;
	//-------------------------------------------
	
	
}


//--------------------------------------------------------------
void memoryMovieMaker::finishMakingMovieInRam(){

	//-------------- graphics -------------------
	//-------------------------------------------
	if (myImageDesc != NULL)
    	DisposeHandle((Handle)myImageDesc);
  	if (myComprDataHdl != NULL)
    	DisposeHandle(myComprDataHdl);
 	if (myGWorld != NULL)
    	DisposeGWorld(myGWorld);
    		
	//-------------- movie -------------------
	//-------------------------------------------
	// end the movie:
  	if (EndMediaEdits(myMedia)  != noErr){
  		if (myDataRef != NULL) DisposeHandle(myDataRef);
  		return;
  	}
  	//-------------------------------------------
  	// add the media to the track
  	if (InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1)  != noErr){
  		if (myDataRef != NULL) DisposeHandle(myDataRef);
  		return;
  	}
  	//-------------------------------------------
  	// add the movie atom to the movie file
  	myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
	
}
		




//--------------------------------------------------------------
void memoryMovieMaker::saveViaFileDialog(){
	
	StandardFileReply   sfr;
	short               nFileRefNum =0;
    short               nResID =movieInDataForkResID;
	StandardPutFile(c2pstr("Enter movie file name:"), c2pstr("untitled.mov"), &sfr);
	Movie				myNewMovie = NULL;
	myResID 			= movieInDataForkResID;
	myNewMovie = FlattenMovieData(	myMovie,
										flattenAddMovieToDataFork | flattenForceMovieResourceBeforeMovieData,
										&sfr.sfFile,
										('TVOD'),
										smSystemScript,
										createMovieFileDeleteCurFile | createMovieFileDontCreateResFile);
	DisposeMovie(myNewMovie);
}

//--------------------------------------------------------------
void memoryMovieMaker::save(char * fileName){
	
	
	
	FSSpec fss;
	OSErr result;
	
	//-------------------------------
	// we need a full path here to get an FSSpec:
	//
	char cwdir[MAX_PATH];
	_getcwd(cwdir, _MAX_PATH);
	char buffer[MAX_PATH];
	sprintf(buffer, "%s//%s",  cwdir, fileName);
	// mac might be different then this:
	NativePathNameToFSSpec (buffer, &fss, 0);
	//-------------------------------
	
	Movie				myNewMovie = NULL;
	
	myNewMovie = FlattenMovieData(	myMovie,
										flattenAddMovieToDataFork | flattenForceMovieResourceBeforeMovieData,
										&fss,
										('TVOD'),
										smSystemScript,
										createMovieFileDeleteCurFile | createMovieFileDontCreateResFile);
	
	DisposeMovie(myNewMovie);
}
		


//--------------------------------------------------------------
void memoryMovieMaker::addFrame(unsigned char * pixels){
	addFrame(pixels, kVideoFrameDuration);
}		

//--------------------------------------------------------------
void memoryMovieMaker::addFrame(unsigned char * pixels, float frameDuration){
	
	//-------------------------------------------
  	// store the old gworld, get to the new one:
  	CGrafPtr           			mySavedPort;
	GDHandle          			mySavedDevice;
  	GetGWorld(&mySavedPort, &mySavedDevice);
  	SetGWorld(myGWorld, NULL);
  	
  	
  	//-------------------------------------------
  	// put data in the movie:
    Ptr    gwAddress, gwAddressBase;
    long   gwWidth;
    
    unsigned char * pixPtr;
    gwAddressBase = GetPixBaseAddr( GetGWorldPixMap( myGWorld ) );   // Get head address of offscreen      
    gwWidth = ( **GetGWorldPixMap( myGWorld ) ).rowBytes & 0x3fff;   // Get with of offscreen              
    
    // this could really be optimized ;)
    
    /*
    for (int i = 0; i < width; i++){
    for (int j = 0; j < height; j++){
	    pixPtr = pixels + (i * 3) + (j * width);
	    gwAddress = gwAddressBase + (i * 4 + 1) + ( j * gwWidth );  	// Get adress for current pixel       
	    memcpy(gwAddress, pixPtr, 3);
    }
    }
    */
    
    for (int j = 0; j < height; j++){
	    pixPtr = pixels + (j * width * 3);
	    gwAddress = gwAddressBase + ( j * gwWidth );  	// Get adress for current pixel       
	    memcpy(gwAddress, pixPtr, width * 3);
    }
    
	//-------------------------------------------
  
	
	
	//-------------------------------------------
  	// compress the image:
    myErr = CompressImage(  myPixMap, 
                &myRect, 
                codecNormalQuality,
                myCodecType,
                myImageDesc, 
                myComprDataPtr);
    if (myErr != noErr){
    	 printf("error in compress image \n");
    	 SetGWorld(mySavedPort, mySavedDevice);
    	 return;
    }
    
    //-------------------------------------------
  	// add the image to the movie:
    myErr = AddMediaSample(  myMedia, 
                myComprDataHdl,
                0,                // no offset in data
                (**myImageDesc).dataSize, 
                frameDuration,      // frame duration
                (SampleDescriptionHandle)myImageDesc, 
                1,                // one sample
                0,                // self-contained samples
                NULL);
    if (myErr != noErr){
    	 printf("error in AddMediaSample \n");
    	 SetGWorld(mySavedPort, mySavedDevice);
    	 return;
    }

	//-------------------------------------------
  	// return current to current gworld
 	SetGWorld(mySavedPort, mySavedDevice);

}

