#ifndef __myth_defs_h__
#include "myth_defs.h"
#endif

#include <assert.h>

#include "sound_intermediate.h"
#include "adpcm.h"


/* ------- structures */

// This is Apple's format, so don't change it.
// For stereo sounds, the Apple IMA format expects a left channel packet, then a right
// channel packet.

struct apple_ima_sample_data
{
	short state;
	char samples[32];
};

enum
{
	SAMPLES_PER_APPLE_IMA_PACKET = 32 * IMA_SAMPLES_PER_BYTE // 32 bytes * IMA_SAMPLES_PER_BYTE
};

/* ------- local prototypes */
static void decompress_sound( intermediate_sound_data *sound );
static void compress_sound( intermediate_sound_data *sound, short compression );

static void apple_ima_compress_stereo( intermediate_sound_data *sound );
static void apple_ima_compress_mono( intermediate_sound_data *sound );

/* ------- code */

intermediate_sound_data *new_intermediate_sound(	void )
{
	struct intermediate_sound_data *sound;
	sound = (intermediate_sound_data *)malloc( sizeof(intermediate_sound_data) );
	
	if( !sound )
		return NULL;

	memset( sound, 0, sizeof(intermediate_sound_data) );
	sound->magic_cookie = SOUND_MAGIC_COOKIE;
	
	return sound;
}

void dispose_intermediate_sound( intermediate_sound_data *sound )
{
	assert( sound );
	assert( sound->magic_cookie == SOUND_MAGIC_COOKIE );
	
	if( sound->samples )
		free( sound->samples );
		
	free( sound );
}

void initialize_sound_header(
	struct intermediate_sound_data *sound,
	short compression_type,
	long sample_frame_count,
	short uncompressed_size_in_bits,
	long frequency,
	short number_of_channels
	)
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && !sound->initialized );
	
	sound->initialized = TRUE;
	sound->compression_type = compression_type;
	sound->sample_frame_count = sample_frame_count;
	sound->uncompressed_sample_size_in_bits = uncompressed_size_in_bits;
	sound->frequency = frequency;
	sound->number_of_channels = number_of_channels;
	
	return;
}

void set_sound_samples(	intermediate_sound_data *sound,	void *samples ) // assigns pointer- don't free it!
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	assert( sound->samples == NULL );
	
	sound->samples = samples;
}

void set_intermediate_sound_compression_type( intermediate_sound_data *sound, short compression_type )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	
	if( sound->compression_type != compression_type )
	{
		// must do something
		if( sound->compression_type != _no_compression )
			decompress_sound(sound);

		compress_sound( sound, compression_type );
	}
}

short get_intermediate_sound_compression_type( intermediate_sound_data *sound )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	return sound->compression_type;
}

void set_uncompressed_sample_size_in_bits( intermediate_sound_data *sound, short size )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	if( sound->uncompressed_sample_size_in_bits != size )
	{
		short compression_type;
		
		compression_type = get_intermediate_sound_compression_type( sound );
		
		// can only adjust sample size on uncompressed sounds...
		decompress_sound( sound );
		
		// only support 8 and 16 bits for now
		switch( sound->uncompressed_sample_size_in_bits )
		{
			case 16:
				switch( size )
				{
					case 16:
						break;
						
					case 8:
						// downsampling
						byte *new_samples;
						long number_of_samples = sound->number_of_channels * sound->sample_frame_count;
						
						new_samples = (byte *)malloc( number_of_samples * sizeof(byte) );
						if( new_samples )
						{
							long sample;
							short *source = (short *)sound->samples;
							
							for( sample= 0; sample < number_of_samples; ++sample )
							{
								new_samples[sample] = ((source[sample] >> 8) & 0xff) + 0x80;
							}
							
							free( sound->samples );
							
							sound->samples = new_samples;
							sound->uncompressed_sample_size_in_bits = size;
						}
						
						break;
						
					default:
						// FIXME:
						assert( 0 );
				}
				break;
				
			case 8:
				switch (size)
				{
					case 16:
						// upsampling
						{
							unsigned short *new_samples;
							long number_of_samples = sound->number_of_channels * sound->sample_frame_count;
							
							new_samples = (unsigned short *)malloc( number_of_samples  *sizeof(unsigned short) );
							if( new_samples )
							{
								long sample;
								byte *source = (byte *)sound->samples;
								
								for( sample = 0; sample < number_of_samples; ++sample )
								{
									short upsample;
									
									upsample = (source[sample] - 0x80) << 8;
									new_samples[sample] = sample;
								}
								
								free( sound->samples );
								
								sound->samples = new_samples;
								sound->uncompressed_sample_size_in_bits = size;
							}
						}
						break;
						
					case 8:
						break;
						
					default:
						// FIXME:
						assert( 0 );
				}
				break;
				
			default:
				// FIXME:
				assert( 0 );
		}
		
		// restore compression type.
		compress_sound( sound, compression_type );
	}
	
	return;
}

