 /***************************************************************************************
 *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;

	DWORD     currentBuffer;

int fSize;

HANDLE eventOn4;
HANDLE eventDone;
int Done=0;
char*			  ReadAheadBuff[12];
int BufferStart[12];
int BufferSize[12];
int NumBuff=0;
int CurrentBuffer;
int CacheSize=1000000;
int RestartPoint;
HANDLE rhandle;

HANDLE eventReadDone;	
/*
 * The main class
 */
void Restart(int pos, int full)
{

	int n;
	if (full)
	{
		CurrentBuffer=0;
		for (n=0;n<NumBuff;n++)
		{
			BufferStart[n]=0;
			BufferSize[n]=0;
		}
		WaitForSingleObject(eventReadDone,INFINITE);
		SetFilePointer(::file, pos,NULL, FILE_BEGIN);
		BufferStart[0]=pos;
		BufferSize[0]=0;
		ReleaseMutex(eventReadDone);	
		ResetEvent(eventDone);
		SetEvent(eventOn4);
		WaitForSingleObject(eventDone,INFINITE);
	}
	else
	{
		CurrentBuffer=0;
		memcpy(ReadAheadBuff[0],ReadAheadBuff[NumBuff-1]+(pos-BufferStart[NumBuff-1]),(BufferSize[NumBuff-1]-(pos-BufferStart[NumBuff-1])));
		BufferSize[0]=(BufferSize[NumBuff-1]-(pos-BufferStart[NumBuff-1]));
		BufferStart[0]=pos;
		for (n=1;n<NumBuff;n++)
		{
			BufferStart[n]=0;
			BufferSize[n]=0;
		}
		SetEvent(eventOn4);
//		readstart=
		//SetEvent(eventDone);
	}
}

DWORD WINAPI ReadAhead(LPVOID lpParameter)
{
	DWORD size;

	HANDLE eventOn4=CreateEvent(NULL,true,false,_T("eventon4"));
	HANDLE eventDone=CreateEvent(NULL,true,false,_T("eventDone"));
	HANDLE eventReadDone=CreateMutex(NULL,false,_T("ReadDone"));
	Done=0;
	
	while(file!=NULL)
	{
		int n;
		WaitForSingleObject(eventOn4,INFINITE);
		ResetEvent(eventOn4);
		if (!file) 
		{
//			ReleaseMutex(eventReadDone);	
			continue;
		}
		WaitForSingleObject(eventReadDone,INFINITE);
//		OutputDebugString(_T("Start Reading"));
		ReadFile(file,(void *) (((char*)ReadAheadBuff[0])+BufferSize[0]),(1000000)-BufferSize[0],(LPDWORD) &size,NULL);
		BufferSize[0]+=size;
		for (n=1;n<NumBuff;n++)
		{
			BufferStart[n]=BufferStart[n-1]+BufferSize[n-1];
			ReadFile(file,(void *) ReadAheadBuff[n],1000000,(LPDWORD) &size,NULL);
			BufferSize[n]=size;
		}
//		OutputDebugString(_T("Done Reading"));
		SetEvent(eventDone);
		ReleaseMutex(eventReadDone);	
	}
	CloseHandle(eventOn4);
	CloseHandle(eventDone);
	CloseHandle(eventReadDone);
	Done=1;
	return 1;
}

/*
 * The main class
 */

