#include "Channels.h"
#include "Utils.h"

#define CH_LAYOUT_MAX 18

static const int channels_panning[CH_LAYOUT_MAX] =
{   0 // Front Left
, 256 // Front Right
, 128 // Front Center
, 128 // Low Frequency
,   0 // Back Left
, 256 // Back Right
,  64 // Front Center Left
, 196 // Front Center Right
, 128 // Back Center
,   0 // Side Left
, 256 // Side Right
, 128 // Top Center
,   0 // Top Front Left
, 128 // Top Front Center
, 256 // Top Front Right
,   0 // Top Back Left
, 128 // Top Back Center
, 256 // Top Back Right
};

// SMPTE/ITU-R recommandations (for 1-6 channels at least)
static const unsigned int channels_layout[9] =
{ 0x000 // Nothing
, 0x004 // Mono
, 0x003 // Stereo
, 0x007 // Surround
, 0x033 // 4.0 (Stereo + Back)
, 0x037 // 5.0 (Surround + Back)
, 0x03F // 5.1 (Surround + Back + Low Freq)
, 0x70F // 7.0 (Surround + Sides + Back center + Low Freq)
, 0x63F // 7.1 (Surround + Back + Sides + Low Freq)
};

unsigned int Channels_GetDefaultLayout(unsigned int channels)
{
	if (channels > 8)
		return (1 << channels) - 1; // not defined so just put anything
	else
		return channels_layout[channels];
}

void Channels_Init(IStream* s, int16_t* ob, int size, unsigned int flags, int freq)
{
	int i, j;
	MixStream* stream = &s->ChannelsArray[0];

	if (s->info.Channels < 2)
		flags &= ~mixstream_flag_interlaced;
	else if (flags & mixstream_flag_interlaced)
		size /= s->info.Channels;

	for (i = 0, j = 0; i < s->info.Channels; i++)
	{
		stream->smp_ptr = (void*) ob;
		stream->pos = 0;
		stream->fpos = 0;
		stream->last_smp_value = 0;
		stream->smp_lstart = 0;
		stream->smp_lend = size;
		stream->flags = flags;
		stream->frequency = freq;

		// Channel position from layout
		stream->panning = 128; // default just in case
		for (;j < CH_LAYOUT_MAX; j++)
		{
			if (s->info.ChannelsLayout & (1 << j))
			{
				stream->panning = channels_panning[j];
				j++;
				break;
			}
		}

		stream++;
		if (flags & mixstream_flag_interlaced)
			ob++;
		else
			ob += size;
	}
}

void Channels_Init32(IStream* s, int32_t* ob, int size, int freq)
{
	int i, j;
	MixStream* stream = &s->ChannelsArray[0];

	for (i = 0, j = 0; i < s->info.Channels; i++)
	{
		// use top 16 bits
		stream->smp_ptr = ((unsigned char*) ob) + 2;
		stream->pos = 0;
		stream->fpos = 0;
		stream->last_smp_value = 0;
		stream->smp_lstart = 0;
		stream->smp_lend = size;
		stream->flags = mixstream_flag_forward|mixstream_flag_16bit|mixstream_flag_interlaced;
		stream->frequency = freq;

		// Channel position from layout
		stream->panning = 128; // default just in case
		for (;j < CH_LAYOUT_MAX; j++)
		{
			if (s->info.ChannelsLayout & (1 << j))
			{
				stream->panning = channels_panning[j];
				j++;
				break;
			}
		}

		stream++;
		ob += size;
	}
}

void Channels_List(IStream* s, int channels)
{
	int i;
	MixStream* stream = &s->ChannelsArray[0];

	for (i = 0; i < channels; i++)
	{
		s->StreamList[i] = stream;
		stream++;
	}
}

void Channels_Reset(IStream* s)
{
	int i;
	MixStream* stream = &s->ChannelsArray[0];

	for (i = 0; i < s->info.Channels; i++)
	{
		stream->pos = 0;
		stream->fpos = 0;
		stream->last_smp_value = 0;

		stream++;
	}
}

#define ExpFactor 4295 // 2^32 / 1000000

int Channel_DurationToSamples(MixStream* stream, int fill)
{
	return CFN_mul16(CFN_mul16(fill, stream->frequency), ExpFactor) >> 8;
}
