#include <v_lib.h>
#include <windows.h>

V_LOCAL	struct globs_s
{
	vboolean	bInfiniteLoop;
	HANDLE		hStopEvent;
	HANDLE		hCompletionEvent;
	vp_stack_t	flist;
	file_t		out;
	char_p		error;
};

#define	DEC_SIZE		2048
#define	MAX_QUEUE_LEN	(44100 * 8)

V_LOCAL	vboolean
Done(struct globs_s *globs)
{
	if(WaitForSingleObject(globs->hStopEvent,0)==WAIT_OBJECT_0)
		return true;
	return false;
}

int WINAPI
WinMain(HINSTANCE hInst,HINSTANCE hStub,LPSTR CmdLine,int nCmdShow)
{
	u32_t num_chan,bps,freq;
	u32_t len,queue_len,song=0,fail_count=0;
	u8_t decompressed[DEC_SIZE];
	vboolean first_pass = true;
	char_p fname;
	file_t in;
	vhandle_t sc, fsc;
	char_p ptr;
	vboolean bStopOnly=false;
	char_t ext[_MAX_EXT];
	struct globs_s globs;
	memZero(&globs,sizeof(globs));
	globs.hStopEvent=OpenEvent(EVENT_ALL_ACCESS,FALSE,"VPlayerStopEvent");
	if(globs.hStopEvent == NULL)
	{
		/* This is a first instance */
		globs.hStopEvent=CreateEvent(NULL,TRUE,FALSE,"VPlayerStopEvent");
		globs.hCompletionEvent=CreateEvent(NULL,TRUE,FALSE,"VPlayerCompletionEvent");
	}
	else
	{
		globs.hCompletionEvent=OpenEvent(EVENT_ALL_ACCESS,FALSE,"VPlayerCompletionEvent");
		/* We need to stop playing */
		SetEvent(globs.hStopEvent);
		WaitForSingleObject(globs.hCompletionEvent,INFINITE);
	}
	/* Extract all cmdline switches and file names */
	vpsClear(&globs.flist);
	sc=tokParseString(CmdLine,NULL);
	if(!sc)
		goto cleanup;
	while(tokNext(sc))
	{
		/* Is it a switch ? */
		if(tokLast(sc)[0]=='-')
		{
			ptr=&tokLast(sc)[1];
			if(*ptr)
			{
				/* get variable from switch */
				if(!strcmp(ptr,"-loop") || !strcmp(ptr,"l"))
					globs.bInfiniteLoop=true;
				else if(!strcmp(ptr,"stop"))
					bStopOnly=true;
			}
			continue;
		}
		/* Determine what type of file which name passed in cmdline */
		fsSplitPath(tokLast(sc),NULL,NULL,ext);
		if(!strcasecmp(ext,"m3u"))
		{
			/* list of files; parse it ! */
			fsc=tokParseFile(tokLast(sc),NULL);
			if(!fsc)
				continue;
			while(tokNext(fsc))
				vpsPush(&globs.flist,strDup(tokLast(fsc)));
			continue;
			tokDestroy(fsc);
		}
		/* this is file name */
		vpsPush(&globs.flist,strDup(tokLast(sc)));
	}
	tokDestroy(sc);
	if(!vpsCount(&globs.flist))
	{
		/* Nothing to play ! */
		if(!bStopOnly)
			globs.error="Nothing to play";
		goto cleanup;
	}
	if(bStopOnly)
		goto cleanup;
	for(;;)
	{
		/* Get next song name */
		if(song==vpsCount(&globs.flist))
		{
			if(!globs.bInfiniteLoop)
				break;
			song=0;
			first_pass=false;
		}
		fname=vpsGet(&globs.flist,song);
		/* Increase song counter */
		song++;
		/* Precache file data */
		in=fileOpenRead(fname);
		if(in)
		{
			u32_t i,len,pages,pagebuf[4096];
			len=fileSize(in);
			pages = len >> 12;
			for(i=0;i<pages;i++)
				fileRead(in,pagebuf,4096);
			fileRead(in,pagebuf,len - (pages << 12));
			fileClose(in);
		}
		if(Done(&globs))
			break;
		/* Open MP3 decoder stream */
		in=mp3Open(fname);
		if(!in)
		{
			/* Wrong item, skip it */
			fail_count++;
			if(!first_pass && fail_count >= song)
			{
				globs.error="There is no valid items in play list !";
				break;
			}
			continue;
		}
		/* Open wave output device */
		fileControl(in,pctl_wave_get_num_channels,&num_chan);
		fileControl(in,pctl_wave_get_sampling_frequency,&freq);
		fileControl(in,pctl_wave_get_bits_per_sample,&bps);
		globs.out=wavePlaybackOpen(num_chan,freq,bps);
		if(!globs.out)
		{
			globs.error="Sound hardware busy";
			goto cleanup;
		}
		/* Decode wave data and write it to DSP */
		if(vpsCount(&globs.flist) == 1)
		fileControl(in,pctl_wave_loop,(void*)globs.bInfiniteLoop);
		for(;;)
		{
			len=fileRead(in,decompressed,DEC_SIZE);
			if(!len)
				break;
			fileWrite(globs.out,decompressed,len);
			if(Done(&globs))
				break;
			queue_len=0;
			fileControl(globs.out,pctl_waveout_get_pending_samples,&queue_len);
			while(queue_len > MAX_QUEUE_LEN)
			{
				timeDelay(50);
				if(Done(&globs))
					break;
				fileControl(globs.out,pctl_waveout_get_pending_samples,&queue_len);
			}
		}
		fileControl(globs.out,pctl_waveout_wait,NULL);
		fileClose(in);
		fileControl(globs.out,pctl_waveout_reset,NULL);
		fileClose(globs.out);
		globs.out=NULL;
		if(Done(&globs))
			break;
	}
cleanup:;
	if(globs.out!=NULL)
	{
		fileControl(globs.out,pctl_waveout_wait,NULL);
		fileClose(globs.out);
		globs.out=NULL;
	}
	SetEvent(globs.hCompletionEvent);
	while(vpsPop(&globs.flist,(void**)&ptr))
		if(ptr)
			memFree(ptr);
	timeDelay(10);
	CloseHandle(globs.hStopEvent);
	CloseHandle(globs.hCompletionEvent);
	vpsClear(&globs.flist);
	if(globs.error!=NULL)
	{
		MessageBoxA(NULL,globs.error,"MP3 player",MB_OK|MB_ICONEXCLAMATION);
		return -1;
	}
	return 0;
}
