#include "stdafx.h"
#include "FrotzCE.h"
#include "FrotzCESnd.h"

#if !defined( _WIN32_WCE ) || _WIN32_WCE >= 210
#include <stdio.h>
#else
#include "FrotzCEIO.h"
#endif

#include <string.h>

extern "C"
{
#include "Frotz/Frotz.h"
}

// Constructor
CFrotzCESnd::CFrotzCESnd()
{
	// Initialise
	m_pachSampleData = NULL;
	m_nCurrentSample = 0;
	m_bPlayingSound = FALSE;
	memset( &m_sHeader, 0, sizeof( m_sHeader ) );
}

/*
 * Beep
 *
 * Play a beep sound. Ideally, the sound should be high- (number == 1)
 * or low-pitched (nnumber == 2).
 *
 */

void CFrotzCESnd::Beep( int nNumber )
{
	if( nNumber == 2 )
		MessageBeep( MB_OK );
	else
		MessageBeep( MB_ICONASTERISK );
}

/*
 * LoadSample
 *
 * Load the given sample from the disk.
 *
 */

BOOL CFrotzCESnd::LoadSample( int nNumber )
{
	// Loaded sample already?
    if (m_nCurrentSample == nNumber)
		return;

	// Find extension
	int nPos;
	for (nPos = strlen( FROTZCEAPP->m_achStoryFile ) - 1; 
		nPos >= 0 && FROTZCEAPP->m_achStoryFile[nPos] != '.'; nPos--);

	// Get length of base sample file name
	int nStubLen = strlen (FROTZCEAPP->m_achStoryFile ) - nPos;
	if (nStubLen > 6) nStubLen = 6;

	// Generate sample file name
	CString strSampleName;
	int nDefDirLen = FROTZCEAPP->m_strDefaultDir.GetLength();
	strSampleName.Format( TEXT( "%.*s\\Sound\\%.*s%02d.snd" ), 
		nDefDirLen, FROTZCEAPP->m_achStoryFile, nStubLen, 
		&FROTZCEAPP->m_achStoryFile[nDefDirLen+1] );

    // Open sample file
	CFile cSampleFile;
	if (cSampleFile.Open( strSampleName, CFile::modeRead ))
	{
	UINT nFileLen;
	BYTE nLoByte, nHiByte;

		// Find out how long the file is for error checking later
		cSampleFile.SeekToEnd();
		nFileLen = cSampleFile.GetPosition();
		cSampleFile.SeekToBegin();

		// If it's shorter than this, it has to be corrupt (minimum size of sound header)
		if( nFileLen <= MIN_HEADER_SIZE )
		{
		CString strError = TEXT( "SoundFile: " );

			// Display error
			strError += strSampleName;
			strError += TEXT( "\nSound file too short!" );
			AfxMessageBox( strError );
			cSampleFile.Close();
			return FALSE;
		}
		// Load header
		// MSB is first, LSB is second
		cSampleFile.Read( &nHiByte, 1 );
		cSampleFile.Read( &nLoByte, 1 );
		m_sHeader.nPrefix = (nHiByte << 8) + nLoByte;

		// Valid length?
		if( m_sHeader.nPrefix > nFileLen )
		{
		CString strError = TEXT( "SoundFile: " );

			// Display error
			strError += strSampleName;
			strError += TEXT( "\nSound file too short!" );
			AfxMessageBox( strError );
			cSampleFile.Close();
			return FALSE;
		}

		// Read sound info
		cSampleFile.Read( &m_sHeader.nRepeats, 1 );
		cSampleFile.Read( &m_sHeader.nBaseNote, 1 );
		cSampleFile.Read( &nHiByte, 1 );
		cSampleFile.Read( &nLoByte, 1 );
		m_sHeader.nFrequency= (nHiByte << 8) + nLoByte;

		// Skip the unused two bytes
		cSampleFile.Read( &nHiByte, 1 );
		cSampleFile.Read( &nHiByte, 1 );

		// Read sample infp
		cSampleFile.Read( &nHiByte, 1 );
		cSampleFile.Read( &nLoByte, 1 );
		m_sHeader.nLength = ( nHiByte << 8 ) + nLoByte;

		// Allocate memory for sample data
		if ((m_pachSampleData = (char *) ::LocalAlloc( LPTR, m_sHeader.nLength)) != NULL) 
		{
			// Read sample data
			if( cSampleFile.Read( m_pachSampleData, m_sHeader.nLength ) != m_sHeader.nLength )
			{
			CString strError = TEXT( "SoundFile: " );

				// Display error
				strError += strSampleName;
				strError += TEXT( "\nError reading sound data from file!" );
				AfxMessageBox( strError );
				cSampleFile.Close();
				return FALSE;
			}

			// Store sample number 
			m_nCurrentSample = nNumber;
		} 
		else 
		{
		CString strError = TEXT( "SoundFile: " );

			// Display error
			strError += strSampleName;
			strError += TEXT( "\nNot enough memory for sample!" );
			AfxMessageBox( strError );
			cSampleFile.Close();
			return FALSE;
		}

		// Close sample file
		cSampleFile.Close();

		return TRUE;
	}
	else
	{
	CString strError = TEXT( "SoundFile: " );

		// Display error
		strError += strSampleName;
		strError += TEXT( "\nCould not open sample file!" );
		AfxMessageBox( strError );
		return FALSE;
	}
}