short get_uncompressed_sample_size_in_bits(	intermediate_sound_data *sound )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );

	return sound->uncompressed_sample_size_in_bits;
}

void set_intermediate_sound_frequency( intermediate_sound_data *sound, long frequency )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	if( frequency != sound->frequency )
	{
		// FIXME:
		assert( 0 );
	}
	
	return;
}

long get_intermediate_sound_frequency( intermediate_sound_data *sound )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	return sound->frequency;
}

long get_intermediate_sound_sample_count( intermediate_sound_data *sound )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	return sound->number_of_channels * sound->sample_frame_count;
}

void *get_intermediate_sound_samples( intermediate_sound_data *sound )
{
	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );
	return sound->samples;
}

long get_samples_size( intermediate_sound_data *sound )
{
	long number_of_samples, sample_size;

	assert( sound && sound->magic_cookie == SOUND_MAGIC_COOKIE && sound->initialized );

	// Multiply by the number of channels (to account for stereo data)
	number_of_samples = sound->number_of_channels * sound->sample_frame_count;
	sample_size = (sound->uncompressed_sample_size_in_bits >> 3) * number_of_samples;

	switch( sound->compression_type )
	{
		case _no_compression:
			break;
			
#ifdef OBSOLETE
		case _ima_adpcm_compression:
			// not sure what happens if you do this with an eight bit sample..
			sample_size = sample_size / IMA_SAMPLES_PER_BYTE + (sample_size & 1);
			break;
#endif
			
		case _apple_ima_adpcm_compression:
			// apple has 2 bytes of header, followed by 64 samples
			// 64 samples= 32 bytes, therefore:
			sample_size = number_of_samples * sizeof(apple_ima_sample_data);
			break;
			
		default:
			// FIXME:
			assert( 0 );
			break;
	}
	
	return sample_size;
}

long get_intermediate_sample_count( intermediate_sound_data *sound )
{
	assert( sound );
	return sound->number_of_channels * sound->sample_frame_count;
}

/* ----------- local code */
static void decompress_sound( intermediate_sound_data *sound )
{
	switch( sound->compression_type )
	{
		case _no_compression:
			break;
			
#ifdef OBSOLETE
		case _ima_adpcm_compression:
			// decompress
			{
				long number_of_samples = sound->number_of_channels * sound->sample_frame_count;
				short *decompressed_samples;

				decompressed_samples = (short *)malloc( number_of_samples * sizeof(short) );
				if( !decompressed_samples )
					// FIXME:
					assert( 0 );

				if( decompressed_samples )
				{
					adpcm_decompress( (char *)sound->samples, number_of_samples, decompressed_samples );
					free( sound->samples );
					
					sound->samples = decompressed_samples;
					sound->compression_type = _no_compression;
				}
			}
			break;
#endif
			
		case _apple_ima_adpcm_compression:
			// decompress
			{
				long number_of_samples = sound->number_of_channels * sound->sample_frame_count;
				long actual_sample_count = number_of_samples * SAMPLES_PER_APPLE_IMA_PACKET;
				short *decompressed_samples;
				
				decompressed_samples = (short *)malloc( actual_sample_count * sizeof(short) );
				if( decompressed_samples )
				{
					long sample_index;
					short *destination = decompressed_samples;
					struct apple_ima_sample_data *sample_data = (struct apple_ima_sample_data *)sound->samples;

					for( sample_index = 0; sample_index < number_of_samples; ++sample_index )
					{
						apple_adpcm_packet_decompress(
							sample_data->state,
							sample_data->samples,
							destination,
							SAMPLES_PER_APPLE_IMA_PACKET
							);
						
						destination += SAMPLES_PER_APPLE_IMA_PACKET;
						sample_data++;
					}
					
					free( sound->samples );
					
					sound->sample_frame_count = actual_sample_count / sound->number_of_channels;
					sound->samples = decompressed_samples;
					sound->compression_type = _no_compression;
				}
				else
					// FIXME:
					assert( 0 );
			}
			break;
			
		default:
			// FIXME:
			assert( 0 );
	}
	
	return;
}

