/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "audio/sample_wav_factory.h"
		#include "audio/sample_wav.h"
		#include "filesystem/filesystem.h"
		#include "filesystem/io_handle.h"
		#include "memory/endian.h"
		#include "platform.h"
		#include "log/log.h"

		using namespace GS;


//------------------------------------------------------------------------------
ISample				*SampleWavFactory::Load(const char *path)
{
	AutoPtr <IO::Handle> h(Platform::Get().io->Open(path));
	if	(h.IsNull())
		return NULL;

	// Verify format.
	char header[4];
	h->Read(header, 4);
	if	(memcmp(header, "RIFF", 4))
		return NULL;
	h->Seek(4);
	h->Read(header, 4);
	if	(memcmp(header, "WAVE", 4))
		return NULL;

	// Create sample.
	AutoPtr <SampleWav> sample(new SampleWav);
	if	(sample.IsNull())
		return NULL;

	// Parse format.
	bool has_format = false, has_data = false;

	struct Format
	{
		short			wFormatTag;
		unsigned short	wChannels;
		unsigned long	dwSamplesPerSec;
		unsigned long	dwAvgBytesPerSec;
		unsigned short	wBlockAlign;
		unsigned short	wBitsPerSample;
	};
	Format format;

	Memory::Set(&format, 0, sizeof(Format));

	forever
	{
		// Chunk + size.
		if	(h->Read(header, 4) != 4)
			break;
		uint chunk_size = h->Read <uint> ();

		// WAV format tag.
		if	(!memcmp(header, "fmt ", 4))
		{
			uint cs = chunk_size;
			if	(cs > sizeof(Format))
			{
				__LOG_W__ << "Unexpected WAV 'format' chunk size. Found " << cs << ", expected " << (int)sizeof(Format) << ".\n";
				cs = sizeof(Format);
			}
			if	(h->Read(&format, cs) != cs)
				__ERR__(__LOG_E__ << "Mangled WAV 'format' chunk in '" << path << "'.\n", NULL)

			Endian::ToHost(&format.wFormatTag, 2, Endian::Intel);
			Endian::ToHost(&format.wChannels, 2, Endian::Intel);
			Endian::ToHost(&format.dwSamplesPerSec, 4, Endian::Intel);
			Endian::ToHost(&format.dwAvgBytesPerSec, 4, Endian::Intel);
			Endian::ToHost(&format.wBlockAlign, 2, Endian::Intel);
			Endian::ToHost(&format.wBitsPerSample, 2, Endian::Intel);

			has_format = true;

			// Finish skipping tag.
			if	(cs != chunk_size)
				h->Seek(chunk_size - cs);
		}

		// WAV data tag.
		else	if	(!memcmp(header, "data", 4))
		{
			if	(has_format)
			{
				char *pcm = sample->AllocAs(chunk_size / (format.wBitsPerSample / 8) / format.wChannels, SampleFormat(SampleFormat::Format_PCM, format.wChannels, format.dwSamplesPerSec, (uchar)format.wBitsPerSample));
				if	(!pcm)
					__ERR__(__LOG_E__ << "failed to allocate WAV data chunk for '" << path << "'.\n", NULL)
				if	(h->Read(pcm, chunk_size) != chunk_size)
					__ERR__(__LOG_E__ << "mangled WAV 'data' chunk in '" << path << "'.\n", NULL)
			}
			else
				__ERR__(__LOG_E__ << "WAV data with no format in '" << path << "'.\n", NULL)

			has_data = true;
		}
		else
			h->Seek(chunk_size);
	}

	if	(!has_format || !has_data)
		return NULL;

	SampleFormat sample_format;
	if	(!sample->GetSampleFormat(sample_format))
		return NULL;

	__LOG__ << "Sample format: " << sample_format.frequency / 1000 << "KHz@" << sample_format.resolution << "bit, " << sample_format.channels << " channel(s).\n";
	return sample.Detach();
}
//------------------------------------------------------------------------------
