/*
	TODO:
*/

#include <stdlib.h>
#include <string.h>
#include "speechy.h"

#define SILENCE			0x80
#define DEF_SAMPLE_RATE	8000
#define MILLISECONDS	1000

#define BEEP_HIGH		192
#define BEEP_LOW		64
#define BEEP_LEN		500
#define BEEP_FREQ1		120
#define BEEP_FREQ2		60

// Initialize a new voice with "count" amount of allophone slots
SPEECHY_VOICE *speechy_init(unsigned int num, unsigned int freq)
{
	SPEECHY_VOICE *voice;
	SPEECHY_SIGNAL *table;

	if(num>END_SEQ)
		return NULL;

	voice=(SPEECHY_VOICE*)malloc(sizeof(SPEECHY_VOICE));
	if(!voice)
		return NULL;

	table=(SPEECHY_SIGNAL*)calloc(sizeof(SPEECHY_SIGNAL), num);
	if(!table)
	{
		free(voice);
		return NULL;
	}

	if(freq==0)
		freq=DEF_SAMPLE_RATE;

	voice->max=num;
	voice->count=0;
	voice->sequence=NULL;
	voice->pos=0;
	voice->freq=freq;

	voice->table=table;

	return voice;
}


int speechy_add(SPEECHY_VOICE *voice, unsigned char *buffer, 
	unsigned int length, int copy)
{
	SPEECHY_SIGNAL *allophone;

	// Make sure we have a slot left
	if(voice->count>=voice->max)
		return 0;

	allophone=&voice->table[voice->count];

	// Allocate space for the sample data
	if(!buffer||!length)
	{
		allophone->data=NULL;
		allophone->length=voice->freq>>2;
	}
	else
	{
		if(copy)
		{
			allophone->data=malloc(sizeof(unsigned char*)*length);
			if(!allophone->data)
			{
				free(allophone->data);
				return 0;
			}
			allophone->copy=copy;
		}
		else
		{
			allophone->data=buffer;
		}

		// Copy the bytes over
		memcpy(allophone->data, buffer, length);
		allophone->length=length;
	}

	// Increment allophone count
	voice->count++;

	return length;
}


int speechy_add_silence(SPEECHY_VOICE *voice, unsigned int time)
{
	SPEECHY_SIGNAL *allophone;
	unsigned int length;

	// Make sure we have a slot left
	if(voice->count>=voice->max)
		return 0;

	if(!time)
		return 0;

	allophone=&voice->table[voice->count];

	// Calculate the number of samples in "time" milliseconds, and allocate a
	// buffer for it. Set "copy" to TRUE
	length=(voice->freq/MILLISECONDS)*time;

	allophone->data=malloc(sizeof(unsigned char*)*length);
	if(!allophone->data)
	{
		free(allophone->data);
		return 0;
	}
	allophone->copy=length;

	// Copy the bytes over
	memset(allophone->data, SILENCE, length);
	allophone->length=length;

	// Increment allophone count
	voice->count++;

	return length;
}


int speechy_sequence(SPEECHY_VOICE *voice, short *sequence)
{
	unsigned short *ptr;
	int len;

	if(!sequence)
	{
		voice->sequence=NULL;
		voice->pos=0;
		return 0;
	}

	ptr=sequence;
	len=0;

	// Get the total length of the sequence in samples
	while(*ptr!=END_SEQ)
	{
		// TODO: if ERR_SEQ modify sequence
		if(*ptr>=voice->count||*ptr&ERR_SEQ_FLAG||!voice->table[*ptr].data)
		{
			len+=voice->freq>>2;
		}
		else
		{
			len+=voice->table[*ptr].length;
		}
		ptr++;
	}

	voice->sequence=sequence;
	voice->pos=0;

	return len;		
}


int speechy_read(SPEECHY_VOICE *voice, unsigned char *buffer, 
	unsigned int length)
{
	unsigned int i, pos, len, read_count=0, current, next, mod;
	unsigned short *ptr;

	
	if(!voice->sequence || !length || !buffer)
		return 0;

	// TODO: if ERR_SEQ then stream out error beep
	// Set positions to our next sample in the sequence
	ptr=voice->sequence;
	pos=voice->pos;
	current=ptr[0];

	next=1;

	if(current!=END_SEQ)
	{
		// Give required samples to the user
		for(i=0; i<length; i++)
		{
			if(current&ERR_SEQ_FLAG||!(voice->table[current].data))
			{
				if(next)
				{
					len=voice->freq>>2;
					next=0;
				}

				if(pos==0)
				{
					mod=voice->freq/BEEP_FREQ1;
				}
				else if(pos==len>>1)
				{
					mod=voice->freq/BEEP_FREQ2;
				}

				buffer[i]=pos%mod<mod>>1?BEEP_HIGH:BEEP_LOW;
			}
			else
			{
				if(next)
				{
					len=voice->table[current].length;
					next=0;
				}

				buffer[i]=voice->table[current].data[pos];
			}

			pos++;

			// We've hit the end of this allophone, go to the next in sequence
			if(pos>=len)
			{
				pos=0;
				ptr++;
				current=*ptr;
				
				if(current==END_SEQ)
				{
					i++;
					break;
				}

				// Length of new allophone
//				len=voice->table[current].length;
				next=1;
			}
		}

		read_count=i;
	}
	else
	{
		// Already at the end, so we read nothing
		read_count=0;
	}

	// End of the sequence, so reset everything
	if(*ptr==END_SEQ)
	{
		ptr=NULL;
		pos=0;
	}


	// Adjust the voice place-keepers before exiting
	voice->sequence=ptr;
	voice->pos=pos;

	return read_count;
}


void speechy_reset(SPEECHY_VOICE *voice)
{
	voice->sequence=NULL;
	voice->pos=0;
}


void free_allophone(SPEECHY_VOICE *voice, unsigned int index)
{
	SPEECHY_SIGNAL *allophone;

	allophone=&voice->table[index];

	if(allophone&&allophone->data&&allophone->copy)
	{
		free(allophone->data);
	}
}


void speechy_exit(SPEECHY_VOICE *voice)
{
	// Free all the allophones
	while(voice->count)
	{
		free_allophone(voice, voice->count);
		voice->count--;
	}

	free(voice->table);
	free(voice);
}