static void compress_sound( intermediate_sound_data *sound, short compression )
{
	long number_of_samples= sound->number_of_channels*sound->sample_frame_count;
	
	assert(sound->compression_type==_no_compression);
	switch (compression)
	{
		case _no_compression:
			break;
			
#ifdef OBSOLETE
		case _ima_adpcm_compression:
			{
				long new_sample_size;
				char *new_samples;
				
				// every sample becomes 4 bits...
				new_sample_size = (number_of_samples / 2 + (number_of_samples & 1) );
				new_samples = (char *)malloc( new_sample_size );
				if( !new_samples)
					// FIXME
					assert( 0 );
				
				// FIXME: this should have something better than an assert
				assert( sound->uncompressed_sample_size_in_bits == 16 );
				
				adpcm_compress( (short *)sound->samples, number_of_samples, new_samples );
				free( sound->samples );
				
				sound->compression_type = compression;
				sound->samples = new_samples;
			}
			break;
#endif
			
		case _apple_ima_adpcm_compression:
			assert( sound->uncompressed_sample_size_in_bits == 16 );
			
			switch( sound->number_of_channels )
			{
				case 2:
					apple_ima_compress_stereo( sound );
					break;
					
				case 1:
					apple_ima_compress_mono( sound );
					break;
					
				default:
					// FIXME:
					assert( 0 );
			}
			break;
		
		default:
			// FIXME
			assert( 0 );
	}
	
	return;
}

static void apple_ima_compress_mono( intermediate_sound_data *sound )
{
	assert( sound->number_of_channels == 1 );

	/*
		Simply take the original samples, grab them in chunks of packets, and compress them.
	*/
	int number_of_samples = sound->sample_frame_count;

	// Pad, if necessary, to an even multiple of SAMPLES_PER_APPLE_IMA_PACKET
	int packet_count =
		(number_of_samples / SAMPLES_PER_APPLE_IMA_PACKET) +
		((number_of_samples % SAMPLES_PER_APPLE_IMA_PACKET) ? 1 : 0);
		
	int padded_samples_per_channel = packet_count * SAMPLES_PER_APPLE_IMA_PACKET;
	int padded_length = sizeof(short) * padded_samples_per_channel;

	int silence = (sound->uncompressed_sample_size_in_bits==8) ? 0x80 : 0;
	short *samples= (short *)malloc( padded_length );
	if( !samples )
		// FIXME:
		assert( 0 );

	// Initially fill with silence and copy the original data into the padded buffer.
	memset( samples, silence, padded_length );
	memcpy( samples, sound->samples, sizeof(short) * number_of_samples );
	
	// ADPCM compress padded buffer
	{
		char *new_samples = (char *)malloc( packet_count * sizeof(apple_ima_sample_data) );
		if( new_samples )
		{
			apple_ima_sample_data *sample_data = (apple_ima_sample_data *)new_samples;

			short *source = samples;
			short adpcm_index = 0;
			short adpcm_predicted = 0;

			long index;

			for( index= 0; index < packet_count; ++index )
			{
			 	// These two bytes of "state" contains the step index, a 7 bit value in the
			 	// low byte, and the 9 most significant bits of the predictor value.

				sample_data->state = (short)((word)adpcm_predicted & 0xff80) | (adpcm_index & 0x7f);
				apple_adpcm_compress(
					source,
					SAMPLES_PER_APPLE_IMA_PACKET,
					sample_data->samples,
					&adpcm_index,
					&adpcm_predicted
					);
					
				sample_data++;
				source += SAMPLES_PER_APPLE_IMA_PACKET;
			}

			free( sound->samples );
			
			sound->samples = new_samples;
			sound->compression_type = _apple_ima_adpcm_compression;
			sound->sample_frame_count = packet_count; // Seems odd, but each packet is a "frame" once compressed.
		}
		else
			// FIXME
			assert( 0 );
	}
	
	// Release the sample buffer
	free( samples );
	
	return;
}

