/*  SCAVENGER by David Ashley  dash@linuxmotors.com */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
//#include <sys/ioctl.h>
#include <sys/time.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#include <alsa/asoundlib.h>


#include "scav.h"
#include "sound.h"

char dirlist[512];

#define NUMSOUNDS	(sizeof(soundnames)/sizeof(char*))
#define MIXMAX 16

#define SOUND_EXIT -2
#define SOUND_QUIET -1

char *soundnames[] =
{
  "fall.raw",
  "dig.raw",
  "pop.raw",
  "victory.raw",
  "death.raw"
};
typedef struct sample
{
	char *data;
	int len;
} sample;

sample samples[NUMSOUNDS];

int soundworking=0;
int fragment;
int soundwrite,soundread;
int *soundbuffer;
int soundbufferlen;

snd_pcm_t *playback_handle;

void opendsp(int samplerate)
{
	int err;
	snd_pcm_hw_params_t *hw_params;
	char *devname = "default"; // "pulse";

	if ((err = snd_pcm_open (&playback_handle, devname, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
		fprintf (stderr, "cannot open audio device %s (%s)\n", 
			 devname,
			 snd_strerror (err));
		exit (1);
	}
	   
	if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
		fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
			 snd_strerror (err));
		exit (1);
	}
			 
	if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) {
		fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

	if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		fprintf (stderr, "cannot set access type (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

	if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
		fprintf (stderr, "cannot set sample format (%s)\n",
			 snd_strerror (err));
		exit (1);
	}
	unsigned int val=samplerate;

	if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &val, 0)) < 0) {
		fprintf (stderr, "cannot set sample rate (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

	if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 1)) < 0) {
		fprintf (stderr, "cannot set channel count (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

	snd_pcm_uframes_t size = fragment;
	if ((err = snd_pcm_hw_params_set_buffer_size_near (playback_handle, hw_params, &size)) < 0) {
		fprintf (stderr, "cannot set buffer size near (%s)\n",
			 snd_strerror (err));
		exit (1);
	}
//	printf("size = %d\n", (int)size);


	if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) {
		fprintf (stderr, "cannot set parameters (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

	snd_pcm_hw_params_free (hw_params);

	if ((err = snd_pcm_prepare (playback_handle)) < 0) {
		fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
			 snd_strerror (err));
		exit (1);
	}

}


void soundinit(void)
{
	int fd[2];
	char devname[256];
	int value;
	int res;

	sprintf(dirlist,"%s/%s,%s",localname,localdirname,libname);
	soundworking=0;
	res=pipe(fd);res=res;//STFU
	soundread=fd[0];
	soundwrite=fd[1];
	res = fork();
	if(res>0)
	{
		close(soundread);
		return;
	}
	close(soundwrite);
	memset(samples,0,sizeof(samples));

// 10,000 hz mono 8bit samples
	fragment = 256;
	opendsp(10000);

	soundbufferlen=fragment*sizeof(int);
	soundbuffer=malloc(soundbufferlen);
	if(!soundbuffer) goto failed;
	value=fcntl(soundread,F_SETFL,O_NONBLOCK);
	soundworking=1;
failed:
	doall();
	exit(0);
}
int readsound(int num)
{
char name[256],*p1,*p2,ch;
int i,file,size,len;
	p1=dirlist;
	for(;;)
	{
		p2=name;
		while(*p1 && (ch=*p1++)!=',')
			*p2++=ch;
		if(p2>name && p2[-1]!='/') *p2++='/';
		strcpy(p2,soundnames[num]);
		file=open(name,O_RDONLY);
		if(file>=0) break;
		if(!*p1)
		{
			samples[num].len=-1;
			return 0;
		}
	}
	size=lseek(file,0,SEEK_END);
	lseek(file,0,SEEK_SET);
	int expand = 1;
	len=samples[num].len=(size*expand+fragment-1)/fragment;
	len*=fragment;
	p1=samples[num].data=malloc(len);
	if(p1)
	{
		memset(p1, 0, len);
		int got=read(file,p1,size);
		for(i=got-1;i>=0;--i)
		{
			int j;
			for(j=expand-1;j>=0;--j)
				p1[i*expand+j] = p1[i] - 0x80;
		}
	} else
		samples[num].data=0;
	close(file);
}

doall()
{
int i,j;
signed char commands[64],commandlen,com;
signed char *p;
int *ip;
int playing[MIXMAX],position[MIXMAX];
int which;
int *mixbuffer;
short *outbuffer;

	while(!soundworking)
	{
		commandlen=read(soundread,commands,1);
		if(commandlen!=1) {
			/* If we get EOF or EPIPE on the input, it means the
			 * main game process has died; so we quit as well */
			if (commandlen==0 || (commandlen<0 && errno==EPIPE))
				exit(0);
			continue;
		}
		com=*commands;
		if(com==SOUND_EXIT) exit(0);
	}
	for(i=0;i<NUMSOUNDS;++i)
		readsound(i);
	memset(playing,0,sizeof(playing));
	memset(position,0,sizeof(position));
	mixbuffer = malloc(fragment * sizeof(*mixbuffer));
	outbuffer = malloc(fragment * sizeof(*outbuffer));
	for(;;)
	{
		commandlen=read(soundread,commands,64);

		if(commandlen<0)
		{
			commandlen=0;
			if(errno==EPIPE) exit(0);
		} else if(commandlen==0) exit(0);
		signed char *comp=commands;
		while(commandlen--)
		{
			com=*comp++;
			if(com==SOUND_QUIET) {memset(position,0,sizeof(position));continue;}
			if(com==SOUND_EXIT) exit(0);
			if(com<NUMSOUNDS)
			{
				for(i=0;i<MIXMAX;++i)
					if(!position[i])
					{
						position[i]=1;
						playing[i]=com;
						break;
					}
			} else if(com>=64 && com<64+NUMSOUNDS)
			{
				com-=64;
				for(i=0;i<MIXMAX;++i)
					if(position[i] && playing[i]==com)
						position[i]=0;
			}
		}
		memset(mixbuffer, 0, fragment * sizeof(*mixbuffer));
		for(i=0;i<MIXMAX;++i)
		{
			if(!position[i]) continue;
			which=playing[i];
			if(position[i]==samples[which].len)
			{
				position[i]=0;
				continue;
			}
			p=samples[which].data;
			if(!p) continue;
			p+=fragment*(position[i]++ -1);

			for(j=0;j<fragment;++j)
				mixbuffer[j] += 255*p[j];
		}
		for(i=0;i<fragment;++i)
		{
			int t = mixbuffer[i];
			if(t>0x7fff) t=0x7fff;
			if(t<-0x8000) t=-0x8000;
			outbuffer[i] = t;
		}
		int res;
		res = snd_pcm_writei(playback_handle, outbuffer, fragment);
//printf("res=%d\n", res);

	}
}

void playsound(int n)
{
	char c;
	int res;
	c=n;
	res=write(soundwrite,&c,1);res=res;//STFU
}

void endsound(void)
{
	playsound(SOUND_EXIT);
}
