#include <v_lib.h>

/*
 * This is regressive reentrance test for our MP3 library.
 * The app creates num_threads threads and dispatches the mp3 files to them,
 * writing decoded data into M$ wave files.
 *
 * It assumes that there are a lot of mp3 files in subdirs of the current
 * directory. Do not call it from /root :)
 */

/*
 * List of source files: will be initialized before processing.
 */
vp_stack_t	src_files = TYPED_STACK_EMPTY_INITIALIZER;
/*
 * All destination files will be stored to "./out/<old_filename>.mp3".
 */
vp_stack_t	dst_files = TYPED_STACK_EMPTY_INITIALIZER;

/*
 * Helper routine used during recursive searsh for music.
 */
void V_CALL cbk(char *fname)
{
	char dst[_MAX_PATH] = {0};
	char dname[_MAX_PATH] = {0};
	char dext[_MAX_PATH] = {0};
	fsSplitPath(fname,NULL,dname,dext);
	if(!strcasecmp(dext,"mp3"))
	{
		vpsPush(&src_files,strDup(fname));
		fsMakePath(dst,"./out",dname,"wav");
		vpsPush(&dst_files,strDup(dst));
	}
}

void clear_stacks()
{
	void *ptr;
	while(vpsCount(&src_files))
	{
		vpsPop(&src_files,&ptr);
		memFree(ptr);
		vpsPop(&dst_files,&ptr);
		memFree(ptr);
	}
	vpsClear(&src_files);
	vpsClear(&dst_files);
}

/*
 * Decoder routine: should be reentrant.
 */
#define	DEC_SIZE	4096

void decode_data(char *src_file,char *dst_file)
{
	vhandle_t in, out;
	u32_t nc,bps,freq;
	u8_t decompressed[DEC_SIZE];
	vsize_t len;
	in = mp3Open(src_file);
	if(!in)
		return;
	fileControl(in,pctl_wave_get_num_channels,&nc);
	fileControl(in,pctl_wave_get_sampling_frequency,&freq);
	fileControl(in,pctl_wave_get_bits_per_sample,&bps);
	out = wavOpenWrite(dst_file,nc,freq,bps);
	if(out == LIBV_INVALID_HANDLE)
	{
		fileClose(in);
		return;
	}
	for(;;)
	{
		len = fileRead(in,decompressed,DEC_SIZE);
		if(!len)
			break;
		len = fileWrite(out,decompressed,len);
	}
	fileClose(in);
	fileClose(out);
}

#define		MAX_THREADS	16

int			num_threads	= 4; /* todo: control from cmdline */

vhandle_t	threads[MAX_THREADS];
vhandle_t	locks[MAX_THREADS];
char		*snames[MAX_THREADS];
char		*dnames[MAX_THREADS];
vboolean	ready[MAX_THREADS];
vboolean	done[MAX_THREADS];
int			num_proc = 0;

/*
 * Thread procedure.
 */
void V_CALL	decoder(void *data)
{
	int dec_no = (int)data;
	printf("Decoder No %i started; passive.\n", dec_no);
	/* wait for 1st task */
	condWait(locks[dec_no]);
	printf("Decoder No %i active.\n", dec_no);
	while(!done[dec_no])
	{
		printf("Decoder No %i: %s -> %s.\n", dec_no, snames[dec_no], dnames[dec_no]);
		decode_data(snames[dec_no],dnames[dec_no]);
		num_proc++; /* atomic */
		memFree(snames[dec_no]);
		snames[dec_no] = NULL;
		memFree(dnames[dec_no]);
		dnames[dec_no] = NULL;
		ready[dec_no] = true;
		printf("Decoder No %i: waiting for next task.\n", dec_no);
		/* wait for next task */
		condWait(locks[dec_no]);
	}
	printf("Decoder No %i finished.\n", dec_no);
	threadExit(0);
}

int main()
{
	char *sname, *dname;
	int i, j, n_files;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	fsEnumFiles(NULL,cbk,true);
	n_files = vpsCount(&src_files);
	printf("queued to decode: %u files\n",n_files);
	for(i = 0;i < num_threads;i++)
	{
		locks[i] = condCreate(true);
		condReset(locks[i]);
		ready[i] = true;
		done[i] = false;
		threads[i] = threadCreate(decoder,(void_p)i);
	}
	for(j = 0;j < n_files;j++)
	{
		printf("Files dispatched: %i, files processed %i.\n",j,num_proc);
		vpsPop(&src_files,(void*)&sname);
		vpsPop(&dst_files,(void*)&dname);
		/* find ready decoder */
		for(;;timeDelay(10))
		{
			for(i = 0;i < num_threads;i++)
			{
				if(!ready[i])
					continue;
				ready[i] = false;
				snames[i] = sname;
				dnames[i] = dname;
				printf("Output %s dispatched to thread %i.\n",dname,i);
				condSet(locks[i]);
				goto next_file;
			}
			/* there is none: wait some time */
		}
next_file:;
	}
	puts("All done; waiting for completion of all threads.");
	for(i = 0;i < num_threads;i++)
	{
		while(!ready[i])
			timeDelay(100);
		printf("Destroying thread %i\n",i);
		done[i] = true;
		condSet(locks[i]);
		threadJoin(threads[i],NULL);
		condDestroy(locks[i]);
	}

	clear_stacks();
	
	memPrintStats();
	return 0;
}

	