#include "flv_mp3_decoder.h"
#include "../nsutil/pcm.h"

int FLVDecoderCreator::CreateAudioDecoder(int stereo, int bits, int sample_rate, int format_type, ifc_flvaudiodecoder **decoder)
{
	if (format_type == FLV::AUDIO_FORMAT_MP3 || format_type == FLV::AUDIO_FORMAT_MP3_8KHZ)
	{
		mpg123_handle *ctx = mpg123_new(NULL, NULL);
		if (!ctx)
			return CREATEDECODER_FAILURE;

		long flags = MPG123_QUIET|MPG123_FORCE_FLOAT|MPG123_SKIP_ID3V2|MPG123_IGNORE_STREAMLENGTH|MPG123_IGNORE_INFOFRAME;
		if (!stereo) {
			flags |= MPG123_FORCE_MONO;
		}
		mpg123_param(ctx, MPG123_FLAGS, flags, 0);
		mpg123_param(ctx, MPG123_RVA, MPG123_RVA_OFF, 0);

		*decoder = new FLVMP3(ctx);
		return CREATEDECODER_SUCCESS;
	}
	return CREATEDECODER_NOT_MINE;
}

int FLVDecoderCreator::HandlesAudio(int format_type)
{
	if (format_type == FLV::AUDIO_FORMAT_MP3 || format_type == FLV::AUDIO_FORMAT_MP3_8KHZ)
	{
		return CREATEDECODER_SUCCESS;
	}
	return CREATEDECODER_NOT_MINE;
}

#define CBCLASS FLVDecoderCreator
START_DISPATCH;
CB(CREATE_AUDIO_DECODER, CreateAudioDecoder)
CB(HANDLES_AUDIO, HandlesAudio)
END_DISPATCH;
#undef CBCLASS

/* --- */
#define FHG_DELAY 529
FLVMP3::FLVMP3(mpg123_handle *mp3) : mp3(mp3)
{
	mpg123_open_feed(mp3);
	bits = 16;
	pregap = FHG_DELAY;
	max_channels = 2;
	channels = 0;
	decode_buffer = 0;
	decode_buffer_length = 0;
}

FLVMP3::~FLVMP3()
{
	if (mp3) {
		mpg123_delete(mp3);
		mp3 = 0;
	}
	free(decode_buffer);
}

int FLVMP3::GetOutputFormat(unsigned int *sample_rate, unsigned int *_channels, unsigned int *_bits)
{
	mpg123_frameinfo frameInfo;
	if (mpg123_info(mp3, &frameInfo) == MPG123_OK)
	{
		*sample_rate = frameInfo.rate;
		channels = (frameInfo.mode == MPG123_M_MONO)?1:2;
		*_channels = channels;
		*_bits = bits;
		return FLV_AUDIO_SUCCESS;
	}
	else
	{
		return FLV_AUDIO_FAILURE;
	}
}

int FLVMP3::DecodeSample(const void *input_buffer, size_t input_buffer_bytes, void *samples, size_t *samples_size_bytes, double *bitrate)
{
	if (!mp3)
		return FLV_AUDIO_FAILURE;

	mpg123_feed(mp3, (unsigned char *)input_buffer, input_buffer_bytes);

	*samples_size_bytes = 0;
	*bitrate = 0;

	for (;;) {
		if (!decode_buffer) {
			int channels = 2;
			long sample_rate;
			int encoding;
			if (mpg123_getformat(mp3, &sample_rate, &channels, &encoding) == MPG123_OK) {
				this->channels = channels;
				decode_buffer_length = sizeof(float) * channels * mpg123_spf(mp3);
				decode_buffer = (float *)malloc(decode_buffer_length);
				if (!decode_buffer) {
					return FLV_AUDIO_FAILURE;
				}
			}
		}

		// get the decoded data out
		size_t pcm_buf_used=0;
		int err = mpg123_read(mp3, (unsigned char *)decode_buffer, decode_buffer_length, &pcm_buf_used);

		if (pcm_buf_used) {
			if (!channels) {
				long sample_rate = 44100;
				int channels = 2;
				int encoding = 0;
				if (mpg123_getformat(mp3, &sample_rate, &channels, &encoding) == MPG123_OK) {
					this->channels = channels;
				} else {
					return FLV_AUDIO_FAILURE;
				}
			}

			// deal with pregap
			size_t numSamples = pcm_buf_used / sizeof(float);
			size_t offset = min(numSamples, pregap * channels);
			numSamples -= offset;
			pregap -= (int)offset / channels;
			float *pcm_buf = decode_buffer + offset;

			// convert to destination sample format
			
			nsutil_pcm_FloatToInt_Interleaved(samples, pcm_buf, bits, numSamples);

			*samples_size_bytes += numSamples * bits / 8;
			samples = (char *)samples + numSamples * bits / 8;

			mpg123_frameinfo frameInfo;
			if (mpg123_info(mp3, &frameInfo) == MPG123_OK) {
				*bitrate = frameInfo.bitrate;
			}
			return FLV_AUDIO_SUCCESS;
		} else if (err == MPG123_NEED_MORE) {
			*samples_size_bytes = 0;
			return FLV_AUDIO_NEEDS_MORE_INPUT;
		} else if (err == MPG123_NEW_FORMAT) {
			continue;
		} else if (err == MPG123_OK) {
			continue;
		}
		else
			return FLV_AUDIO_FAILURE;
	}
	return FLV_AUDIO_SUCCESS;
}

void FLVMP3::Flush()
{
	mpg123_open_feed(mp3);
	pregap = FHG_DELAY;
}

void FLVMP3::Close()
{
	if (mp3) {
		mpg123_delete(mp3);
		mp3 = 0;
	}
	delete this;
}

void FLVMP3::SetPreferences(unsigned int _max_channels, unsigned int preferred_bits)
{
	if (preferred_bits)
		bits = preferred_bits;

	if (max_channels > _max_channels)
		max_channels = _max_channels;

	long flags = MPG123_QUIET|MPG123_FORCE_FLOAT|MPG123_SKIP_ID3V2|MPG123_IGNORE_STREAMLENGTH|MPG123_IGNORE_INFOFRAME;
	if (max_channels == 1) {
		flags |= MPG123_FORCE_MONO;
	}
	mpg123_param(mp3, MPG123_FLAGS, flags, 0);
	mpg123_param(mp3, MPG123_RVA, MPG123_RVA_OFF, 0);

}

#define CBCLASS FLVMP3
START_DISPATCH;
CB(FLV_AUDIO_GETOUTPUTFORMAT, GetOutputFormat)
CB(FLV_AUDIO_DECODE, DecodeSample)
VCB(FLV_AUDIO_FLUSH, Flush)
VCB(FLV_AUDIO_CLOSE, Close)
VCB(FLV_AUDIO_SETPREFERENCES, SetPreferences)
END_DISPATCH;
#undef CBCLASS