#include "Decoder.h"
#include <Windows.h>

const unsigned int Decoder::CheckSize = 4;

Decoder* Decoder::CreateInstance( void )
{
	return new Decoder();
}

Decoder::Decoder( void ) :
m_bInitialized( false ),
m_WavChannelSize( 0 ),
m_WavSize( 0 )
{
}

Decoder::~Decoder( void )
{
	if( m_bInitialized == true )
	{
		::ov_clear( &m_OVFile );
	}
}

bool Decoder::Initialize( Mix::Plugin::IReader* pSrc )
{
	//OggVorbisコールバックの準備
	ov_callbacks OVCallbacks =
	{
		&Decoder::OVRead,
		&Decoder::OVSeek,
		&Decoder::OVClose,
		&Decoder::OVTell,
	};

	//OggVorbisファイルを開く
	if( ::ov_open_callbacks( pSrc, &m_OVFile, 0, 0, OVCallbacks ) != 0 )
	{
		return false;
	}

	//Vorbis情報を取得
	vorbis_info* pOVInfo = ::ov_info( &m_OVFile, -1 );

	//出力フォーマット
	m_Format.wFormatTag = WAVE_FORMAT_PCM;
	m_Format.nChannels = static_cast<WORD>( pOVInfo->channels );
	m_Format.wBitsPerSample = Decoder::BITSPERSAMPLE;
	m_Format.nSamplesPerSec = pOVInfo->rate;
	m_Format.nBlockAlign = ( m_Format.nChannels * m_Format.wBitsPerSample ) / 8;
	m_Format.nAvgBytesPerSec = m_Format.nSamplesPerSec * m_Format.nBlockAlign;

	//波形のサイズを算出
	m_WavChannelSize = pOVInfo->channels * 2;
	m_WavSize = m_WavChannelSize * ::ov_pcm_total( &m_OVFile, -1 );

	//初期化済み
	m_bInitialized = true;

	return true;
}

void Decoder::Destroy( void )
{
	delete this;
}

void Decoder::Reset( Mix::Plugin::IReader* pSrc )
{
	( void )pSrc;

	::ov_pcm_seek( &m_OVFile, 0 );
}

unsigned int Decoder::Read( Mix::Plugin::IReader* pSrc, void* pDst, unsigned int size )
{
	( void )pSrc;

	unsigned char* pui8Dst = reinterpret_cast<unsigned char*>( pDst );

	unsigned int requestSize = CONVBUFFER_SIZE;
	int bitstream = 0;
	unsigned int readSize = 0;
	unsigned int comSize = 0;
	bool bAdjust = false;

	if( size < requestSize )
	{
		requestSize = size;
		bAdjust = true;
	}

	while( 1 )
	{
		readSize = ::ov_read( &m_OVFile, reinterpret_cast<char*>( ( pui8Dst + comSize ) ), requestSize, 0, 2, 1, &bitstream );
		if( readSize == 0 )
		{
			break;
		}

		comSize += readSize;

		if( comSize >= size )
		{
			break;
		}

		if( ( size - comSize ) < CONVBUFFER_SIZE )
		{
			bAdjust = true;
			requestSize = size - comSize;
		}
	}

	return comSize;
}

unsigned long long Decoder::GetPosition( void )
{
	return ( m_WavChannelSize * ::ov_pcm_tell( &m_OVFile ) );
}

unsigned long long Decoder::GetSize( void )
{
	return m_WavSize;
}

LPCWAVEFORMATEX Decoder::GetFormat( void )
{
	return &m_Format;
}

size_t Decoder::OVRead( void* buffer, size_t size, size_t maxCount, void* stream )
{
	if( buffer == 0 )
	{
		return 0;
	}

	Mix::Plugin::IReader* pReader = reinterpret_cast<Mix::Plugin::IReader*>( stream );

	unsigned long long ui64Size = pReader->GetSize() - pReader->GetPosition();
	size_t sizeCount = static_cast<size_t>( ui64Size ) / size;
	if( sizeCount > maxCount )
	{
		sizeCount = maxCount;
	}

	pReader->Read( buffer, static_cast<unsigned int>( size * sizeCount ) );

	return sizeCount;
}

int Decoder::OVSeek( void* buffer, ogg_int64_t offset, int flag )
{
	Mix::Plugin::IReader* pReader = reinterpret_cast<Mix::Plugin::IReader*>( buffer );

	switch( flag )
	{
	case SEEK_CUR:
		pReader->Seek( Mix::Plugin::IReader::CURRENT, offset );
		break;
	case SEEK_END:
		pReader->Seek( Mix::Plugin::IReader::END, offset );
		break;
	case SEEK_SET:
		pReader->Seek( Mix::Plugin::IReader::BEGIN, offset );
		break;

	default:
		return -1;
	}

	return 0;
}

long Decoder::OVTell( void* buffer )
{
	Mix::Plugin::IReader* pReader = reinterpret_cast<Mix::Plugin::IReader*>( buffer );
	return static_cast<long>( pReader->GetPosition() );
}

int Decoder::OVClose( void* buffer )
{
	return 0;
}