/*
 * StartSample
 *
 * Play the given sample at the given volume (ranging from 1 to 8 and
 * 255 meaning a default volume). The sound is played once or several
 * times in the background (255 meaning forever). In Z-code 3, the
 * repeats value is always 0 and the number of repeats is taken from
 * the sound file itself. The end_of_sound function is called as soon
 * as the sound finishes.
 *
 */

void CFrotzCESnd::StartSample( int nNumber, int nVolume, int nRepeats )
{
WAVEFORMAT EX sWaveFormat;
WAVEHDR sWaveHdr;

	// Stop sample from playing
	StopSample();

	// Try to load specified sample
    if (!LoadSample( nNumber )) return;

	// Clear wave headers
	memset( &sWaveFormat, 0, sizeof( WAVEFORMATEX ) );
	memset( &sWaveHdr, 0, sizeof( WAVEHDR ) );

	// Set this up for PCM, 1 channel, 8 bits unsigned samples
	sWaveFormat.wFormatTag = WAVE_FORMAT_PCM;
	sWaveFormat.nChannels = 1;
	sWaveFormat.nSamplesPerSec = m_sHeader.nFrequency;
	sWaveFormat.nAvgBytesPerSec = m_sHeader.nFrequency;
	sWaveFormat.nBlockAlign = 1;
	sWaveFormat.wBitsPerSample = 8;
	sWaveFormat.cbSize = 0;

	// Set up wave header
	sWaveHdr.lpData = (LPSTR) m_pachSampleData;
	sWaveHdr.dwBufferLength = m_sHeader.nLength;
	sWaveHdr.dwLoops = m_sHeader.nRepeats;
	if (sWaveHdr.dwLoops == 0) sWaveHdr.dwLoops = 255;
    if (h_version >= V5) sWaveHdr.dwLoops = nRepeats;
	sWaveHdr.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;

	// Play sound
	m_bPlayingSound = sndPlaySound( sWaveHdr, SND_ASYNC | SND_MEMORY );
}

/*
 * StopSample
 *
 * Turn off the current sample.
 *
 */

void CFrotzCESnd::StopSample (void)
{
	// Playing sound?
	if (m_bPlayingSound)
	{
		// Stop sound
		sndPlaySound( NULL, 0 );
		m_bPlayingSound = FALSE;
	}
}

/*
 * ResetSound()
 *
 * Free resources allocated for playing samples.
 *
 */

void CFrotzCESnd::ResetSound()
{
	// Stop playing sample
	StopSample();

	// Free buffer
    if (m_pchSampleData) delete m_pchSampleData;
    m_pchSampleData = NULL;

	m_nCurrentSample = 0;
}

// Destructor
CFrotzCESnd::~CFrotzCESnd()
{
	// Clean up
	ResetSound();
}