//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: odother
//
//   CLASSES: none
//
//   ORIGINS: 27
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//
// @(#) 1.2 nt/src/samples/cdplayer/cdcntrl.cpp, odcdplayerpart, od96nt, odnt9712d 11/5/96 17:11:48 [ 3/21/97 17:32:46 ]

/*-------------------------------------------------------------------+
|                            CDControl.C                             |
| ================================================================== |
|                                                                    |
| Description:  CD Control implementation                            |
|                                                                    |
| Author:       David Kuettel, Summer 1996                           |
|                                                                    |
+-------------------------------------------------------------------*/
#ifndef IBMSample_CDCONTROL
#include "CDCntrl.h"
#endif

#ifndef IBMSample_CDREGISTRY
#include "CDReg.h"
#endif

#include <stdio.h>

const char* StatusMessage [] = {
	"Please insert CD...",		// MSG_INSERT
	"Playing",					// MSG_PLAYING
	"Stopped",					// MSG_STOPPED
	"Paused",					// MSG_PAUSED
	"Ejecting",					// MSG_EJECTING
	"Loading",					// MSG_LOADING
} ;

#include <qprintf.h>


/*-------------------------------------------------------------------+
|                      CDControl::CDControl ()                       |
| ================================================================== |
|                                                                    |
| Function:    CDControl default constructor                         |
|                                                                    |
| Algorithm:   reset current track & offset                          |
|                                                                    |
+-------------------------------------------------------------------*/
CDControl::CDControl (HWND windowHandle) :
	hWnd (windowHandle), fState (kNoMedia), fIndex (0), fAlbum (NULL)
{
	DWORD rc;

	rc = device.Open ();
	if (rc == MMSYSERR_NOERROR) {
		device.SetNotification (hWnd);
		device.Stop ();

		cdLoad ();
		cdStop ();

		SetTimer (hWnd, 1, CDControl::kTimeout, NULL);
	} else {
		char errorMessage [256];
		mciGetErrorString (rc, errorMessage, 255);
		qprintf ("CD Device Error [%d]: %s\n", rc, errorMessage);
		fState = kNoDrive;
	}
}


/*-------------------------------------------------------------------+
|                     CDControl::~CDControl ()                       |
| ================================================================== |
|                                                                    |
| Function:    CDControl destructor...                               |
|                                                                    |
+-------------------------------------------------------------------*/
CDControl::~CDControl ()
{
	KillTimer (hWnd, 1);

	cdStop ();
	device.Close ();
}


/*-------------------------------------------------------------------+
|                       CDControl::Poll ()                           |
| ================================================================== |
|                                                                    |
| Function:    Poll...                                               |
|                                                                    |
| Algorithm:   Previous state                                        |
|                  kNoMedia:                                         |
|                      o  Try and load the CD (cdLoad)               |
|                                                                    |
|                  kStopped:                                         |
|                  kPlaying:                                         |
|                  kPaused:                                          |
|                      o  Nothing...                                 |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::cdPoll ()
{
	DWORD mode;

	if (device.Status (mode)) {
		cdReset ();
		return;
	}

	switch (fState) {
		case kNoMedia:		// an error occured
			cdLoad ();
			break;

		case kPaused:				// no op...
		case kStopped:				// no op...
			break;

		case kPlaying:				// no op...
			/*
			 *  We were playing and now we just stopped...
			 */
			if (mode == MCI_MODE_STOP) {
				if (fIndex >= GetNumTracks () && 
				    !(fOptions & kRepeat)) {
					cdStop ();
				} else {
					NextTrack (fIndex);
					cdPlay ();
				}

			} else if (mode == MCI_MODE_PLAY) {
				long time = GetSecond ();

				if ((fMode == kIntro10 && time > 10) ||
				    (fMode == kIntro20 && time > 20) ||
				    (fMode == kIntro30 && time > 30)) {
					if (fIndex >= GetNumTracks () &&
					    !(fOptions && kRepeat)) {
						cdReset ();	
					} else {
						NextTrack (fIndex);
						cdPlay ();
					}
				}
			}
			break;

		default:
			MessageBox (hWnd, "cdPause() unknown state", "ERROR", MB_OK);
			break;
	}
}


