//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

#include "stdwin.h"
#include <evcode.h>
#include "cplay.h"
#include "file.h"
#include "media.h"
#include "toolbar.h"
#include "resource.h"

// Current multimedia variables
static Media media;


//
// CanPlay
//
// Return true if we can go to a playing state from our current state
//
BOOL CanPlay()
{
    return (media.state == Stopped || media.state == Paused);
}


//
// CanStop
//
// Return true if we can go to a stopped state from our current state
//
BOOL CanStop()
{
    return (media.state == Playing || media.state == Paused);
}


//
// CanPause
//
// Return true if we can go to a paused state from our current state
//
BOOL CanPause()
{
    return (media.state == Playing || media.state == Stopped);
}


//
// IsInitialized
//
// Return true if we have loaded and initialized a multimedia file
//
BOOL IsInitialized()
{
    return (media.state != Uninitialized);
}


//
// ChangeStateTo
//
void ChangeStateTo( State newState )
{
    media.state = newState;

    // update the toolbar
    UpdateToolbar();
}


//
// InitMedia
//
// Initialization
//
BOOL InitMedia()
{
    ChangeStateTo( Uninitialized );

    media.hGraphNotifyEvent = NULL;
    media.pGraph = NULL;

    return TRUE;
}


//
// CreateFilterGraph
//
BOOL CreateFilterGraph()
{
    IMediaEvent *pME;
    HRESULT hr;

    ASSERT(media.pGraph == NULL);

    hr = CoCreateInstance(&CLSID_FilterGraph,           // CLSID of object
			  NULL,                         // Outer unknown
			  CLSCTX_INPROC_SERVER,         // Type of server
			  &IID_IGraphBuilder,           // Interface wanted
			  (void **) &media.pGraph);     // Returned object
    if (FAILED(hr)){
	media.pGraph = NULL;
	return FALSE;
    }

    // We use this to find out events sent by the filtergraph

    hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaEvent, (void **) &pME);
    if (FAILED(hr)) {
	DeleteContents();
	return FALSE;
    }

    hr = pME->lpVtbl->GetEventHandle(pME, (OAEVENT*) &media.hGraphNotifyEvent);
    pME->lpVtbl->Release( pME );

    if (FAILED(hr)) {
	DeleteContents();
	return FALSE;
    }

    return TRUE;

} // CreateFilterGraph


// Destruction
//
// DeleteContents
//
void DeleteContents()
{
    if (media.pGraph != NULL) {
	media.pGraph->lpVtbl->Release( media.pGraph );
	media.pGraph = NULL;
    }

    // this event is owned by the filter graph and is thus invalid
    media.hGraphNotifyEvent = NULL;

    ChangeStateTo( Uninitialized );
}


//
// RenderFile
//
BOOL RenderFile( LPSTR szFileName )
{
    HRESULT hr;
    WCHAR wPath[MAX_PATH];

    DeleteContents();

    if ( !CreateFilterGraph() ) {
	PlayerMessageBox( IDS_CANT_INIT_QUARTZ );
	return FALSE;
    }

    MultiByteToWideChar( CP_ACP, 0, szFileName, -1, wPath, MAX_PATH );

    SetCursor( LoadCursor( NULL, IDC_WAIT ) );
    hr = media.pGraph->lpVtbl->RenderFile(media.pGraph, wPath, NULL);
    SetCursor( LoadCursor( NULL, IDC_ARROW ) );

    if (FAILED( hr )) {
	PlayerMessageBox( IDS_CANT_RENDER_FILE );
	return FALSE;
    }
    return TRUE;

} // RenderFile


//
// SetTitle
//
// Update the title of the video renderer to "Player - szFile"
//
void SetTitle( HWND hwnd, char *szFile )
{
    char szNewTitle[ _MAX_FNAME + _MAX_EXT  + 20 ];

    strcpy( szNewTitle, APP_NAME );
    strcat( szNewTitle, " - " );
    strcat( szNewTitle, szFile );

    // Update the window's title
    SetWindowText( hwnd, szNewTitle );

} // SetTitle