static void apple_ima_compress_stereo( intermediate_sound_data *sound )
{
	assert( sound->number_of_channels == 2 );

	/*
	The original stereo sound samples (from the AIFF chunk) come in the format
	L0 R0 L1 R1 L2 R2 L3 R3 .... Ln Rn
	Unfortunately, the Mac's IMA ADPCM packet format wants to get stereo data as a packet of left,
	followed by a packet of right.  This makes sense, but is a total pain in the ass for us.  Thus,
	if there are, say, 3 samples per packet, the Mac wants this:
	L0 L1 L2 R0 R1 R2 .... Ln Ln+1 Ln+2 Rn Rn+1 Rn+2
	*/
	
	int number_of_samples_per_channel = sound->sample_frame_count;

	// Pad, if necessary, to an even multiple of SAMPLES_PER_APPLE_IMA_PACKET
	int packets_per_channel =
		(number_of_samples_per_channel / SAMPLES_PER_APPLE_IMA_PACKET) +
		((number_of_samples_per_channel % SAMPLES_PER_APPLE_IMA_PACKET) ? 1 : 0);
		
	int padded_samples_per_channel = packets_per_channel * SAMPLES_PER_APPLE_IMA_PACKET;
	int padded_length = sizeof(short) * padded_samples_per_channel;
	int total_packet_count = sound->number_of_channels * packets_per_channel;

	int silence = (sound->uncompressed_sample_size_in_bits == 8) ? 0x80 : 0;

	short *left_samples = (short *)malloc( padded_length );
	short *right_samples = (short *)malloc( padded_length );
	
	if( !left_samples || !right_samples )
		// FIXME
		assert( 0 );

	// Initially fill with silence
	memset( left_samples, silence, padded_length );
	memset( right_samples, silence, padded_length );	
	
	// Split the old sound samples into left and right sample buffers
	{
		short *source = (short *)sound->samples;
		short *left = left_samples;
		short *right = right_samples;
		int index;
	
		for( index= 0; index < number_of_samples_per_channel; ++index )
		{
			*left++ = *source++;
			*right++ = *source++;
		}
	}

	// ADPCM compress the left and right buffers, alternating left packet, right packet...
	{
		char *new_samples = (char *)malloc( total_packet_count * sizeof(struct apple_ima_sample_data) );
		
		if( new_samples )
		{
			struct apple_ima_sample_data *sample_data = (apple_ima_sample_data *)new_samples;

			short *left = left_samples;
			short left_adpcm_index = 0;
			short left_adpcm_predicted = 0;

			short *right = right_samples;
			short right_adpcm_index = 0;
			short right_adpcm_predicted = 0;
			long index;

			for( index= 0; index < packets_per_channel; ++index )
			{
			 	// These two bytes of "state" contains the step index, a 7 bit value in the
			 	// low byte, and the 9 most significant bits of the predictor value.

				sample_data->state = (short)((word) left_adpcm_predicted & 0xff80) | (left_adpcm_index & 0x7f);
				apple_adpcm_compress(
					left,
					SAMPLES_PER_APPLE_IMA_PACKET,
					sample_data->samples,
					&left_adpcm_index,
					&left_adpcm_predicted
					);
					
				sample_data++;
				left += SAMPLES_PER_APPLE_IMA_PACKET;

				sample_data->state= (short)((word) right_adpcm_predicted & 0xff80) | (right_adpcm_index & 0x7f);
				apple_adpcm_compress(
					right,
					SAMPLES_PER_APPLE_IMA_PACKET,
					sample_data->samples,
					&right_adpcm_index,
					&right_adpcm_predicted
					);
					
				sample_data++;
				right += SAMPLES_PER_APPLE_IMA_PACKET;
			}

			free( sound->samples );
			
			sound->samples = new_samples;
			sound->compression_type = _apple_ima_adpcm_compression;
			sound->sample_frame_count = packets_per_channel; // Seems odd, but each packet is a "frame" once compressed.
		}
		else
		{
			// FIXME:
			assert( 0 );
		}
		
	}
	
	// Release the left and right sample buffers
	free( left_samples );
	free( right_samples );
	
	return;
}
