/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					 ////
////																					 ////
////								   - HWave.cpp -    								 ////
////																					 ////
////																					 ////
////				Implémentation des fonctions SCOL de la librairie sonore			 ////
////									 Version  1.0									 ////
////																					 ////
////								  Hilaire Verschuere								 ////
////																					 ////
////																					 ////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////


#include "../Basic/ZooScene.h"




//-----------------------------------------------------------------------------
// Name: HWave::HWave()
// Desc: Constructs the class which will be use with HSound
//-----------------------------------------------------------------------------
HWave::HWave( bool addToList )
	  :HSound( addToList )
{
	WaveType = true ;
    m_hmmio	 = NULL ;
}




//-----------------------------------------------------------------------------
// Name: HWave::HWave()
// Desc: Copy Constructor 
//-----------------------------------------------------------------------------
HWave::HWave( HWave &h )
	  :HSound( h )
{
	WaveType = true ;
	m_fileName = h.m_fileName ;
}
	



//-----------------------------------------------------------------------------
// Name: CWaveFile::~CWaveFile()
// Desc: Destructs the class
//-----------------------------------------------------------------------------
HWave::~HWave()
{
	Stop() ;
	SAFE_DELETE( pHSndNotify ) ;
    Close();
}



//-----------------------------------------------------------------------------
// Name: HWave::Open()
// Desc: Open a wave file for reading
//-----------------------------------------------------------------------------
HRESULT HWave::Open( char* strFileName )
{
    if( strFileName == NULL )  return S_FALSE;

	SAFE_DELETE_ARRAY( m_fileName ) ;
	m_fileName = (char*)new char[ strlen( strFileName ) + 1 ] ;
	strcpy( m_fileName, strFileName ) ;

    SAFE_DELETE_ARRAY( m_pwfx );
	Close() ;

    m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );

    if( NULL == m_hmmio )
    {
        HRSRC   hResInfo;
        HGLOBAL hResData;
        DWORD   dwSize;
        VOID*   pvRes;

        // Loading it as a file failed, so try it as a resource
        if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
        {
            if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
                return ERRMSG( "FindResource" );
        }

        if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
            return ERRMSG( "LoadResource" );

        if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
            return ERRMSG( "SizeofResource" );

        if( NULL == ( pvRes = LockResource( hResData ) ) )
            return ERRMSG( "LockResource" );

        CHAR* pData = new CHAR[ dwSize ];
        _memcpy( (char*)pData, (char*)pvRes, dwSize );

        MMIOINFO mmioInfo;
        ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
        mmioInfo.fccIOProc = FOURCC_MEM;
        mmioInfo.cchBuffer = dwSize;
        mmioInfo.pchBuffer = (CHAR*) pData;

        m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
    }

    if( FAILED( ReadMMIO( m_pwfx, m_hmmio ) ) )
    {
        // ReadMMIO will fail if its an not a wave file
        mmioClose( m_hmmio, 0 ) ;
        return ERRMSG( "ReadMMIO" );
    }
	
    if( FAILED( ResetFile() ) ) return ERRMSG("ResetFile" );

    // After the reset, the size of the wav file is m_ck.cksize so store it now
    m_dwSize = m_ck.cksize ;
	
	m_dwNotifySize = MsToChar(m_pwfx, 250) ;	

    return S_OK ;
}






//-----------------------------------------------------------------------------
// Name: HWave::OpenNext()
// Desc: Open next file
//-----------------------------------------------------------------------------
HRESULT HWave::OpenNext( char* strFileName )
{
    if( strFileName == NULL )  return S_FALSE;

	SAFE_DELETE_ARRAY( m_fileName ) ;
	m_fileName = (char*)new char[ strlen( strFileName ) + 1 ] ;
	strcpy( m_fileName, strFileName ) ;

	SAFE_DELETE_ARRAY( m_pwfx );
	Close() ;

	//m_fileName = strFileName ;

    m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );

    if( NULL == m_hmmio )
    {
        HRSRC   hResInfo;
        HGLOBAL hResData;
        DWORD   dwSize;
        VOID*   pvRes;

        // Loading it as a file failed, so try it as a resource
        if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
        {
            if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
                return ERRMSG( "FindResource" );
        }

        if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
            return ERRMSG( "LoadResource" );

        if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
            return ERRMSG( "SizeofResource" );

        if( NULL == ( pvRes = LockResource( hResData ) ) )
            return ERRMSG( "LockResource" );

        CHAR* pData = new CHAR[ dwSize ];
        _memcpy( (char*)pData, (char*)pvRes, dwSize );

        MMIOINFO mmioInfo;
        ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
        mmioInfo.fccIOProc = FOURCC_MEM;
        mmioInfo.cchBuffer = dwSize;
        mmioInfo.pchBuffer = (CHAR*) pData;

        m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
    }

    if( FAILED( ReadMMIO( m_pwfx, m_hmmio ) ) )
    {
        // ReadMMIO will fail if its an not a wave file
        mmioClose( m_hmmio, 0 ) ;
        return ERRMSG( "ReadMMIO" );
    }
	
    if( FAILED( ResetFile() ) ) return ERRMSG("ResetFile" );

	// After the reset, the size of the wav file is m_ck.cksize so store it now
    m_nextSize = m_ck.cksize ;

    return S_OK ;
}




