//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// cavi.cpp
//
//		This file contains some basic code to play and display AVI files
//		It implements a CAvi class which can be used to display video
//		for windows files.
//		This class calls back java in two function (see AviPlayer.java
//		and AviObserver.java) to notify when a stop or a 
//		position status change occurred
//
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.

#ifndef __CAVI_H__
#include "cavi.h"
#endif

#ifndef _INC_MMSYSTEM
#include <mmsystem.h>
#endif

#ifdef WIN32
    #include <digitalv.h>
#endif

#include "resource.h"

#ifndef _AviPlayer_H_
#include "AVIPlayer.h"
#endif
#ifndef _AviObserver_H_
#include "AVIObserver.h"
#endif

extern HINSTANCE g_hDllInstance;

// static counter for initialize the timer
UINT CAvi::s_InstanceCount = 0;

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
//
// CAvi constructor and destructor
//
CAvi::CAvi (BOOL autoStart, BOOL bLoop, NPP instance)
{
	_pluginInstance = instance;
    _mDeviceID = 0;
	_hMovieWnd = 0;

	_bLoop = bLoop;
	_bAutoStart = autoStart;
    _bPlaying = FALSE;

	_uTimeOut = 0;
	_uTimerID = ++s_InstanceCount;
}

CAvi::~CAvi ()
{
	Close();
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Open 
//
//	Open the avi file for playback and display the first frame. 
//
BOOL CAvi::Open (HWND hWnd, LPCSTR Filename)
{
    DWORD RetCode;  
    MCI_DGV_OPEN_PARMS OpenParms;
    MCI_DGV_WINDOW_PARMS WindowParms;

    // Close any device that is already open.
    if (_mDeviceID){
        Close ();
    }

    // Open a device for playback.
    OpenParms.dwCallback = 0;
    OpenParms.wDeviceID = 0;
    OpenParms.lpstrDeviceType = NULL;
    OpenParms.lpstrElementName = (char*)Filename;
    OpenParms.lpstrAlias = 0;
    OpenParms.dwStyle = WS_CHILD | WS_VISIBLE;
    OpenParms.hWndParent = hWnd;
    if (RetCode = mciSendCommand(0, MCI_OPEN, 
									(DWORD) MCI_OPEN_ELEMENT | 
											MCI_DGV_OPEN_PARENT | 
											MCI_DGV_OPEN_WS, 
									(DWORD)(LPVOID)&OpenParms)) {
        char szBuf[256];
        char szError[128];
        mciGetErrorString(RetCode, szBuf, 256);
        ::LoadString(g_hDllInstance, MCI_ERROR_OPEN, szError, 128);
        MessageBox(NULL, szBuf, szError /*"AVI Plugin: Error Opening Device!"*/, MB_OK);
        return FALSE;
    }
    // The device was opened, cache the device ID.
    _mDeviceID = OpenParms.wDeviceID;

    // set and cache the AVI window handle 
	WindowParms.dwCallback = 0;
    WindowParms.hWnd = hWnd;
    WindowParms.nCmdShow = SW_SHOW;
    WindowParms.lpstrText = (LPSTR) NULL;
    if (RetCode = mciSendCommand (_mDeviceID, 
									MCI_WINDOW, 
									MCI_DGV_WINDOW_HWND, 
									(DWORD)(LPVOID)&WindowParms)) {
        return FALSE;
    }
    _hMovieWnd = WindowParms.hWnd;

	// start playing if auto start defined
	if (_bAutoStart)
		// win95 does not seem to like a direct call to play
		// but a post message will work
		::PostMessage(_hMovieWnd, WM_LBUTTONDOWN, 0, 0L);
		//Play();

    return TRUE;
}


//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Close 
//
//	Stop the avi file that may be playing and close the device ID
//
void CAvi::Close (void)
{
    // Closing a device ID will stop the video playback.
    if (_mDeviceID)
        mciSendCommand (_mDeviceID, MCI_CLOSE, 0L, NULL);

    _bPlaying = FALSE;
	_mDeviceID = 0;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Play
//
//	Start the video playback. 
//	This function immediately returns control back to the program.
//	Call back OnStop when stopped (see CPluginWnd::PluginWndProc in plgwnd.cpp
//	and CAvi::OnStop in this file)
//
BOOL CAvi::Play ()
{
    DWORD RetCode, dwFlags = MCI_NOTIFY;	// notify a window proc when 
											// the play status change (like stop)
    MCI_DGV_PLAY_PARMS PlayParms;
    
	// if no device open return
	if (!_mDeviceID)
		return FALSE;

    // Start playback using the MCI_PLAY command.
    PlayParms.dwCallback = (DWORD)_hMovieWnd; // window proc that recives the notification
    PlayParms.dwFrom = PlayParms.dwTo = 0;

#ifdef WIN32
    if (_bLoop) 
		dwFlags = MCI_DGV_PLAY_REPEAT;
#endif
    if (RetCode = mciSendCommand (_mDeviceID, 
									MCI_PLAY, 
									dwFlags, 
									(DWORD)(LPVOID)&PlayParms)) {
        char szBuf[256];
        char szError[128];
        mciGetErrorString(RetCode,szBuf,256);
        ::LoadString(g_hDllInstance, MCI_ERROR_PLAY, szError, 128);
        MessageBox(NULL, szBuf, szError /*"MCI Play Error"*/, MB_OK);

        return FALSE;
    }

	// start the timer so we can track down the position 
	if (_uTimeOut)
		SetTimer(_hMovieWnd, _uTimerID, _uTimeOut, NULL);
    
	_bPlaying = TRUE;
    
	return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Stop
//
//	Pause a video. 
//
BOOL CAvi::Stop (void)
{
    // Stop playback 
	if (_mDeviceID && mciSendCommand (_mDeviceID, MCI_STOP, 0L, NULL)) {
        return FALSE;
    }

    _bPlaying = FALSE;
    return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Seek
//
//	Seek the video at a specified position
//
BOOL CAvi::Seek (ULONG dwSeekPosition)
{
	MCI_SEEK_PARMS seekParams;
	seekParams.dwTo = dwSeekPosition;

    if (_mDeviceID && mciSendCommand(_mDeviceID, 
										MCI_SEEK, 
										MCI_TO, 
										(DWORD)(LPVOID)&seekParams)) {
        return FALSE;
    }

    _bPlaying = FALSE;
    return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Rewind
//
//	Rewind the video to the beginning and display the first frame.
//
BOOL CAvi::Rewind (void)
{
    // Use the MCI_SEEK command to return to the beginning of the file.
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
							MCI_SEEK, 
							MCI_SEEK_TO_START, 
							NULL)) {
        return FALSE;
    }

    _bPlaying = FALSE;
    return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Forward
//
//	Forward the video to the end and display the last frame.
//
BOOL CAvi::Forward (void)
{
    // Use the MCI_SEEK command to go to the end of the file.
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
							MCI_SEEK, 
							MCI_SEEK_TO_END, 
							NULL)) {
        return FALSE;
    }

    _bPlaying = FALSE;
	// I know, I know you won't believe it but MCI it's not always
	// positioning the avi at the end of the stream with a seek command
	// but if I seek first, I got back one frame and
	// forward another one, the magic happens.
	// Don't beat me up!
	FrameBack();
	FrameForward();
    return TRUE; 
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// FrameForward
//
//	Forward the video by one frame.
//
BOOL CAvi::FrameForward (void)
{
	MCI_DGV_STEP_PARMS StepParms;

	StepParms.dwFrames = 1L;
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
						MCI_STEP, 
						MCI_DGV_STEP_FRAMES, 
						(DWORD)(LPVOID)&StepParms)) {
        return FALSE;
    }

    _bPlaying = FALSE;
    return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// FrameBack
//
//	Reverse the video by one frame.
//
BOOL CAvi::FrameBack (void)
{
	MCI_DGV_STEP_PARMS StepParms;

    // Use MCI_STEP to move back one frame.
	StepParms.dwFrames = 1L;
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
						MCI_STEP, 
						MCI_DGV_STEP_REVERSE, 
						(DWORD)(LPVOID)&StepParms)) {
        return FALSE;
        }

    _bPlaying = FALSE;
    return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// GetLength
//
//	Return the Avi's length in frames
//
DWORD CAvi::GetLength (void)
{
    MCI_STATUS_PARMS StatusParms;

    // Determine the length in frames of the file.
    StatusParms.dwItem = MCI_STATUS_LENGTH;
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
						MCI_STATUS, 
						MCI_STATUS_ITEM, 
						(DWORD)(LPVOID)&StatusParms)) {
        return FALSE;
    }
    return StatusParms.dwReturn;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// GetPosition