INPUTMEDIA_API int InputMediaOpen(LPTSTR lpFilename, int mode, int type, int reservesize, int maxsize) 
{
	if(lpFilename) {
		int n;
		TCHAR temp[100];
		::file       = NULL;
//		::mode       = -1;
		lastReadPos=0;
		Done=1;
		for (n=0;n<NumBuff;n++)
		{
			ReadAheadBuff[n]=0;
		}
		::file =CreateFile((LPCTSTR)lpFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		if (::file==INVALID_HANDLE_VALUE ) return 0;
		//::mode = INPUT_TYPE_FILE;

		MEMORYSTATUS ms;
		GlobalMemoryStatus(&ms);
		maxsize*=1000000;
		if (maxsize==0) maxsize=12000000;
		if (reservesize!=-1) CacheSize=ms.dwAvailPhys-reservesize; //1000000;
		if (CacheSize>maxsize)
			CacheSize=maxsize;
		fSize=GetFileSize(::file,NULL);
		if (fSize<CacheSize)
		{
			CacheSize=fSize+100000;
			if (CacheSize<2000000) CacheSize=2000000;
			RestartPoint=-1;
		}
		else
		{
			if (CacheSize<2000000) CacheSize=2000000;
			RestartPoint=CacheSize/10;
		}
		if (RestartPoint<400000&&(RestartPoint>0)) RestartPoint=400000;
		if (RestartPoint>800000) RestartPoint=800000;
		NumBuff=(CacheSize/1000000);
		if ((NumBuff*1000000)<CacheSize) NumBuff++;

		for (n=0;n<NumBuff;n++)
		{
			BufferStart[n]=0;
			BufferSize[n]=0;
			ReadAheadBuff[n]=(char*)malloc(1000000);
			if (!ReadAheadBuff[n])
			{
				MessageBox(GetActiveWindow(),_T("Error allocating buffer"),_itow(CacheSize,temp,10),MB_OK);
				return 0;
			}
		}
		eventOn4=CreateEvent(NULL,true,false,_T("eventon4"));
		eventDone=CreateEvent(NULL,true,false,_T("eventDone"));
		eventReadDone=CreateMutex(NULL,false,_T("ReadDone"));
		SetEvent(eventOn4);

		CurrentBuffer=0;
		BufferStart[0]=0;
		DWORD rtid;
		Done=0;
		rhandle=CreateThread(NULL,NULL,ReadAhead,(LPVOID)0,0,&rtid);
		WaitForSingleObject(eventDone,INFINITE);
#if (_WIN32_WCE>=300)
		CeSetThreadQuantum(rhandle,25);
#endif

	}
	return fSize;
}

INPUTMEDIA_API void InputMediaClose() 
{
	int n;
	//TerminateThread(rhandle,0);
	if(::file) {

		CloseHandle(::file);
	}
	for (n=0;n<NumBuff;n++)
		if (ReadAheadBuff[n]) free(ReadAheadBuff[n]);
	::file=NULL;
	SetEvent(eventOn4);
	while(!Done) Sleep(100);
	CloseHandle(eventOn4);
	CloseHandle(eventDone);
	CloseHandle(eventReadDone);

}


INPUTMEDIA_API 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>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]))
		{
			if (CurrentBuffer==(NumBuff-1)&&(BufferSize[CurrentBuffer]==1000000))
			{
				Restart(lastReadPos-100000,1);
			}
			else
			{
				if (BufferSize[CurrentBuffer]==1000000)
				{
					memcpy((void*)data,(void*)((char*)ReadAheadBuff[CurrentBuffer]+(lastReadPos-BufferStart[CurrentBuffer])),BufferSize[CurrentBuffer]-(lastReadPos-BufferStart[CurrentBuffer]));
					data+=BufferSize[CurrentBuffer]-(lastReadPos-BufferStart[CurrentBuffer]);
					bytesleft-=BufferSize[CurrentBuffer]-(lastReadPos-BufferStart[CurrentBuffer]);
					lastReadPos+=BufferSize[CurrentBuffer]-(lastReadPos-BufferStart[CurrentBuffer]);
					CurrentBuffer++;
				}
			}

		}
		else if ((lastReadPos+bytesleft>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]-RestartPoint))&&(BufferSize[CurrentBuffer]==1000000)&&(CurrentBuffer==(NumBuff-1)))
		{
			Restart(lastReadPos-100000,0);
		}
		else if(BufferSize[CurrentBuffer]>0)
		{
			if (BufferSize[CurrentBuffer]<(bytesleft))
			{
				bytesleft-=BufferSize[CurrentBuffer];
			}
			if (lastReadPos-BufferStart[CurrentBuffer]+bytesleft>BufferSize[CurrentBuffer])
			{
				bytesleft-=BufferSize[CurrentBuffer]-(lastReadPos-BufferStart[CurrentBuffer]);
			}
			memcpy((void*)data,(void*)((char*)ReadAheadBuff[CurrentBuffer]+(lastReadPos-BufferStart[CurrentBuffer])),bytesleft);
			::lastReadPos+=bytesleft;
			bytesleft=0;
		}
	}
	size-=bytesleft;
	return size;
}


INPUTMEDIA_API int InputMediaSeek(int size, unsigned int method)
{

	switch(method)
	{
	case INPUT_SEEK_SET:
		if (size<BufferStart[CurrentBuffer])
		{
			if (size<BufferStart[0])
			{
				Restart(size,1);

			}
			else
			{
				while(size<BufferStart[CurrentBuffer])
					CurrentBuffer--;
			}

		}

		if (size>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]))
		{
			if (size>(BufferStart[NumBuff-1]+BufferSize[NumBuff-1]))
			{
				Restart(size-100000,1);
			}
			else if (size>(BufferStart[NumBuff-1]+BufferSize[NumBuff-1]-RestartPoint)&&(BufferSize[NumBuff-1]==1000000))
			{
				Restart(size-100000,0);
			}
			else
			{
				while (size>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]))
					if (BufferSize[CurrentBuffer]==1000000) CurrentBuffer++;
			}
		}
		::lastReadPos = size;
		return 0;
		break;

	
	case INPUT_SEEK_CUR:

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

			if (lastReadPos>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]))
			{
				if (::lastReadPos>(BufferStart[NumBuff-1]+BufferSize[NumBuff-1]))
				{
					Restart(lastReadPos-100000,1);
				}
				else if (::lastReadPos>(BufferStart[NumBuff-1]+BufferSize[NumBuff-1]-RestartPoint)&&(BufferSize[NumBuff-1]==1000000))
				{
					Restart(lastReadPos-100000,0);
				}
				else
				{
					while (lastReadPos>(BufferStart[CurrentBuffer]+BufferSize[CurrentBuffer]))
						if (BufferSize[CurrentBuffer]==1000000) CurrentBuffer++;
				}
			}

			return 0;
		}
		break;

	case INPUT_SEEK_END:

		SetFilePointer( ::file, size, NULL,FILE_END);
		return 0;
		break;
	}
	return 0;
}


// This is the constructor of a class that has been exported.
// see InputMedia.h for the class definition
CInputMedia::CInputMedia()
{ 
	return; 
}