//-----------------------------------------------------------------------------
// Name: HWave::ReadMMIO()
// Desc: Support function for reading from a multimedia I/O stream.
//       m_hmmio must be valid before calling.  This function uses it to
//       update m_ckRiff, and m_pwfx. 
//-----------------------------------------------------------------------------
HRESULT HWave::ReadMMIO( WAVEFORMATEX* &pwfx, HMMIO &hmmio)
{
    MMCKINFO        ckIn;           // chunk info. for general use.
    PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.       

    pwfx = NULL;

    if( ( 0 != mmioDescend( hmmio, &m_ckRiff, NULL, 0 ) ) )
        return ERRMSG( "mmioDescend" );

    // Check to make sure this is a valid wave file
    if( (m_ckRiff.ckid != FOURCC_RIFF) || (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
        return ERRMSG( "mmioFOURCC" ); 

    // Search the input file for for the 'fmt ' chunk.
    ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if( 0 != mmioDescend( hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
        return ERRMSG( "mmioDescend" );

    // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
    // if there are extra parameters at the end, we'll ignore them
       if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
           return ERRMSG( "sizeof(PCMWAVEFORMAT)" );

    // Read the 'fmt ' chunk into <pcmWaveFormat>.
    if( mmioRead( hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
        return ERRMSG( "mmioRead" );

    // Allocate the waveformatex, but if its not pcm format, read the next
    // word, and thats how many extra bytes to allocate.
    if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
    {
        pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
        if( NULL == pwfx ) return ERRMSG( "pwfx" );

        // Copy the bytes from the pcm structure to the waveformatex structure
        _memcpy( (char*)pwfx, (char*)(&pcmWaveFormat), sizeof(pcmWaveFormat) );
        pwfx->cbSize = 0;
    }
    else
	{
		SAFE_DELETE_ARRAY( pwfx ) ;
		return S_FALSE ;
	}
   

    // Ascend the input file out of the 'fmt ' chunk.
    if( 0 != mmioAscend( hmmio, &ckIn, 0 ) )
    {
        SAFE_DELETE_ARRAY( pwfx );
        return ERRMSG( "mmioAscend" );
    }

    return S_OK ;
}




//-----------------------------------------------------------------------------
// Name: HWave::Read()
// Desc: Reads section of data from a wave file into pBuffer and returns 
//       how much read in pdwSizeRead, reading not more than dwSizeToRead.
//       This uses m_ck to determine where to start reading from.  So 
//       subsequent calls will be continue where the last left off unless 
//       Reset() is called.
//-----------------------------------------------------------------------------
DWORD HWave::Read( char* pBuffer, DWORD dwSizeToRead )
{
    MMIOINFO mmioinfoIn; // current status of m_hmmio
	
    if( !m_hmmio || !pBuffer ) return 0 ;
  
    if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )	return ERRMSG( "mmioGetInfo" );
            
    unsigned int cbDataIn = dwSizeToRead ;
    if( cbDataIn > m_ck.cksize )  cbDataIn = m_ck.cksize ;
    m_ck.cksize -= cbDataIn ;

    for( DWORD cT = 0; cT < cbDataIn; cT++ )
    {
        // Copy the bytes from the io to the buffer.
        if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
        {
            if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
                return ERRMSG( "mmioAdvance" );

            if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
				return ERRMSG( "mmioinfoIn.pchNext == mmioinfoIn.pchEndRead" );
        }

        // Actual copy.
        *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
        mmioinfoIn.pchNext++;
    }

    if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ) return ERRMSG( "mmioSetInfo" );

    return cbDataIn ;
}






//-----------------------------------------------------------------------------
// Name: HWave::Close()
// Desc: Closes the wave file 
//-----------------------------------------------------------------------------
HRESULT HWave::Close()
{
	mmioClose( m_hmmio, 0 );
    m_hmmio = NULL;

	return S_OK ;
}




//-----------------------------------------------------------------------------
// Name: HWave::ResetFile()
// Desc: Resets the internal m_ck pointer so reading starts from the 
//       beginning of the file again 
//-----------------------------------------------------------------------------
HRESULT HWave::ResetFile()
{
    if( m_hmmio == NULL ) return S_FALSE;

    // Seek to the data
    if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC), SEEK_SET ) ) return ERRMSG( "mmioSeek" );

    // Search the input file for the 'data' chunk.
    m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
    if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) return ERRMSG( "mmioDescend" );
    
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: HWave::SetFilePosition()
// Desc: Set the read position in the wave file
// position in bytes
//-----------------------------------------------------------------------------
HRESULT	HWave::SetFilePosition( long position )
{
	if( labs( position ) > m_dwSize ) return S_FALSE ;

	// Seeking into the wave file
	mmioSeek(m_hmmio, position, SEEK_SET ) ;

	// We have to keep the data size remaining in the file
	m_ck.cksize = m_dwSize - position ;

	return S_OK ;
}





//////////////////////////////////////////////////////////////////////////////////////////////
// Name: IsAWaveFile(char* fileName)
// Desc: Tell if the file is a wave file.
//////////////////////////////////////////////////////////////////////////////////////////////
bool HWave::IsAWaveFile(char* fileName)
{
	MMCKINFO ckRiff ;
	MMCKINFO ckIn ;
	PCMWAVEFORMAT pcmWaveFormat;  // Temp PCM structure to load in.
	HMMIO	 hmmio = mmioOpen( fileName, NULL, MMIO_ALLOCBUF | MMIO_READ );

	if( hmmio == NULL )
    {
        HRSRC   hResInfo;
        HGLOBAL hResData;
        DWORD   dwSize;
        VOID*   pvRes;

        // Loading it as a file failed, so try it as a resource
        if( NULL == ( hResInfo = FindResource( NULL, fileName, TEXT("WAVE") ) ) )
        {
            if( NULL == ( hResInfo = FindResource( NULL, fileName, TEXT("WAV") ) ) )
                return 0 ;
        }

        if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
            return 0 ;

        if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
            return 0 ;

        if( NULL == ( pvRes = LockResource( hResData ) ) )
            return 0 ;

        CHAR* pData = new CHAR[ dwSize ];
        _memcpy( (char*)pData, (char*)pvRes, dwSize );

        MMIOINFO mmioInfo;
        ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
        mmioInfo.fccIOProc = FOURCC_MEM;
        mmioInfo.cchBuffer = dwSize;
        mmioInfo.pchBuffer = (CHAR*) pData;

        hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
    }

	if( ( 0 != mmioDescend( hmmio, &ckRiff, NULL, 0 ) ) )
	{
		mmioClose( hmmio, 0 );
		return false ; 
	}


    // Check to make sure this is a valid wave file
    if( (ckRiff.ckid != FOURCC_RIFF) || (ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
    {
		mmioClose( hmmio, 0 );
		return false ; 
	}


	// Search the input file for for the 'fmt ' chunk.
    ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if( 0 != mmioDescend( hmmio, &ckIn, &ckRiff, MMIO_FINDCHUNK ) )
	{
		mmioClose( hmmio, 0 );
		return false ; 
	}


	// Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
    // if there are extra parameters at the end, we'll ignore them
    if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
	{
		mmioClose( hmmio, 0 );
		return false ; 
	}
    

	// Read the 'fmt ' chunk into <pcmWaveFormat>.
    if( mmioRead( hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
    {
		mmioClose( hmmio, 0 );
		return false ; 
	}

	// Allocate the waveformatex, but if its not pcm format, read the next
    // word, and thats how many extra bytes to allocate.
    if( pcmWaveFormat.wf.wFormatTag != WAVE_FORMAT_PCM )
    {
		mmioClose( hmmio, 0 );
		return false ; 
	}
	else 
	{
		mmioClose( hmmio, 0 );
		return true ;
	}
}




//-----------------------------------------------------------------------------
// Name: HWave::OpenNext()
// Desc: Open next file
//-----------------------------------------------------------------------------
bool HWave::SameProperties( char* strFileName )
{
    if( strFileName == NULL )  return S_FALSE;

	WAVEFORMATEX* pwfx = NULL ;
  
    HMMIO hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );

    if( NULL == hmmio )
    {
        HRSRC   hResInfo;
        HGLOBAL hResData;
        DWORD   dwSize;
        VOID*   pvRes;

        // Loading it as a file failed, so try it as a resource
        if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
        {
            if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
                return false ;
        }

        if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
            return false ;

        if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
            return false ;

        if( NULL == ( pvRes = LockResource( hResData ) ) )
            return false ;

        CHAR* pData = new CHAR[ dwSize ];
        _memcpy( (char*)pData, (char*)pvRes, dwSize );

        MMIOINFO mmioInfo;
        ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
        mmioInfo.fccIOProc = FOURCC_MEM;
        mmioInfo.cchBuffer = dwSize;
        mmioInfo.pchBuffer = (CHAR*) pData;

        hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
    }

    if( FAILED( ReadMMIO( pwfx, hmmio ) ) )
    {
        // ReadMMIO will fail if its an not a wave file
        mmioClose( hmmio, 0 ) ;
        return false ;
    }

	if( *pwfx != *m_pwfx )
	{
		MMechostr( 0, "Wave file %s have different audio properties than %s.", strFileName, m_fileName ) ;
		return false ;
	}

	mmioClose( hmmio, 0 );
	SAFE_DELETE_ARRAY( pwfx ) ;

    return true ;
}