//
//	Return the Avi's position in frames
//
DWORD CAvi::GetPosition (void)
{
    MCI_STATUS_PARMS StatusParms;
   
    // Determine the current position of the stream.
    StatusParms.dwItem = MCI_STATUS_POSITION;
    if (_mDeviceID && mciSendCommand (_mDeviceID, 
						MCI_STATUS, 
						MCI_STATUS_ITEM, 
						(DWORD)(LPVOID)&StatusParms)) {
        return FALSE;
    }
    return StatusParms.dwReturn;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// GetWidth
//
//	Return the width of the video window in pixels.
//
int CAvi::GetWidth (void)
{
	MCI_DGV_RECT_PARMS RectParms;
   
    // Use MCI_WHERE to get the video window rectangle.
	if (_mDeviceID && mciSendCommand (_mDeviceID, 
										MCI_WHERE, 
										MCI_DGV_WHERE_SOURCE, 
										(DWORD)(LPVOID)&RectParms)) {
		return 0;
	}

    return RectParms.rc.right;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// GetHeight
//
//	Return the height of the video window in pixels.
//
int CAvi::GetHeight (void)
{
	MCI_DGV_RECT_PARMS RectParms;
   
    // Use MCI_WHERE to get the video window rectangle.
	if (_mDeviceID && mciSendCommand (_mDeviceID, 
										MCI_WHERE, 
										MCI_DGV_WHERE_SOURCE, 
										(DWORD)(LPVOID)&RectParms)) {
		return FALSE;
	}

    return RectParms.rc.bottom;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Center
//
//	This is a private function that simply positions the video window in the
//	center of the parent window. 
//
BOOL CAvi::Center (void)
{
   CRect BoundsRect, MovieRect, WindowRect;
   MCI_DGV_RECT_PARMS RectParms;
   
   // return false if no device open
   if (!_mDeviceID)
	   return FALSE;

   // Use MCI_WHERE to get the video window rectangle.
   if (mciSendCommand (_mDeviceID, 
						MCI_WHERE, 
						MCI_DGV_WHERE_SOURCE, 
						(DWORD)(LPVOID)&RectParms))
        return FALSE;

   // Get size of the playback window.
   BoundsRect = RectParms.rc;
   MovieRect.left = 0;
   MovieRect.top = 0;
   MovieRect.right = MovieRect.left + BoundsRect.right;
   MovieRect.bottom = MovieRect.top + BoundsRect.bottom;
   
   ::GetWindowRect(_hMovieWnd, &WindowRect);

   // Move the playback window.
   MoveWindow (_hMovieWnd, (WindowRect.Width() - MovieRect.Width())/2, 
                          (WindowRect.Height() - MovieRect.Height())/2, 
                          BoundsRect.right, 
						  BoundsRect.bottom, 
						  TRUE);

   return TRUE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Update 
//
//	
//
void CAvi::Update ()
{
    MCI_DGV_UPDATE_PARMS UpdateParams;
    BOOL result = FALSE;
    char szErrorText[MAX_PATH];

    szErrorText[0] = NULL;

    UpdateParams.dwCallback = 0;
    UpdateParams.hDC = ::GetDC(_hMovieWnd);
    if (_mDeviceID)
        mciSendCommand (_mDeviceID, 
							MCI_UPDATE, 
							MCI_DGV_UPDATE_HDC, 
							(DWORD)(LPVOID)&UpdateParams);

    ::ReleaseDC(_hMovieWnd, UpdateParams.hDC);

    //_mOpen = FALSE;
    //_bPlaying = FALSE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// Realize
//
//
BOOL 
CAvi::Realize (void)
{
    // plugins must realize their palette as a background palette
	if (_mDeviceID)
		return !mciSendCommand (_mDeviceID, 
									MCI_REALIZE, 
									MCI_DGV_REALIZE_BKGD, 
									NULL);
    return FALSE;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// SetFrequency
//
//	Set the timer time-out to update position value
//
void 
CAvi::SetFrequency(UINT uTimer)
{
	if (_bPlaying && _uTimeOut)
		KillTimer(_hMovieWnd, _uTimerID);
		
	_uTimeOut = uTimer;
		
	if (_uTimeOut && _bPlaying)
		 SetTimer(_hMovieWnd, _uTimerID, _uTimeOut, 0);
}


//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// OnStop
//
//	Notification that a stop event occurred
//	Stop events should leave from here
//	Assume a java class is listening for an event to happen (see AviPlayer.java)
//	If no listener is passed to the java class we don't fire the event.
//	
//
void 
CAvi::OnStop()
{
	AviPlayer* javaAviInst;
	JRIEnv* env;
	AviObserver* observer = 0;
	// load the java instance representing the plugin instance
	javaAviInst = (AviPlayer*)NPN_GetJavaPeer(_pluginInstance);
	if (javaAviInst) {
		env = NPN_GetJavaEnv();
		// find the listener if any (observer on AviPlayer java class; AviPlayer.java)
		observer = javaAviInst->getObserver(env);
	}

	// if time out is non zero we setted a timer on play.
	if (_uTimeOut) {
		KillTimer(_hMovieWnd, _uTimerID);
		if (observer)
			observer->onPositionChange(env, GetPosition());
	}

	// if a java class is waiting for the event, fire it.
	// JAVA side:
	// This java class needs to implement the AviObserver interface
	// and register itself thru the advise function (see AviPlayer.java)
	if (observer)
		observer->onStop(env);

	_bPlaying = FALSE;
}


//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
// OnPoisitionChange
//
//	Notification that the position changed
//	PositionChange events should leave from here
//	(see comment above)
//
void
CAvi::OnPositionChange()
{
	AviPlayer* javaAviInst;
	JRIEnv* env;
	AviObserver* observer = 0;
	javaAviInst = (AviPlayer*)NPN_GetJavaPeer(_pluginInstance);
	if (javaAviInst) {
	env = NPN_GetJavaEnv();
		observer = javaAviInst->getObserver(env);
		if (observer)
			observer->onPositionChange(env, GetPosition());
	}
}