#if 0
/*-------------------------------------------------------------------+
|                       CDControl::Load ()                           |
| ================================================================== |
|                                                                    |
| Function:    Load (No Media)                                       |
|                                                                    |
| Algorithm:   Previous state                                        |
|                  kNoMedia:                                         |
|                      o  Try and load the CD (cdLoad)               |
|                                                                    |
|                  kStopped:                                         |
|                  kPlaying:                                         |
|                  kPaused:                                          |
|                      o  Nothing...                                 |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::Load ()
{
	switch (fState) {
		case kNoMedia:
			cdLoad ();
			break;

		case kStopped:
			break;

		case kPlaying:
			break;

		case kPaused:
			break;

		default:
			break;
	}
}


/*-------------------------------------------------------------------+
|                       CDControl::Stop ()                           |
| ================================================================== |
|                                                                    |
| Function:    Stop                                                  |
|                                                                    |
| Algorithm:   Previous state                                        |
|                  kNoMedia:                                         |
|                      o  Ensure cd was loaded...                    |
|                                                                    |
|                  kStopped:                                         |
|                      o  nothing...                                 |
|                                                                    |
|                  kPlaying:                                         |
|                  kPaused:                                          |
|                      o  Stop playing (cdStop)                      |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::Stop ()
{
	switch (fState) {
		case kNoMedia:
		case kStopped:
		case kPlaying:
		case kPaused:
			cdStop ();
			break;

		default:
			break;
	}
}


/*-------------------------------------------------------------------+
|                       CDControl::Play ()                           |
| ================================================================== |
|                                                                    |
| Function:    Play                                                  |
|                                                                    |
| Algorithm:   Previous state                                        |
|                  kNoMedia:                                         |
|                      o  Try and load the CD (cdLoad)               |
|                      o  Begin playing (cdPlay)                     |
|                                                                    |
|                  kStopped:                                         |
|                      o  Continuous play: play next track (cdPlay)  |
|                                                                    |
|                  kPlaying:                                         |
|                      o  IntroPlay: next track (cdPlay)             |
|                                                                    |
|                  kPaused:                                          |
|                      o  Resume playing (cdResume)                  |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::Play ()
{
    DWORD mode;

	switch (fState) {
		case kNoMedia:
			break;

		case kPlaying:
			/*
			 *  Special case: we reached the end of a track and should
			 *  begin playing the next track...
			 */
			if (device.Status (mode)) break;

			if (mode == MCI_MODE_STOP) {
				if (fIndex >= GetNumTracks () && 
				    !(fOptions & kRepeat)) 
					cdStop ();
				else
					NextTrack (fIndex);
			}
			cdPlay ();
			break;

		case kStopped:
			cdPlay ();
			break;

		case kPaused:
			break;

		default:
			break;
	}
}


/*-------------------------------------------------------------------+
|                       CDControl::Pause ()                          |
| ================================================================== |
|                                                                    |
| Function:    Pause                                                 |
|                                                                    |
| Algorithm:   Previous state                                        |
|                  kNoMedia:                                         |
|                  kStopped:                                         |
|                      o  (shouldn't have gotten here...)            |
|                                                                    |
|                  kPlaying:                                         |
|                      o  Pause (cdPause)                            |
|                                                                    |
|                  kPaused:                                          |
|                      o  Resume playing (cdResume)                  |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::Pause ()
{
	switch (fState) {
		case kNoMedia:
		case kStopped:
			break;

		case kPlaying:
			cdPause ();
			break;

		case kPaused:
			cdPlay ();
			break;

		default:
			break;
	}
}

#endif


/*-------------------------------------------------------------------+
|                       CDControl::cdLoad ()                         |
| ================================================================== |
|                                                                    |
| Function:    cdLoad (state transition function)                    |
|                                                                    |
| Algorithm:   o  Close the CD caddy if it is open                   |
|              o  Ensure media (CD) is present                       |
|              o  Get track information...                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdLoad ()
{
	DWORD mode;
	int result = S_OK;

	if (device.Status (mode))
		return E_FAIL;

	switch (mode) {
		case MCI_MODE_OPEN:
			device.CloseDoor ();
			break;

		case MCI_MODE_STOP:				// all good...
		case MCI_MODE_PAUSE:
		case MCI_MODE_SEEK:
		case MCI_MODE_PLAY:
			break;

		case MCI_MODE_NOT_READY:		// don't know how to handle this yet
			MessageBox (hWnd, "cdLoad() MCI_MODE_NOT_READY", "ERROR", MB_OK);
			break;

		default:
			MessageBox (hWnd, "cdLoad() unknown state", "ERROR", MB_OK);
			break;
	}

	/*
	 *  Ensure that user loaded a CD...
	 */
	if (device.IsMediaPresent ()) {

		fAlbum = new CDAlbum ("<key>", "<artist>", "<title>");

		/*
		 *  Get number of tracks, generate unique key... etc
		 */
		int numTracks = device.NumTracks ();

		for (int i=0; i<numTracks; i++) {
			char songName [25];

			sprintf (songName, "song %d", i+1);

			CDSong* song = new CDSong (songName, i+1, 0);
			fAlbum->AddSong (song);
		}

		char* uniqueID;
		if (device.GetID (uniqueID) == ERROR_SUCCESS) {
			fAlbum->SetKey (uniqueID);

			cout << "CD unique id = " << uniqueID << endl;

			if (CDRegistry::LookupSongTitles (*fAlbum)) {
				cout << "LookupSongTitles() failed" << endl;

				CDRegistry::RegisterSongTitles (*fAlbum);
				CDRegistry::LookupSongTitles (*fAlbum);
			}
		}
		fState = kStopped;

	} else {
		fState = kNoMedia;

		result = MCI_MODE_NOT_READY;
	}

	return result;
}


