 /***************************************************************************************
 *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"
#include <wininet.h>


/*
 * Seek Enum
 */

enum {

	INPUT_SEEK_SET,
	INPUT_SEEK_CUR,
	INPUT_SEEK_END
};

//	DWORD mode;
	HINTERNET hInternet;
	HANDLE file;
	HANDLE logfile;
	unsigned long 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;
int inread=0;
HANDLE eventReadDone;	
int LostConnect=0;
HANDLE rhandle;
int waiting=0;
/*
 * The main class
 */
DWORD WINAPI ReadAhead(LPVOID lpParameter)
{
	unsigned long size;
	char t[10];
	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;
		}
#ifdef LOGFILE
		OutputDebugString(_T("about to read\r\n"));
		logfile=CreateFile(_T("\\my documents\\pdlog.txt"),GENERIC_WRITE|GENERIC_READ,NULL,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		SetFilePointer(logfile,0,0,FILE_END);
		WriteFile(logfile,"about to read=",14,&filesize,NULL);
		WriteFile(logfile,_itoa(readamt,t,10),strlen(t),&filesize,NULL);
		WriteFile(logfile,"\r\n",2,&filesize,NULL);
		CloseHandle(logfile);
#endif
			if (!InternetReadFile(file,(void *) (ReadAheadBuff+WritePos),readamt,&size))
			{
				MessageBox(GetActiveWindow(),_T("Lost Server Connection"),_T(""),MB_OK);
				ReleaseMutex(eventReadDone);	
				LostConnect=1;
				return 1;
			}
#ifdef LOGFILE
		logfile=CreateFile(_T("\\my documents\\pdlog.txt"),GENERIC_WRITE|GENERIC_READ,NULL,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		SetFilePointer(logfile,0,0,FILE_END);
		WriteFile(logfile,"done read\r\n",11,&filesize,NULL);
		CloseHandle(logfile);
		OutputDebugString(_T("done read\r\n"));
#endif
//		InternetReadFile(::http, (void *) CurrentPtr, 8192, &read);
		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;
}

int InputMediaOpen(char* lpFilename, int mode, int type, int reservesize, int maxsize) 
{
	::file = NULL;
	LostConnect=0;
	::hInternet   = NULL;		

	if(lpFilename) {
		int n;
		char temp[100];
		DWORD rtid;
		::file       = NULL;
		lastReadPos=0;

		::hInternet = InternetOpen(_T("PocketMVP"),
									   INTERNET_OPEN_TYPE_PRECONFIG,
									   NULL, NULL, 0);

		if(::hInternet == NULL) {
				
			MessageBox(GetActiveWindow(),_T("Could not open an Internet connection"),_T(""),MB_OK);
			return 0;
		}


		::file = InternetOpenUrl(hInternet, (LPTSTR)lpFilename, NULL, -1, INTERNET_FLAG_NO_CACHE_WRITE, 0 );
			
		if(::file == NULL) {
			
			InternetCloseHandle(::hInternet);

			MessageBox(GetActiveWindow(),_T("Could not open URL"),_T(""),MB_OK);
			return 0;
		}
		

		DWORD buffer;
		DWORD dwLength = 4;

		if(!HttpQueryInfo(::file, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &buffer, &dwLength, NULL)) {

			InternetCloseHandle(::file);
			InternetCloseHandle(::hInternet);
			
			MessageBox(GetActiveWindow(),_T("Could not get file information"),_T(""),MB_OK);
			
			return 0;
		}
		else {

			::fSize = buffer;			
		}

		if (!fSize ) return 0;
		if (!ReadAheadBuff) 
			ReadAheadBuff=(char*)malloc(CACHE_SIZE);
		BufferStart=0;
		BufferStartNext=0;
		WritePos=0;
		maxread=20000;

		eventReadDone=CreateMutex(NULL,false,_T("ReadDone"));
		rhandle=CreateThread(NULL,NULL,ReadAhead,(LPVOID)0,0,&rtid);
		//while(WritePos<CACHE_SIZE-1100000&&(WritePos<fSize)) Sleep(10);
	//	maxread=50000;
		

	}
	return fSize;
}

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

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


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)>(WritePos+BufferStartNext)))
		{
			HCURSOR oldcursor;
			oldcursor=SetCursor(LoadCursor(NULL, IDC_WAIT));
			while((lastReadPos+bytesleft)>(WritePos+BufferStartNext)&&(!LostConnect))
			{
				Sleep(10);
			}
			SetCursor(oldcursor);
			if (LostConnect) return 0;
		}		
		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;

		}
		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;
}


INPUTMEDIA_API 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))
		{
						HCURSOR oldcursor;

			oldcursor=SetCursor(LoadCursor(NULL, IDC_WAIT));
			while (size-BufferStart<WritePos+100000&& (BufferStartNext!=BufferStart)&&(!LostConnect))
			{
				Sleep(10);
			}
			SetCursor(oldcursor);
			if (LostConnect) return -1;
		}
		return 0;
		break;

	
	case INPUT_SEEK_CUR:

		if(!size) {
			return ::lastReadPos;
		}
		else 
		{
			int holdpos=lastReadPos;
			::lastReadPos += size;
			if (lastReadPos>(BufferStartNext+CACHE_SIZE))
			{
							HCURSOR oldcursor;
				oldcursor=SetCursor(LoadCursor(NULL, IDC_WAIT));

				while (lastReadPos>(BufferStartNext+CACHE_SIZE)&&(!LostConnect))
				{
					Sleep(10);
				}
				SetCursor(oldcursor);
				if (LostConnect) return 0;
			}

			return 0;
		}
		break;

	}
	return 0;
}


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


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

