///////////////////////////////////////////////////////////
//
//  IMPLEMENTATION
//
//  CONTEXT: Genesis3D lightweight framework
//  MODULE:	 general MCI player, here just used for MIDI
//  PREREQ:	 mainviewcontroller.h
//  FILE:	 mci.cpp
//	AUTHOR:  Klaus Zerbe <klaus@zerbe.com>
//  ORIGIN:  http://klaus.zerbe.com
//  VERSION: 1.0
//  DATE:    3.2.1999
//  PURPOSE: play music and maybe larger WAVE files or CD-audio
//           can be easily extended for playing videos etc.
//
///////////////////////////////////////////////////////////


#define VC_EXTRALEAN

#include <windows.h>
#include <mmsystem.h>
#include "mci.h"
#include "mainviewcontroller.h"

// notification window class registered?
bool CMCI_Player::bWndClassRegistered= false;

// midi player singleton
CMCI_Player* CMCI_Player::m_pMidiPlayer= NULL;

// prepare MCI player stuff
CMCI_Player::CMCI_Player()
{
	LPSTR pClassName= "MCICALLBACK";
	HINSTANCE hInst= CMainViewController::GetInstance();
	// if notification window class not registered, just do it
	if (!bWndClassRegistered) {
		WNDCLASS wndClass;
		wndClass.style= CS_HREDRAW | CS_VREDRAW;
		wndClass.lpfnWndProc= MciWndProc;
		wndClass.cbClsExtra= 0;
		wndClass.cbWndExtra= 0;
		wndClass.hInstance= hInst;
		wndClass.hIcon= NULL;
		wndClass.hCursor= NULL;
		wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
		wndClass.lpszMenuName= NULL;
		wndClass.lpszClassName= pClassName;
		if (!RegisterClass(&wndClass)) throw "MCI window registration failed";
		bWndClassRegistered= true;
	}
	// create an invisible window
	m_hWnd= CreateWindowEx(WS_EX_APPWINDOW, pClassName, "",
		WS_DISABLED, 0, 0, 1, 1, NULL, NULL, hInst, this);
	if (!m_hWnd) throw "cannot open MCI window";
	// connect window with CMCI_Player instance
	SetWindowLong(m_hWnd,GWL_USERDATA,(LONG)this);
	// init variables
	state= stopped;
	m_deviceID= 0;
	m_dwFrom= m_dwTo= 0;
	m_bContinous= false;
	m_bEnabled= false;
	m_pPlayItem= NULL;
}

CMCI_Player::~CMCI_Player() 
{
	// destroy file name buffer
	if (m_pPlayItem) delete m_pPlayItem;
	// make sure music stops
	Close();
	// avoid callbacks after destruction
	SetWindowLong(m_hWnd,GWL_USERDATA,(LONG)0);
}

CMCI_Player* CMCI_Player::GetMidiPlayer()
{
	// lazy singleton creation
	if (!m_pMidiPlayer) m_pMidiPlayer= new CMCI_Player();
	return m_pMidiPlayer;
}

// open file to play
void CMCI_Player::Open(LPSTR pFileName)
{
	DWORD dw;
	MCI_OPEN_PARMS openParams;
	MCI_SET_PARMS setParams;
    openParams.lpstrDeviceType = NULL;
    openParams.lpstrElementName = pFileName;
	state= stopped;
    dw= mciSendCommand(0, MCI_OPEN, MCI_WAIT|MCI_OPEN_ELEMENT,
       (DWORD)(LPVOID)&openParams);
	if (dw!=0) { // failed!
		m_deviceID= 0;
		return;
	}
	// success!
	m_deviceID= openParams.wDeviceID;
	// set time format to milliseconds
	setParams.dwTimeFormat= MCI_FORMAT_MILLISECONDS;
	dw= mciSendCommand(m_deviceID, MCI_SET, MCI_WAIT|MCI_SET_TIME_FORMAT,
       (DWORD)(LPVOID)&setParams);
}

// close played file
void CMCI_Player::Close()
{
    MCI_GENERIC_PARMS gp;
    if (m_deviceID == 0) return; 
    mciSendCommand(m_deviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPVOID)&gp);
	m_deviceID= 0;
	state= stopped;
	m_bContinous= false;
}

// play it again sam (or continous)
void CMCI_Player::Play(bool bContinous)
{
	DWORD dw, dwDuration;
	MCI_PLAY_PARMS m_PlayParams;
	// exit if not prepared or currently playing
    if (m_deviceID == 0) return;
	if (state==playing) return;
	// set play interval
	dwDuration= GetDuration();
	if (m_dwTo > dwDuration) m_dwTo= dwDuration;
	// play and notify m_hWnd when ready
    m_PlayParams.dwCallback= (ULONG)m_hWnd;
    m_PlayParams.dwFrom= m_dwFrom; 
    m_PlayParams.dwTo= m_dwTo ? m_dwTo : dwDuration; 
	dw= MCI_NOTIFY | MCI_FROM | MCI_TO;
    dw= mciSendCommand(m_deviceID, MCI_PLAY, dw,
		(DWORD)(LPVOID)&m_PlayParams);
	if (dw==0) {  // success
		state= playing; m_bContinous= bContinous;
	} else { // failed
		state= stopped; m_bContinous= false; 
	}
}

// stop playing
void CMCI_Player::Stop()
{
	DWORD dw;
	// exit if already stopped
	if (!(m_deviceID && (state==playing || state==pausing))) return;
    MCI_GENERIC_PARMS gp;
    dw= mciSendCommand(m_deviceID, MCI_STOP, MCI_WAIT, (DWORD)(LPVOID)&gp);
	if (dw==0) state= stopped;
}

// pause playing
void CMCI_Player::Pause()
{
	DWORD dw;
	if (!(m_deviceID && state==playing)) return;
    MCI_GENERIC_PARMS gp;
    dw= mciSendCommand(m_deviceID, MCI_PAUSE, MCI_WAIT, (DWORD)(LPVOID)&gp);
	if (dw==0) state= pausing;
}

// get notified and restart if continous play
void CMCI_Player::Notify(WORD wFlags)
{
	m_wNotification= wFlags;
	// not interested in errors here
	if (wFlags!=MCI_NOTIFY_SUCCESSFUL) return;
	if (m_bContinous) {
		// play again
		SetPosition(0); Play(m_bContinous);
	} else {
		// play just once, so stop
		Close();
		state= ready;
	}
}

// get piece duration
DWORD CMCI_Player::GetDuration()
{
	DWORD dw;
	if (!m_deviceID) return 0;
	MCI_STATUS_PARMS parms;
	parms.dwItem= MCI_STATUS_LENGTH;
    dw= mciSendCommand(m_deviceID, MCI_STATUS, MCI_WAIT|MCI_STATUS_ITEM, 
		(DWORD)(LPVOID)&parms);
	return (dw==0) ? parms.dwReturn : 0;
}

//  notification window function
//  you may extend this to a full video player easily
long CALLBACK MciWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	CMCI_Player *pMci;
	// get instance
	pMci= (CMCI_Player*)GetWindowLong(hWnd, GWL_USERDATA);
	if (pMci) {
		switch (msg) {
			case WM_CLOSE:
				// destroy window on destruction
				DestroyWindow(hWnd); 
				return 0;
			case MM_MCINOTIFY:
				// notify related instance
				pMci->Notify(wParam);
				return 0;
		}
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}
