#include "main.h"
#include "GainLayer.h"
#include <malloc.h>
#include <math.h>
#include <locale.h>
#include "api.h"

static void FillFloat(float *floatBuf, void *samples, size_t bps, size_t numSamples, size_t numChannels, float preamp)
{
	switch (bps)
	{
	case 8:
		{
			unsigned __int8 *samples8 = (unsigned __int8 *)samples;
			size_t totalSamples = numSamples * numChannels;
			for (size_t x = 0; x != totalSamples; x ++)
			{
				floatBuf[x] = (float)(samples8[x] - 128) * preamp;
			}
		}
		break;
	case 16:
		{
			short *samples16 = (short *)samples;
			size_t totalSamples = numSamples * numChannels;
			for (size_t x = 0; x != totalSamples; x ++)
			{
				floatBuf[x] = (float)samples16[x] * preamp;
			}
		}
		break;
	case 24:
		{
			unsigned __int8 *samples8 = (unsigned __int8 *)samples;
			size_t totalSamples = numSamples * numChannels;
			for (size_t x = 0; x != totalSamples; x ++)
			{
				long temp = (((long)samples8[0]) << 8);
				temp = temp | (((long)samples8[1]) << 16);
				temp = temp | (((long)samples8[2]) << 24);
				floatBuf[x] = (float)temp * preamp;
				samples8 += 3;
			}
		}
		break;
	}
}

inline static float fastclip(float x, const float a, const float b)
{
	float x1 = (float)fabs (x - a);
	float x2 = (float)fabs (x - b);
	x = x1 + (a + b);
	x -= x2;
	x *= 0.5f;
	return (x);
}

#define PA_CLIP_( val, min, max )\
	{ val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }

void Float32_To_Int16_Clip(
    void *destinationBuffer, signed int destinationStride,
    void *sourceBuffer, signed int sourceStride,
    unsigned int count)
{
	float *src = (float*)sourceBuffer;
	signed short *dest = (signed short*)destinationBuffer;

	while ( count-- )
	{
		long samp = lrint(*src);

		PA_CLIP_( samp, -0x8000, 0x7FFF );
		*dest = (signed short) samp;

		src += sourceStride;
		dest += destinationStride;
	}
}
inline static void clip(double &x, double a, double b)
{
	double x1 = fabs (x - a);
	double x2 = fabs (x - b);
	x = x1 + (a + b);
	x -= x2;
	x *= 0.5;
}

void Float32_To_Int24_Clip(
    void *destinationBuffer, signed int destinationStride,
    void *sourceBuffer, signed int sourceStride,
    unsigned int count)
{
	float *src = (float*)sourceBuffer;
	unsigned char *dest = (unsigned char*)destinationBuffer;

	while ( count-- )
	{
		/* convert to 32 bit and drop the low 8 bits */
		double scaled = *src;
		clip( scaled, -2147483648., 2147483647. );
		signed long temp = (signed long) scaled;

		dest[0] = (unsigned char)(temp >> 8);
		dest[1] = (unsigned char)(temp >> 16);
		dest[2] = (unsigned char)(temp >> 24);
		src += sourceStride;
		dest += destinationStride * 3;
	}
}

static void FillSamples(void *samples, float *floatBuf, size_t bps, size_t numSamples, size_t numChannels)
{
	switch (bps)
	{
	case 16:
		Float32_To_Int16_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
		break;
	case 24:
		Float32_To_Int24_Clip(samples, 1, floatBuf, 1, numSamples*numChannels);
		break;

	}
}

float GetGain(WMInformation *info, bool allowDefault)
{
	if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
	{
		float dB = 0, peak = 1.0f;
		wchar_t gain[64]=L"", peakVal[64]=L"";
		switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
		{
		case 0:    // track
			info->GetAttribute(L"replaygain_track_gain", gain, 64);
			if (!gain[0]	&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
				info->GetAttribute(L"replaygain_album_gain", gain, 64);

			info->GetAttribute(L"replaygain_track_peak", peakVal, 64);
			if (!peakVal[0]	&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
				info->GetAttribute(L"replaygain_album_peak", peakVal, 64);

			break;
		case 1:
			info->GetAttribute(L"replaygain_album_gain", gain, 64);
			if (!gain[0]	&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
				info->GetAttribute(L"replaygain_track_gain", gain, 64);

			info->GetAttribute(L"replaygain_album_peak", peakVal, 64);
			if (!peakVal[0]	&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
				info->GetAttribute(L"replaygain_track_peak", peakVal, 64);

			break;
		}

		_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();

		if (gain[0])
		{
			if (gain[0] == L'+')
				dB = static_cast<float>(_wtof_l(&gain[1],C_locale));
			else
				dB = static_cast<float>(_wtof_l(gain,C_locale));
		}
		else if (allowDefault)
		{
			dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
			return powf(10.0f, dB / 20.0f);
		}

		if (peakVal[0])
		{
			peak = static_cast<float>(_wtof_l(peakVal,C_locale));
		}

		switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
		{
		case 0:    // apply gain
			return powf(10.0f, dB / 20.0f);
		case 1:    // apply gain, but don't clip
			return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
		case 2:    // normalize
			return 1.0f / peak;
		case 3:    // prevent clipping
			if (peak > 1.0f)
				return 1.0f / peak;
			else
				return 1.0f;
		}

	}

	return 1.0f; // no gain
}


void GainLayer::AudioDataReceived(void *_data, unsigned long sizeBytes, DWORD timestamp)
{
	if (enabled)
	{
		size_t samples = audio->AudioBytesToSamples(sizeBytes);
		int channels = audio->Channels();
		if (floatSize < (samples * channels))
		{
			delete [] floatData;
			floatSize = samples * channels;
			floatData = new float[floatSize];
		}
		if (outSize < sizeBytes)
		{
			delete [] outData;
			outSize=sizeBytes;
			outData = (void *)new __int8[sizeBytes];
		}
		
		FillFloat(floatData, _data, audio->BitSize(), samples , channels, replayGain);
		FillSamples(outData, floatData, audio->BitSize(), samples , channels);

		WMHandler::AudioDataReceived(outData, sizeBytes, timestamp);
	}
	else
		WMHandler::AudioDataReceived(_data, sizeBytes, timestamp);
}

void GainLayer::Opened()
{
	enabled=	(AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false));
	if (enabled)
		replayGain = GetGain(info, true);
	WMHandler::Opened();
}