/*-------------------------------------------------------------------+
|                       CDControl::cdPlay ()                         |
| ================================================================== |
|                                                                    |
| Function:    cdPlay (state transition function)                    |
|                                                                    |
| Algorithm:   o  Close the CD caddy if it is open                   |
|              o  Ensure media (CD) is present                       |
|              o  Get track information...                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdPlay ()
{
	DWORD mode;

	if (device.Status (mode))
		return E_FAIL;

	switch (mode) {
		case MCI_MODE_NOT_READY:		// an error occured
		case MCI_MODE_OPEN:
			cdReset ();
			break;

		case MCI_MODE_PAUSE:			// Resume playing...
			device.Resume ();
			fState = kPlaying;
			break;

		case MCI_MODE_SEEK:			// no op...
			break;


		case MCI_MODE_STOP:			// begin playing
		case MCI_MODE_PLAY: 			// switch tracks ?
			{
				/*
				 *  Translate index into actual track number...
				 */
				long track;
				List<CDSong*>* songs = fAlbum->GetSongs ();
				track = (*songs) [fIndex]->GetTrack ();

				device.Play ((BYTE) track);
				fState = kPlaying;
			}
			break;

		default:
			MessageBox (hWnd, "cdPlay() unknown state", "ERROR", MB_OK);
			break;
	}

	return S_OK;
}


/*-------------------------------------------------------------------+
|                       CDControl::cdPause ()                        |
| ================================================================== |
|                                                                    |
| Function:    cdPause (state transition function)                   |
|                                                                    |
| Algorithm:   o  Pause...                                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdPause ()
{
    DWORD mode;

	if (device.Status (mode))
		return E_FAIL;

    switch (mode) {
		case MCI_MODE_NOT_READY:		// an error occured
		case MCI_MODE_OPEN:
			cdReset ();
			break;

		case MCI_MODE_STOP:				// no op...
		case MCI_MODE_PAUSE:
		case MCI_MODE_SEEK:
			break;

		case MCI_MODE_PLAY: 			// Resume playing
			device.Pause ();
			fState = kPaused;
			break;

		default:
			MessageBox (hWnd, "cdPause() unknown state", "ERROR", MB_OK);
			break;
	}

	return S_OK;
}


/*-------------------------------------------------------------------+
|                       CDControl::cdStop ()                         |
| ================================================================== |
|                                                                    |
| Function:    cdStop (state transition function)                    |
|                                                                    |
| Algorithm:   o  Pause...                                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdStop ()
{
    DWORD mode;

	if (device.Status (mode))
		return E_FAIL;

    switch (mode) {
		case MCI_MODE_NOT_READY:		// an error occured
		case MCI_MODE_OPEN:
			cdReset ();
			break;

		case MCI_MODE_STOP:				// no op...
			break;

		case MCI_MODE_PAUSE:
		case MCI_MODE_SEEK:
		case MCI_MODE_PLAY:
			device.Stop ();
			fIndex = 0;
			fState = kStopped;
			break;

		default:
			MessageBox (hWnd, "cdStop() unknown state", "ERROR", MB_OK);
			break;
	}

	return S_OK;
}


/*-------------------------------------------------------------------+
|                       CDControl::cdEject ()                        |
| ================================================================== |
|                                                                    |
| Function:    cdEject (state transition function)                   |
|                                                                    |
| Algorithm:   o  Eject...                                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdEject ()
{
    DWORD mode;

	if (device.Status (mode))
		return E_FAIL;

    switch (mode) {
		case MCI_MODE_NOT_READY:		// an error occured
			MessageBox (hWnd, "cdStop() MCI_MODE_NOT_READY", "ERROR", MB_OK);
			cdReset ();
			break;

		case MCI_MODE_OPEN:				// no op...
			break;

		case MCI_MODE_STOP:				// Eject the CD...
		case MCI_MODE_PAUSE:
		case MCI_MODE_SEEK:
		case MCI_MODE_PLAY:
			device.OpenDoor ();
			fState = kNoMedia;
			break;

		default:
			MessageBox (hWnd, "cdEject() unknown state", "ERROR", MB_OK);
			break;
	}

	return S_OK;
}


/*-------------------------------------------------------------------+
|                       CDControl::cdReset ()                        |
| ================================================================== |
|                                                                    |
| Function:    cdReset (state transition function)                   |
|                                                                    |
| Algorithm:   o  Reset...                                           |
|                                                                    |
+-------------------------------------------------------------------*/
int CDControl::cdReset ()
{
	int result = S_OK;

	fIndex = 0;

	device.Close ();
	device.Open ();
	device.Stop ();

	return result;
}