//
// OpenMediaFile
//
// File..Open has been selected
//
void OpenMediaFile( HWND hwnd, LPSTR szFile )
{
    static char szFileName[ _MAX_PATH ];
    static char szTitleName[ _MAX_FNAME + _MAX_EXT ];

    if( szFile != NULL && RenderFile( szFile ))
    {
	LPSTR szTitle;

	// Work out the full path name and the file name from the
	// specified file
	GetFullPathName( szFile, _MAX_PATH, szFileName, &szTitle );
	strncpy( szTitleName, szTitle, _MAX_FNAME + _MAX_EXT );
	szTitleName[ _MAX_FNAME + _MAX_EXT -1 ] = '\0';

	// Set the main window title and update the state
	SetTitle( hwnd, szTitleName );
	ChangeStateTo( Stopped );

    } else if( DoFileOpenDialog( hwnd, szFileName, szTitleName )
	       && RenderFile( szFileName ) ){

	// Set the main window title and update the state
	SetTitle( hwnd, szTitleName );
	ChangeStateTo( Stopped );
    }

} // OpenMediaFile


//
// OnMediaPlay
//
// There are two possible UI strategies for an application: you either play
// from the start each time you stop, or you play from the current position,
// in which case you have to explicitly rewind at the end. If you want the
// play from current and rewind at end, enable this code, if you want the
// other option, then enable FROM_START in both OnMediaStop and OnAbortStop.

#undef REWIND
#define FROM_START

void OnMediaPlay()
{
    if( CanPlay() ){
	HRESULT hr;
	IMediaControl *pMC;

	// Obtain the interface to our filter graph
	hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

	if( SUCCEEDED(hr) ){
#ifdef REWIND
	    IMediaPosition * pMP;
	    hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
		                                      &IID_IMediaPosition,
		                                      (void**) &pMP);
	    if (SUCCEEDED(hr)) {
		// start from last position, but rewind if near the end
		REFTIME tCurrent, tLength;
		hr = pMP->lpVtbl->get_Duration(pMP, &tLength);
		if (SUCCEEDED(hr)) {
		    hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent);
		    if (SUCCEEDED(hr)) {
			// within 1sec of end? (or past end?)
			if ((tLength - tCurrent) < 1) {
			    pMP->lpVtbl->put_CurrentPosition(pMP, 0);
			}
		    }
		}
		pMP->lpVtbl->Release(pMP);
	    }
#endif
	    // Ask the filter graph to play and release the interface
	    hr = pMC->lpVtbl->Run( pMC );
	    pMC->lpVtbl->Release( pMC );

	    if( SUCCEEDED(hr) ){
		ChangeStateTo( Playing );
		return;
	    }
	}

	// Inform the user that an error occurred
	PlayerMessageBox( IDS_CANT_PLAY );

    }

} // OnMediaPlay


//
// OnMediaPause
//
void OnMediaPause()
{
    if( CanPause() ){
	HRESULT hr;
	IMediaControl *pMC;

	// Obtain the interface to our filter graph
	hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

	if( SUCCEEDED(hr) ){
	    // Ask the filter graph to pause and release the interface
	    hr = pMC->lpVtbl->Pause( pMC );
	    pMC->lpVtbl->Release( pMC );

	    if( SUCCEEDED(hr) ){
		ChangeStateTo( Paused );
		return;
	    }
	}

	// Inform the user that an error occurred
	PlayerMessageBox( IDS_CANT_PAUSE );
    }

} // OnMediaPause


