 /*This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The GPL can be found at: http://www.gnu.org/copyleft/gpl.html						*
 *																						*
 *	Authors:
 *			Marc Dukette
 **************************************************************************************/
#include "stdafx.h"
#include "InputMedia.h"


/*
 * Seek Enum
 */

enum {

	INPUT_SEEK_SET,
	INPUT_SEEK_CUR,
	INPUT_SEEK_END
};

//	DWORD mode;

	HANDLE file;
//	int filesize;
	/*
 	 * HTTP/FTP stuff 
	 */


//	HANDLE    ioMutex;
	long     lastReadPos;

	int     currentBuffer;
int maxread=100000;
int fSize;
#define CACHE_SIZE 3000000
//int Done=0;
char*			  ReadAheadBuff=0;
int BufferStart;
int BufferStartNext;
int CurrentBuffer;
int nCacheSize=1000000;
int WritePos;
HANDLE rhandle;
HANDLE eventReadDone;	
int restarted=1;
/*
 * The main class
 */
void Restart(int pos)
{
	if (pos<0) pos=0;
	WaitForSingleObject(eventReadDone,INFINITE);
	SetFilePointer(file, pos,NULL, FILE_BEGIN);
//	fs_seek(::file,pos,SEEK_SET);
	BufferStart=pos;
	BufferStartNext=pos;
	WritePos=0;
	maxread=200000;
	restarted=1;
	ReleaseMutex(eventReadDone);	
#if (_WIN32_WCE>=300)
		CeSetThreadQuantum(rhandle,100);
#endif
	while(WritePos<CACHE_SIZE-1100000&&(WritePos+BufferStartNext<fSize)) Sleep(10);
#if (_WIN32_WCE>=300)
		CeSetThreadQuantum(rhandle,25);
#endif
	maxread=50000;
}

DWORD WINAPI ReadAhead(LPVOID lpParameter)
{
	unsigned long size;
	while (file!=NULL)
	{
		unsigned long n;
		int readamt;
		if (!file) 
		{
			continue;
		}
		if (BufferStartNext+WritePos>=fSize)
		{
			Sleep(10);
			continue;
		}
		if (lastReadPos-(WritePos+BufferStart)<1000000&&(lastReadPos>(WritePos+BufferStart))&&(BufferStartNext>BufferStart))
		{
			Sleep(10);
			continue;
		}
		if ((WritePos-(lastReadPos-BufferStart))>CACHE_SIZE-1000000)
		{
			Sleep(10);
			continue;
		}
		//printf("buffer=%d\r\n",(WritePos-(lastReadPos-BufferStart)));
		WaitForSingleObject(eventReadDone,INFINITE);
		readamt=maxread;
		if (CACHE_SIZE-(WritePos+maxread)<0)
		{
			readamt=CACHE_SIZE-WritePos;
		}
		ReadFile(file,(void *) (ReadAheadBuff+WritePos),readamt,&size,NULL);
		WritePos+=size;
		//printf("Write=%d Size=%d\r\n",WritePos,size);
		if (size<readamt)
		{
			Sleep(10);
			ReleaseMutex(eventReadDone);	
			continue;
		}
		if (WritePos==CACHE_SIZE)
		{
			WritePos=0;
			BufferStartNext=BufferStart+CACHE_SIZE;
		}
		ReleaseMutex(eventReadDone);	
	}
	return 1;
}

/*
 * The main class
 */

int InputMediaOpen(char* lpFilename, int mode, int type, int reservesize, int maxsize) 
{
	if(lpFilename) {
		int n;
		char temp[100];
		DWORD rtid;
//		::file       = NULL;
//		::mode       = -1;
		lastReadPos=0;
//		Done=1;
//		ReadAheadBuff=0;

		::file =CreateFile((LPCTSTR)lpFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		if (::file==INVALID_HANDLE_VALUE )
	{
		LPVOID lpMsgBuf;
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),0,(LPTSTR) &lpMsgBuf, 0, NULL );
		MessageBox( GetActiveWindow(), (LPCTSTR)lpMsgBuf, _T("Error loading file reader"), MB_OK | MB_ICONINFORMATION );
		LocalFree( lpMsgBuf );
		return 0;
	}
//		::file =fs_open((char*)lpFilename,O_RDONLY);
		//::mode = INPUT_TYPE_FILE;

		fSize=GetFileSize(::file,NULL);
		if (!fSize ) return 0;
		if (!ReadAheadBuff) 
			ReadAheadBuff=(char*)malloc(CACHE_SIZE);
		if (!ReadAheadBuff) 
			return 0;
		BufferStart=0;
		BufferStartNext=0;
		WritePos=0;
		maxread=200000;
		eventReadDone=CreateMutex(NULL,false,_T("ReadDone"));
		rhandle=CreateThread(NULL,NULL,ReadAhead,(LPVOID)0,0,&rtid);
#if (_WIN32_WCE>=300)
		CeSetThreadQuantum(rhandle,25);
#endif
		//SetThreadPriority(rhandle,THREAD_PRIORITY_BELOW_NORMAL);
		//while(WritePos<CACHE_SIZE-1100000&&(WritePos<fSize)) Sleep(10);
		maxread=50000;


	}
	return fSize;
}