void CDControl::ChangeTrack (long inc)
{
	if (inc > 0)
		NextTrack (fIndex);
	else
		PrevTrack (fIndex);

	cdPlay ();
}


void CDControl::SelectTrack (long track)
{
	fIndex = track - 1;
	cdPlay ();
}


LPSTR CDControl::Status ()
{
	static char statBuff [50];
	DWORD mode;

	if (fState == kNoDrive) {
		strcpy (statBuff, "No CDROM");
		return statBuff;
	}

	if (device.Status (mode)) {
		strcpy (statBuff, "Device query failed");
		return statBuff;
	}
	
	// return fState;
	switch (mode) {
		case MCI_MODE_NOT_READY:
			strcpy (statBuff, "Device Not Ready");
			break;

		case MCI_MODE_PAUSE:
			strcpy (statBuff, "Paused");
			break;

		case MCI_MODE_PLAY:
			strcpy (statBuff, "Playing");
			break;

		case MCI_MODE_STOP:
			strcpy (statBuff, "Stopped");
			break;

		case MCI_MODE_OPEN:
			strcpy (statBuff, "Device open");
			break;

		default:
			strcpy (statBuff, "unknown state!");
			break;
	}

	return statBuff;
}


/*-------------------------------------------------------------------+
|                      CDControl::GetTrack ()                        |
|                      CDControl::GetMinute ()                       |
|                      CDControl::GetSecond ()                       |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
long CDControl::GetTrack ()
{
	DWORD position;

	if (device.Track (position)) {
		return 0;
	}
	
	return MCI_TMSF_TRACK(position);
}
long CDControl::GetMinute ()
{
	DWORD position;

	if (device.Track (position)) {
		return 0;
	}
	
	return MCI_TMSF_MINUTE(position);
}
long CDControl::GetSecond ()
{
	DWORD position;

	if (device.Track (position)) {
		return 0;
	}
	
	return MCI_TMSF_SECOND(position);
}


long CDControl::TrackLength ()
{
	DWORD trackLength;

	if (device.TrackLength ((BYTE) fIndex, trackLength)) {
		return 0;
	}
	
	return trackLength;
}


/*-------------------------------------------------------------------+
|                       CDControl::Repeat ()                         |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::Repeat ()
{
	if (fOptions & kRepeat)
		fOptions ^= kRepeat;
	else
		fOptions |= kRepeat;
}


/*-------------------------------------------------------------------+
|                    CDControl::GetNumTracks ()                      |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
long CDControl::GetNumTracks ()
{
	int numTracks = 1;

	if (fAlbum) {
		numTracks = fAlbum->GetSongs ()->Length ();
	}

	return numTracks;
}


/*-------------------------------------------------------------------+
|                      CDControl::NextTrack ()                       |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::NextTrack (long& index)
{
	if (index + 1 < GetNumTracks ())
		index++;
	else
		index = 0;
}


/*-------------------------------------------------------------------+
|                      CDControl::PrevTrack ()                       |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
void CDControl::PrevTrack (long& index)
{
	if (index - 1 >= 0)
		index--;
	else
		index = GetNumTracks () - 1;
}


/*-------------------------------------------------------------------+
|                      CDControl::GetAlbum ()                        |
|                      CDControl::GetArtist ()                       |
|                      CDControl::GetSong ()                         |
| ================================================================== |
|                                                                    |
| Function:    ...                                                   |
|                                                                    |
| Algorithm:   ...                                                   |
|                                                                    |
| Parameters:  ...                                                   |
|                                                                    |
| Returns:     ...                                                   |
|                                                                    |
+-------------------------------------------------------------------*/
const char* CDControl::GetAlbum ()
{
	const char *album = NULL;

	if (fAlbum) album = fAlbum->GetTitle ();

	return album;
}
const char* CDControl::GetArtist ()
{
	const char *artist = NULL;

	if (fAlbum) artist = fAlbum->GetArtist ();

	return artist;
}
const char* CDControl::GetSong ()
{
	const char *title = NULL;

	if (fAlbum) {
		List<CDSong*>& list = *fAlbum->GetSongs ();
		CDSong* song = list [fIndex];

		if (song)
			title = song->GetTitle ();
	}

	return title;
}