//
// OnMediaAbortStop
//
// Called when we get an EC_USER_ABORT or EC_ERROR_ABORT event
//
void OnMediaAbortStop(void)
{
    if(CanStop())
    {
	HRESULT hr;
	IMediaControl *pMC;

	// Obtain the interface to our filter graph
	hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);

	if( SUCCEEDED(hr) ){
#ifdef FROM_START
	    IMediaPosition * pMP;
#endif
	    // Ask the filter graph to stop and release the interface
	    hr = pMC->lpVtbl->Stop( pMC );
	    pMC->lpVtbl->Release( pMC );

#ifdef FROM_START

	    // if we want always to play from the beginning
	    // then we should seek back to the start here
	    // (on app or user stop request, and also after EC_COMPLETE).
	    hr = media.pGraph->lpVtbl->QueryInterface(
			media.pGraph,
			&IID_IMediaPosition,
			(void**) &pMP);
	    if (SUCCEEDED(hr)) {
		pMP->lpVtbl->put_CurrentPosition(pMP, 0);
		pMP->lpVtbl->Release(pMP);
	    }

	    // no visible rewind or we will re-show the window!

#endif

	    if( SUCCEEDED(hr) ){
		ChangeStateTo( Stopped );
		return;
	    }
	}
	// Inform the user that an error occurred
	PlayerMessageBox( IDS_CANT_STOP );
    }

} // OnMediaAbortStop


//
// OnMediaStop
//
// There are two different ways to stop a graph. We can stop and then when we
// are later paused or run continue from the same position. Alternatively the
// graph can be set back to the start of the media when it is stopped to have
// a more CDPLAYER style interface. These are both offered here conditionally
// compiled using the FROM_START definition. The main difference is that in
// the latter case we put the current position to zero while we change states
//
void OnMediaStop()
{
    if( CanStop() ){
	HRESULT hr;
	IMediaControl *pMC;

	// Obtain the interface to our filter graph
	hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaControl, (void **) &pMC);
	if( SUCCEEDED(hr) ){

#ifdef FROM_START
	    IMediaPosition * pMP;
	    OAFilterState state;

	    // Ask the filter graph to pause
	    hr = pMC->lpVtbl->Pause( pMC );

	    // if we want always to play from the beginning
	    // then we should seek back to the start here
	    // (on app or user stop request, and also after EC_COMPLETE).
	    hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
						      &IID_IMediaPosition,
						      (void**) &pMP);
	    if (SUCCEEDED(hr)) {
		pMP->lpVtbl->put_CurrentPosition(pMP, 0);
		pMP->lpVtbl->Release(pMP);
	    }

	    // wait for pause to complete
	    pMC->lpVtbl->GetState(pMC, INFINITE, &state);
#endif

	    // now really do the stop
	    pMC->lpVtbl->Stop( pMC );
	    pMC->lpVtbl->Release( pMC );
	    ChangeStateTo( Stopped );
	    return;
	}

	// Inform the user that an error occurred
	PlayerMessageBox( IDS_CANT_STOP );
    }

} // OnMediaStop


//
// GetGraphEventHandle
//
// We use this to check for graph events
//
HANDLE GetGraphEventHandle()
{
    return media.hGraphNotifyEvent;

} // GetGraphEventHandle


//
// OnGraphNotify
//
// If the event handle is valid, then ask the graph if
// anything has happened (eg the graph has stopped...)
//
void OnGraphNotify()
{
    IMediaEvent *pME;
    long lEventCode, lParam1, lParam2;

    ASSERT( media.hGraphNotifyEvent != NULL );

    if( SUCCEEDED(media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaEvent, (void **) &pME))){
	if( SUCCEEDED(pME->lpVtbl->GetEvent(pME, &lEventCode, &lParam1, &lParam2, 0))) {
	    if (lEventCode == EC_COMPLETE) {
		OnMediaStop();
	    } else if ((lEventCode == EC_USERABORT) ||
		       (lEventCode == EC_ERRORABORT)) {
		OnMediaAbortStop();
	    }
	}
	pME->lpVtbl->Release( pME );
    }
}