void InputMediaClose() 
{
	int n;
	if(fSize) {
		
		CloseHandle(::file);
		file=NULL;

	}
	Sleep(100);
	free(ReadAheadBuff);
	ReadAheadBuff=0;
	fSize=0;

}


int InputMediaRead(char *data, unsigned int size)
{
	int bytesleft;
	if (size+lastReadPos>fSize)
		size=fSize-lastReadPos;
	bytesleft=size;


	while(bytesleft&&(lastReadPos<fSize))
	{
		if ((lastReadPos+bytesleft)>(WritePos+BufferStartNext+20000))
		{
			//printf("BufferStart=%d BufferNext=%d WriteBos=%d \r\n",BufferStart, BufferStartNext,WritePos);
			Restart(lastReadPos-100000);
		}

		while((lastReadPos+bytesleft)>(WritePos+BufferStartNext))
		{
		//	printf("caught up\r\n");
			Sleep(10);
		}
		
		if (lastReadPos+bytesleft>(BufferStart+CACHE_SIZE))
		{
			if (lastReadPos<(BufferStart+CACHE_SIZE))
			{
				memcpy((void*)data,(void*)((char*)ReadAheadBuff+(lastReadPos-BufferStart)),(BufferStart+CACHE_SIZE)-lastReadPos);
				bytesleft-=((BufferStart+CACHE_SIZE)-lastReadPos);
				data+=((BufferStart+CACHE_SIZE)-lastReadPos);
				::lastReadPos=BufferStart+CACHE_SIZE; //+=((BufferStart+CACHE_SIZE)-lastReadPos);
			}
			//printf("cycle=%d\r\n",lastReadPos);
			BufferStart=BufferStartNext;
			restarted=0;

		}
		else if (lastReadPos<BufferStart-CACHE_SIZE+WritePos||(lastReadPos<BufferStart&&(restarted)))
		{
			//printf("BufferStart=%d BufferNext=%d WriteBos=%d \r\n",BufferStart, BufferStartNext,WritePos);
			Restart(lastReadPos-100000);
		}
		else if (lastReadPos<BufferStart)
		{
			if (lastReadPos+bytesleft>(BufferStart))
			{
				memcpy((void*)data,(void*)((char*)ReadAheadBuff+(lastReadPos-(BufferStart-CACHE_SIZE))),BufferStart-lastReadPos);
				bytesleft-=(BufferStart-lastReadPos);
				data+=(BufferStart-lastReadPos);
				::lastReadPos=BufferStart; //+=(BufferStart-lastReadPos);
			}
			else
			{
				memcpy((void*)data,(void*)((char*)ReadAheadBuff+(lastReadPos-(BufferStart-CACHE_SIZE))),bytesleft);
				::lastReadPos+=bytesleft;

				bytesleft=0;
			}
		}
		else
		{
			memcpy((void*)data,(void*)((char*)ReadAheadBuff+(lastReadPos-BufferStart)),bytesleft);
			::lastReadPos+=bytesleft;

			bytesleft=0;
		}

	}
	size-=bytesleft;
	return size;
}


int InputMediaSeek(int size, unsigned int method)
{
	switch(method)
	{
	case INPUT_SEEK_SET:
		::lastReadPos = size;
//		if (size<BufferStart-400000||(size<lastReadPos-500000))

		if (size-BufferStart<WritePos+100000&& (BufferStartNext!=BufferStart))
		{
			//printf("BufferStart=%d BufferNext=%d WriteBos=%d \r\n",BufferStart, BufferStartNext,WritePos);
			if (size-100000>0)
				Restart(size-100000);
			else
				Restart(size);

		}
		return 0;
		break;

	
	case INPUT_SEEK_CUR:

		if(!size) {
			return ::lastReadPos;
		}
		else 
		{
			int holdpos=lastReadPos;
			::lastReadPos += size;

			if (lastReadPos>(BufferStartNext+CACHE_SIZE))
			{
				//printf("BufferStart=%d BufferNext=%d WriteBos=%d respos=%d\r\n",BufferStart, BufferStartNext,WritePos,holdpos);
				Restart(lastReadPos-100000);
			}

			return 0;
		}
		break;

	}
	return 0;
}


