/*
 *      PsychQTCreateMovie() -- Create a movie object.
 *
 *      This function tries to open a Quicktime-Moviefile and create an
 *      associated movie object for it.
 *
 *      win = Pointer to window record of associated onscreen window.
 *      moviename = char* with the name of the moviefile.
 *      preloadSecs = How many seconds of the movie should be preloaded/prefetched into RAM at movie open time?
 *      moviehandle = handle to the new movie.
 */
void PsychQTCreateMovie(PsychWindowRecordType *win, const char* moviename, double preloadSecs, int* moviehandle)
{
    Movie theMovie = NULL;
    QTVisualContextRef QTMovieContext = NULL;
    QTAudioContextRef  QTAudioContext = NULL;
    int i, slotid;
    OSErr error;
    CFStringRef movieLocation;
	CFURLRef movieURLLocation;
    CFStringRef coreAudioDeviceUID;
    psych_bool trueValue = TRUE;
    QTNewMoviePropertyElement newMovieProperties[4] = {0};
    int propcount = 0;
    char msgerr[10000];
    char errdesc[1000];
    Rect movierect;
    psych_bool printErrors;

    // Suppress output of error-messages if moviehandle == 1000. That means we
    // run in our own Posix-Thread, not in the Matlab-Thread. Printing via Matlabs
    // printing facilities would likely cause a terrible crash.
    printErrors = (*moviehandle == -1000) ? FALSE : TRUE;
    
    // Set movie handle to "failed" initially:
    *moviehandle = -1;

    // We startup the Quicktime subsystem only on first invocation.
    if (firsttime) {
#if PSYCH_SYSTEM == PSYCH_WINDOWS
        // Initialize Quicktime for Windows compatibility layer: This will fail if
        // QT isn't installed on the Windows machine...
        error = InitializeQTML(0);
        if (error!=noErr) {
            if (printErrors) {
                PsychErrorExitMsg(PsychError_internal, "Quicktime Media Layer initialization failed: Quicktime not properly installed?!?");
            } else return;

        }
#endif

        // Initialize Quicktime-Subsystem:
        error = EnterMovies();
        if (error!=noErr) {
            if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime EnterMovies() failed!!!"); else return;
        }
        firsttime = FALSE;
    }
    
    if (!PsychIsOnscreenWindow(win)) {
        if (printErrors) PsychErrorExitMsg(PsychError_user, "Provided windowPtr is not an onscreen window."); else return;
    }

    if (NULL==moviename) {
        if (printErrors) PsychErrorExitMsg(PsychError_internal, "NULL-Ptr instead of moviename passed!"); else return;
    }

    if (numMovieRecords >= PSYCH_MAX_MOVIES) {
        *moviehandle = -2;
        if (printErrors) PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open movies exceeded!"); else return;
    }

    // Search first free slot in movieRecordBANK:
    for (i=0; (i < PSYCH_MAX_MOVIES) && (movieRecordBANK[i].theMovie); i++);
    if (i>=PSYCH_MAX_MOVIES) {
        *moviehandle = -2;
        if (printErrors) PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open movies exceeded!"); else return;
    }

    // Slot slotid will contain the movie record for our new movie object:
    slotid=i;
    
    // Create name-string for moviename:
    movieLocation = CFStringCreateWithCString (kCFAllocatorDefault, moviename, kCFStringEncodingASCII);

    // Zero-out new record in moviebank:
    movieRecordBANK[slotid].theMovie=NULL;    
    movieRecordBANK[slotid].QTMovieContext=NULL;    
    movieRecordBANK[slotid].QTAudioContext=NULL;
    movieRecordBANK[slotid].QTMovieGWorld=NULL;
    
    if (!PSYCH_USE_QT_GWORLDS) {
        // Create QTGLTextureContext:
#if PSYCH_SYSTEM != PSYCH_WINDOWS
        error = QTOpenGLTextureContextCreate (kCFAllocatorDefault,
                                                    win->targetSpecific.contextObject,
                                                    win->targetSpecific.pixelFormatObject,
                                                    NULL,
                                                    &QTMovieContext);
#endif
        if (error!=noErr) {
            if (printErrors) PsychErrorExitMsg(PsychError_internal, "OpenGL Quicktime visual context creation failed!!!"); else return;
        }        
    }

    // The Movie location 
    newMovieProperties[propcount].propClass = kQTPropertyClass_DataLocation;
	if (strstr(moviename, "http:") || strstr(moviename, "ftp:")) {
		// Open movie from URL, e.g., http- or ftp- server:
		movieURLLocation = CFURLCreateWithString(kCFAllocatorDefault, movieLocation, NULL);
		newMovieProperties[propcount].propID = kQTDataLocationPropertyID_CFURL;
		newMovieProperties[propcount].propValueSize = sizeof(movieURLLocation);
		newMovieProperties[propcount++].propValueAddress = (void*) &movieURLLocation;
	}
	else {
		// Open movie file from filesystem:
		newMovieProperties[propcount].propID = kQTDataLocationPropertyID_CFStringPosixPath;
		newMovieProperties[propcount].propValueSize = sizeof(CFStringRef);
		newMovieProperties[propcount++].propValueAddress = &movieLocation;
    }
	
    if (!PSYCH_USE_QT_GWORLDS) {
        // The Movie visual context
        newMovieProperties[propcount].propClass = kQTPropertyClass_Context;
        newMovieProperties[propcount].propID = kQTContextPropertyID_VisualContext;
        newMovieProperties[propcount].propValueSize = sizeof(QTVisualContextRef);
        newMovieProperties[propcount++].propValueAddress = &QTMovieContext;
    }
    
    if (TRUE) {
        // Create QTAudioContext for default CoreAudio device:
        coreAudioDeviceUID = NULL; // Use default audio-output device.
        error =QTAudioContextCreateForAudioDevice (kCFAllocatorDefault,
                                                   coreAudioDeviceUID,
                                                   NULL,
                                                   &QTAudioContext);
        if (error!=noErr) {
            if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime audio context creation failed!!!"); else return;
        }
        
        // The Movie audio context
        newMovieProperties[propcount].propClass = kQTPropertyClass_Context;
        newMovieProperties[propcount].propID = kQTContextPropertyID_AudioContext;
        newMovieProperties[propcount].propValueSize = sizeof(QTAudioContextRef);
        newMovieProperties[propcount++].propValueAddress = &QTAudioContext;
    }
    
    // The Movie active
    newMovieProperties[propcount].propClass = kQTPropertyClass_NewMovieProperty;
    newMovieProperties[propcount].propID = kQTNewMoviePropertyID_Active;
    newMovieProperties[propcount].propValueSize = sizeof(trueValue);
    newMovieProperties[propcount++].propValueAddress = &trueValue;
    
    // Instantiate the Movie
    error = NewMovieFromProperties(propcount, newMovieProperties, 0, NULL, &theMovie);
    if (error!=noErr) {
        QTVisualContextRelease(QTMovieContext);
        QTAudioContextRelease(QTAudioContext);
        switch(error) {
            case -2000:
            case -50:
            case -43:
                sprintf(errdesc, "File not found.");
            break;
            
            case -2048:
                sprintf(errdesc, "This is not a file that Quicktime understands.");
            break;
            
            case -2003:
                sprintf(errdesc, "Can't find media handler (codec) for this movie.");
            break;
            
            default:
                sprintf(errdesc, "Unknown: Check http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes");
        }
        
        sprintf(msgerr, "Couldn't load movie %s! Quicktime error code %i [%s]", moviename, (int) error, errdesc);
        *moviehandle = (int) error;
        if (printErrors) PsychErrorExitMsg(PsychError_user, msgerr); else return;
    }
    
    CFRelease(movieLocation);

    if (PSYCH_USE_QT_GWORLDS) {
        // Determine size of images in movie:
        GetMovieBox(theMovie, &movierect);
        
		// Only create a GWorld if movie frames contain at least 1 pixel. This way we skip GWorld
		// setup on "movies" which only consist of sound tracks.
		if ((movierect.right - movierect.left != 0) && (movierect.bottom - movierect.top != 0)) {
			// Create GWorld for this movie object:
			// error = QTNewGWorld(&movieRecordBANK[slotid].QTMovieGWorld, k32ABGRPixelFormat, &movierect,  NULL, NULL, 0);
			error = QTNewGWorld(&movieRecordBANK[slotid].QTMovieGWorld, 0, &movierect,  NULL, NULL, 0);
			if (error!=noErr) {
				QTAudioContextRelease(QTAudioContext);
				DisposeMovie(movieRecordBANK[slotid].theMovie);
				movieRecordBANK[slotid].theMovie=NULL;    
				if (printErrors) PsychErrorExitMsg(PsychError_internal, "Quicktime GWorld creation failed!!!"); else return;
			}
			
			// Attach this GWorld as rendering target for Quicktime:
			SetMovieGWorld(theMovie, movieRecordBANK[slotid].QTMovieGWorld, NULL);
		}
    }
    
    // Preload preloadSecs seconds of movie into system RAM for faster playback:
	if (preloadSecs > 0) LoadMovieIntoRam(theMovie, 0, ((long) preloadSecs + 0.5) * GetMovieTimeScale(theMovie),  keepInRam);
	// Special setting - 1 means: Load whole movie into RAM:
	if (preloadSecs == -1) LoadMovieIntoRam(theMovie, 0, GetMovieDuration(theMovie),  keepInRam);

    // We don't preroll: Didn't help for async playback, but leads to failure in
    // manual playback mode: PrerollMovie(theMovie, 0, FloatToFixed(1));

    // MoviesTask() it to make sure start of plaback will be as stutter-free as possible:
    MoviesTask(theMovie, 10000);
    
    // Assign new record in moviebank:
    movieRecordBANK[slotid].theMovie=theMovie;    
    movieRecordBANK[slotid].QTMovieContext=QTMovieContext;    
    movieRecordBANK[slotid].QTAudioContext=QTAudioContext;
    movieRecordBANK[slotid].loopflag = 0;
    *moviehandle = slotid;

    // Increase counter:
    numMovieRecords++;

    // Compute basic movie properties - Duration and fps as well as image size:
    
    // Compute duration in seconds:
    movieRecordBANK[slotid].movieduration = (double) GetMovieDuration(theMovie) / (double) GetMovieTimeScale(theMovie);

    // Compute expected framerate, assuming a linear spacing between frames: It is derived as
    // reciprocal of the duration of the first video frame in the movie:
    movieRecordBANK[slotid].fps = PsychDetermineMovieFramecountAndFps(theMovie, NULL);

    // Determine size of images in movie:
    GetMovieBox(theMovie, &movierect);
    movieRecordBANK[slotid].width = movierect.right - movierect.left;
    movieRecordBANK[slotid].height = movierect.bottom - movierect.top;
    
    // We set nrframes == -1 to indicate that this value is not yet available.
    // Will do counting on first query for this parameter as it is very time-consuming:
    movieRecordBANK[slotid].nrframes = -1;
    
    return;
}
        CGLPixelFormatAttribute pfa[attributeCount];
        pfa[0] = kCGLPFAWindow;
        pfa[1] = (CGLPixelFormatAttribute) 0;
        err = CGLChoosePixelFormat(pfa, &cglPixelFormat, &attributeCount);
        BACKEND_ASSERT3(err == noErr, "Could not create pixel format (OpenGL)", FATAL_ERROR, false)
    }

    CFTypeRef keys[] = { kQTVisualContextWorkingColorSpaceKey };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault,
        (const void **)keys,
        (const void **)&colorSpace, 1,
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks);
        
	err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext,
        cglPixelFormat, textureContextAttributes, &m_visualContext);
    CFRelease(textureContextAttributes);
    BACKEND_ASSERT3(err == noErr, "Could not create visual context (OpenGL)", FATAL_ERROR, false)

    if (m_movieRef && m_visualContext)
        SetMovieVisualContext(m_movieRef, m_visualContext);
    return true;    
}

bool QuickTimeVideoPlayer::videoFrameChanged(const LinkTimeProxy &timeStamp)
{
    if (!m_movieRef || m_state != Playing || !m_hasVideo)
        return false;

    if (!m_visualContext)
        setGLContext(PhononSharedQGLWidget()->context());