/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					 ////
////																					 ////
////								   - HData.cpp -	    							 ////
////																					 ////
////																					 ////
////				Implémentation des fonctions SCOL de la librairie sonore			 ////
////									 Version  1.0									 ////
////																					 ////
////								  Hilaire Verschuere								 ////
////																					 ////
////																					 ////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////


#include "../Basic/ZooScene.h"



//-----------------------------------------------------------------------------
// Name: HSound::HSound()
// Constructor
//-----------------------------------------------------------------------------
HData::HData( bool addToList )
	  :HSound( addToList )
{
	DataType	 = 0 ;
	head		 = NULL ;
	queue		 = NULL ;
	curs		 = NULL ;
}




//-----------------------------------------------------------------------------
// Name: HData::HData()
// Desc: copy constructor
//-----------------------------------------------------------------------------
HData::HData( HData &h )
	  :HSound( h )
{
	DataType     = h.DataType ;
	head		 = NULL ;
	queue		 = NULL ;

	if( DataType & AS_SND_LINEAR ) curs = &head ; 
	else 
	{
		curs = new lbuffers* ;
		*curs = NULL ;
	}
	
	// Copy sound data for the new object
	CopyData( h.head ) ;
}
	



//-----------------------------------------------------------------------------
// Name: HData::~HData()
// Desc: Destructs the class
//-----------------------------------------------------------------------------
HData::~HData()
{
	Stop() ;
	SAFE_DELETE( pHSndNotify ) ;
	Close() ;
}




//-----------------------------------------------------------------------------
// Name: HData::HData()
// Desc: Setting data to use it manually
//-----------------------------------------------------------------------------
HRESULT HData::SetData( int channel, int bitPerSample, int freq, int flag )
{
    if( (channel!=1 && channel!=2) || (bitPerSample!=8 && bitPerSample!=16) || freq<5000 || freq>48000 ) 
	{
		MMechostr( 1, "Error in setting sound parameters\n" ) ; 
		return S_FALSE ;
	}

	DataType = flag ;

	if( DataType & AS_SND_CIRCULAR )
	{
		curs = new lbuffers* ;
		*curs = NULL ;
	}
	else
	{
		curs = &head ;
	}
	
	// setup format
	m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
	memset(m_pwfx, 0, sizeof(PCMWAVEFORMAT)) ;
	m_pwfx->wFormatTag		= WAVE_FORMAT_PCM ;
	m_pwfx->nChannels		= channel ;
	m_pwfx->wBitsPerSample	= bitPerSample ;
	m_pwfx->nSamplesPerSec	= freq ;
	m_pwfx->nBlockAlign		= (bitPerSample/8) * channel ;
	m_pwfx->nAvgBytesPerSec	= m_pwfx->nSamplesPerSec * m_pwfx->nBlockAlign ;
	m_pwfx->cbSize			= 0 ;

	m_dwNotifySize = m_pwfx->nSamplesPerSec * m_pwfx->nBlockAlign / ( 4 * NB_PLAY_NOTIFICATIONS ) ;
    m_dwNotifySize -= m_dwNotifySize % m_pwfx->nBlockAlign ;

	return 0 ;
}

 


