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


		#include "audio_stream_ogg/audio_stream_ogg.h"
		#include "filesystem/filesystem.h"
		#include "platform.h"
		#include "log/log.h"
		#include "stb_vorbis.c"

		using namespace GS;
		using namespace GS::IO;


//------------------------------------------------------------------------------
bool				AudioStreamOGG::Seek(int t_ms)
{
	/// Rewind the stream and seek to the correct frame.
	int target = (vf->sample_rate * t_ms) / 1000;

	// Logic is broken when seeking below the first packet.
	if	(target < 1024)
	{
		h->Rewind();
		byte_left = 0;

		stb_vorbis_flush_pushdata(vf);		
	}
	else
	{
		// TODO compute seek point and sample to skip.
		int lo = 0, hi = h->GetSize();

		forever
		{
			// Determine seek point.
			int mid = (lo + hi) / 2;

			// Seek and reload push buffer.
			h->Seek(mid, Base::SeekStart);
			RefillBuffer();

			stb_vorbis_flush_pushdata(vf);

			int ns = 0, ch = 0, cs = 0;
			float **fo = NULL;

			forever
			{
				ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));

				if	(ns)	// Samples are coming in.
				{
					cs = stb_vorbis_get_sample_offset(vf);
					if	(cs != -1)	// Sample located.
						break;
				}
			}

			// Check if target sample is within reach.
			if	((cs > (target - 24000)) && (cs <= target))
				forever
				{
					RefillBuffer();
					ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));

					int skip = target - cs;
					if	((cs != -1) && (skip < ns))
					{
						seek_correction = skip;
						return true;
					}
					cs = stb_vorbis_get_sample_offset(vf);
				}

			// Refine approximation.
			if	(cs > target)
				hi = mid;
			else
				lo = mid;
		}
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t				AudioStreamOGG::GetPCMBufferSize() const
{	return vorbis_info.max_frame_size * format.channels * 2 * 2;	}
size_t				AudioStreamOGG::GetPCM(void *pcm)
{
	int ns, ch;
	float **fo = NULL;

	RefillBuffer();
	ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));

	if	(vf->error != VORBIS__no_error)
		return 0;

	if	(seek_correction)
	{
		for (int n = 0; n < ch; ++n)
			fo[n] += seek_correction;
		ns -= seek_correction;
		seek_correction = 0;
	}
	if	(ns < 0)
		ns = 0;

	convert_channels_short_interleaved(ch, (short *)pcm, ch, fo, 0, ns);		// size = ns * 2 * format.channels
	return ns * 2 * format.channels;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t				AudioStreamOGG::RefillBuffer()
{
	size_t request_size = buffer.GetSize() - byte_left;
	size_t read = h->Read(&buffer[(int)byte_left], request_size);
	byte_left += read;
	return read;
}
void				AudioStreamOGG::ConsumeBuffer(size_t size)
{
	memmove(buffer, &buffer[(int)size], byte_left - size);
	byte_left -= size;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				AudioStreamOGG::IsEOF() const
{
	if	(byte_left > 0)
		return false;
	if	(h->IsEOF())
		return true;
	return vf ? asbool(vf->eof) : false;
}
bool				AudioStreamOGG::Open(const char *uri)
{
	h = Platform::Get().io->Open(uri);
	if	(!h)
		return false;
	
	if	(!buffer.Allocate(16384))
		return false;

	RefillBuffer();
	vf = stb_vorbis_open_pushdata((uchar *)buffer.c_ptr(), byte_left, &consumed, &err, NULL);
	if	(!vf)
	{
		h = NULL;
		return false;
	}
	ConsumeBuffer(consumed);

	vorbis_info = stb_vorbis_get_info(vf);
	seek_correction = 0;

	format.channels = vorbis_info.channels;
	format.resolution = 16;
	format.frequency = vorbis_info.sample_rate;

	__LOG_H__ << "Vorbis stream '" << uri << "' - " << vorbis_info.sample_rate << "hz 16bit " << vorbis_info.channels << " channel(s).\n";
	__LOG__ << "    Max frame size = " << vorbis_info.max_frame_size << "\n";
	return true;
}
void				AudioStreamOGG::Close()
{
	if	(vf)
		stb_vorbis_close(vf);
	vf = NULL;

	__LOG_H__ << "Vorbis stream closed.\n";
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
AudioStreamOGG::AudioStreamOGG()
{
	vf = NULL;
	seek_correction = 0;
	byte_left = 0;
}
AudioStreamOGG::~AudioStreamOGG()
{	Close();	}
//------------------------------------------------------------------------------