//-----------------------------------------------------------------------------
// Name: HData::AddData()
// Desc: Add data to the list of buffers
//-----------------------------------------------------------------------------
HRESULT HData::AddData( char* data, unsigned int size, int position )
{
	if( (DataType & AS_SND_CIRCULAR) && IsSoundPlaying() ) return S_FALSE ;

	lbuffers* ptr ;

	// on enlève de la liste des données la dimension position
	if( position < 0 )
	{
		if( m_dwSize > -position )
		{
			m_dwSize += position ;
			RemoveData( queue, -position - (queue->size - queue->pos) ) ;
		}
		else
		{
			m_dwSize = 0 ;
			FreeBuffers( head ) ;
		}

		position = 0 ;
		if( size == 0 ) return  S_OK ;
	}

	// Création d'un élément de la liste des données
	ptr = new lbuffers ;
	ptr->buffer = new char[ position + size ] ;
	_FillMemory( ptr->buffer, position, (BYTE)( m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ) ;
	_memcpy( ptr->buffer+position, data, size ) ;
	ptr->size = position + size ;
	ptr->pos = 0 ;
	ptr->next = NULL ;
	
	m_dwSize += ( position + size ) ;
	
	// Insertion dans la liste
	if( !head )
	{
		queue = ptr ;
		head  = ptr ;
		if( DataType & AS_SND_CIRCULAR ) *curs = ptr ;
	}
	else 
	{
		queue->next = ptr ;
		ptr->prev = queue ;
		queue = ptr ;
	}


	if( DataType & AS_SND_CIRCULAR )
	{
		pHSndNotify->SetEndStreaming( m_dwSize - 1 ) ;
		pHSndNotify->SetNotify() ;
	}

	return S_OK ;
}


//-----------------------------------------------------------------------------
// Name: HData::RemoveData()
// Desc: Remove any data to the list of buffers
//-----------------------------------------------------------------------------
void HData::RemoveData( lbuffers* &ptr, int position )
{
	if( !ptr ) { head=NULL ; return ; }
	
	if( position >= 0 )
	{
		RemoveData( ptr->prev, position - (ptr->size - ptr->pos) ) ;
		SAFE_DELETE_BUFFER( ptr ) ;
	}
	else
	{
		ptr->size = ptr->pos - position ;
		ptr->next = NULL ;
	}
}



//-----------------------------------------------------------------------------
// Name: HData::FreeBuffers()
// Desc: Delete the list of buffers of data
//-----------------------------------------------------------------------------
void HData::FreeBuffers(lbuffers* &h)
{
	if( h != NULL ) 
	{
		FreeBuffers( h->next ) ;
		SAFE_DELETE_BUFFER( h ) ;
	}
}



//-----------------------------------------------------------------------------
// Name: HData::InitBuffers()
// Desc: Set the position of each buffers at 0
//-----------------------------------------------------------------------------
void HData::InitBuffers(lbuffers* h)
{
	if( h != NULL ) 
	{
		InitBuffers( h->next ) ;
		h->pos = 0 ;
	}
}



//-----------------------------------------------------------------------------
// Name: HData::CopyData()
// Desc: Used to copy an HData object
//-----------------------------------------------------------------------------
void HData::CopyData( lbuffers* h )
{
	if( !h ) return ;
	else
	{
		int pos = ( DataType & AS_SND_LINEAR ? h->pos : 0 ) ;
		lbuffers* ptr = new lbuffers ;
		ptr->size = h->size - pos ;
		ptr->buffer = new char[ ptr->size ] ;
		_memcpy( ptr->buffer, h->buffer + pos, ptr->size ) ;
		ptr->pos = 0 ;
		ptr->next = NULL ;
		
		if( !queue ) 
		{
			head  = ptr ;
			queue = ptr ;
			if( DataType & AS_SND_CIRCULAR ) *curs = ptr ;
			
		}
		else
		{
			queue->next = ptr ;
			ptr->prev = queue ;
		}

		queue = ptr ;
	}

	CopyData( h->next ) ;
}



//-----------------------------------------------------------------------------
// Name: HData::ReadLinear()
// Desc: Read data in list of buffers and erase data read
//-----------------------------------------------------------------------------
DWORD HData::ReadLinear( char* pBuffer, unsigned long sizeToRead )
{
	DWORD size ;

	if( sizeToRead == 0 || !(*curs) )
	{
		return 0 ;
	}
	else if( (*curs)->pos < (*curs)->size )
	{
		size = MIN( ((*curs)->size - (*curs)->pos), sizeToRead ) ;
		_memcpy( pBuffer, (*curs)->buffer + (*curs)->pos, size ) ;
		(*curs)->pos += size ;	
	}
	else
	{
		size = 0 ;
		lbuffers* ptr = (*curs) ;
		(*curs) = (*curs)->next ;
		SAFE_DELETE_BUFFER( ptr ) ;
	}
	
	m_dwSize -= size ;

	return ( size + Read( pBuffer+size, sizeToRead-size ) ) ;
}


//-----------------------------------------------------------------------------
// Name: HData::ReadCircular()
// Desc: Read data in list of buffers and keep data read
//-----------------------------------------------------------------------------
DWORD HData::ReadCircular( char* pBuffer, unsigned long sizeToRead )
{
	DWORD size ;

	if( sizeToRead == 0 || !(*curs) )
	{
		return 0 ;
	}
	else if( (*curs)->pos < (*curs)->size )
	{
		size = MIN( ((*curs)->size - (*curs)->pos), sizeToRead ) ;
		_memcpy( pBuffer, (*curs)->buffer + (*curs)->pos, size ) ;
		(*curs)->pos += size ;	
	}
	else
	{
		size = 0 ;		
		(*curs)->pos = 0 ;
		(*curs) = (*curs)->next ;
	}
	
	return ( size + Read( pBuffer+size, sizeToRead-size ) ) ;
}




//-----------------------------------------------------------------------------
// Name: HData::Close()
// Desc: Retuns the duration of audio data 
//-----------------------------------------------------------------------------
HRESULT HData::Close()
{
	FreeBuffers( head ) ;

	if( DataType & AS_SND_CIRCULAR ) SAFE_DELETE( curs ) ;

	m_dwSize = 0 ;

	return S_OK ;
}

//-----------------------------------------------------------------------------
// Name: HData::ResetFile()
// Desc: Resets the data
//-----------------------------------------------------------------------------
HRESULT HData::ResetFile()
{
	if( DataType & AS_SND_LINEAR )
	{
		Close() ;
	}
	else
	{
		*curs = head ;
		InitBuffers( head ) ;
	}

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HData::SetFilePosition()
// Desc: Set the reading position in the audia data
// position in bytes
//-----------------------------------------------------------------------------
HRESULT HData::SetFilePosition( long position )
{
	if( !head || (position>m_dwSize) ) return S_FALSE ;

	lbuffers* ptr = head ;

	InitBuffers( ptr ) ;

	position -= ptr->size ;
	while( position>0 && ptr->next )
	{
		ptr = ptr->next ;
		if( DataType & AS_SND_LINEAR ) SAFE_DELETE_BUFFER( ptr->prev );
		position -= ptr->size ;
	}

	if( position<=0 )
	{
		ptr->pos = ptr->size + position ;
		(*curs) = ptr ;
	}
	else return S_FALSE ;	

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HData::GetPlayProgress()
// Desc: Get the read position in millisecondes
//-----------------------------------------------------------------------------
DWORD HData::GetPlayProgress()
{
	if( DataType & AS_SND_LINEAR ) return 0 ;
	else return HSound::GetPlayProgress() ;
}


