// Common, groups Files and WinWrap
// Copyright Alexander Liss

#include "filewin.h"


File::File(
	LPCTSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	LPSECURITY_ATTRIBUTES lpSA,
	HANDLE hTemplateFile
	)
{
	handle=::CreateFile(lpFileName,dwDesiredAccess,dwShareMode,lpSA,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile);
}

BOOL File::get_type(DWORD& type)
{
	if(!is_valid_handle(handle)) return FALSE;
	type=GetFileType(handle);
	return TRUE;
}

BOOL File::get_size( DWORD& FileSize)
{
	if(!is_valid_handle(handle)) return FALSE;
	FileSize=::GetFileSize(handle,0);
	return TRUE;
}

BOOL File::get_size( ULARGE_INTEGER& FileSize)
{
	if(!is_valid_handle(handle)) return FALSE;
	FileSize.LowPart=::GetFileSize(handle,&FileSize.HighPart);
	return TRUE;
}


BOOL File::set_pointer(
	 LONG DistanceToMove,  
	 DWORD dwMoveMethod               
	)
{ 
	if(!is_valid_handle(handle)) return FALSE;
	::SetFilePointer(handle,DistanceToMove,0,dwMoveMethod);
	DWORD g=GetLastError();
	if(g!=NO_ERROR) return FALSE;
	return TRUE;
}

// LONG - DWORD in 2nd parameter of SetFilePointer
BOOL File::set_pointer(
  LARGE_INTEGER DistanceToMove,	     
  DWORD dwMoveMethod           
)
{
	if(!is_valid_handle(handle)) return FALSE;
	LARGE_INTEGER z=DistanceToMove;
	::SetFilePointer(handle,*((LONG*)&z.LowPart),&z.HighPart,dwMoveMethod);
	DWORD g=GetLastError();
	if(g!=NO_ERROR) return FALSE;
	return TRUE;
}

BOOL File::set_file_end()
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::SetEndOfFile(handle);
}

BOOL File::current_pointer(DWORD& place)
{
	if(!is_valid_handle(handle)) return FALSE;
	place=::SetFilePointer(handle,0,0,FILE_CURRENT);
	DWORD g=GetLastError();
	if(g!=NO_ERROR) return FALSE;
	return TRUE;
}

BOOL File::current_pointer(ULARGE_INTEGER& place)
{
	if(!is_valid_handle(handle)) return FALSE;
	LARGE_INTEGER z;
	memset(&z,0,sizeof(z));
	z.LowPart=::SetFilePointer(handle,0,&z.HighPart,FILE_CURRENT);
	DWORD g=GetLastError();
	if(g!=NO_ERROR) return FALSE;
	place.LowPart=z.LowPart;
	place.HighPart=z.HighPart;
	return TRUE;
}

BOOL File::read(
		DWORD& BytesActual,
		LPVOID lpBuffer,
		DWORD dwBytesLimit		
	)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::ReadFile(handle,lpBuffer,dwBytesLimit,&BytesActual,0);
}


BOOL File::write(
		DWORD& BytesActual,
		LPVOID lpBuffer,
		DWORD dwBytesLimit		
	)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::WriteFile(handle,lpBuffer,dwBytesLimit,&BytesActual,0);
}

BOOL File::flush()
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::FlushFileBuffers(handle);
}


BOOL File::lock(
  DWORD dwFileOffsetLow,         
  DWORD dwFileOffsetHigh,        
  DWORD nNumberOfBytesToLockLow,  
  DWORD nNumberOfBytesToLockHigh  
)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::LockFile(handle,dwFileOffsetLow,dwFileOffsetHigh,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh);
}

BOOL File::unlock(
  DWORD dwFileOffsetLow,         
  DWORD dwFileOffsetHigh,        
  DWORD nNumberOfBytesToLockLow,  
  DWORD nNumberOfBytesToLockHigh  
)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::UnlockFile(handle,dwFileOffsetLow,dwFileOffsetHigh,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh);
}

BOOL File::cancel()
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::CancelIo(handle);
}

	// AsyncFileOperation

void AsyncFileOperation::clear()
{
	handle=0;
	memset(&overlapped,0,sizeof(overlapped));
}

BOOL AsyncFileOperation::lock(
  DWORD nNumberOfBytesToLockLow,   
  DWORD nNumberOfBytesToLockHigh, 
  DWORD dwFlags                  
)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::LockFileEx(handle,dwFlags,0,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh,&overlapped);
}

BOOL AsyncFileOperation::unlock(
  DWORD nNumberOfBytesToLockLow,   
  DWORD nNumberOfBytesToLockHigh
)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::UnlockFileEx(handle,0,nNumberOfBytesToLockLow,nNumberOfBytesToLockHigh,&overlapped);
}

BOOL AsyncFileOperation::get_result(
				DWORD& dwTransfer,
				BOOL bWait
				)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::GetOverlappedResult(handle,&overlapped,&dwTransfer,bWait);
}

	// AsyncReadWrite


BOOL AsyncReadWrite::read(
		  DWORD& BytesActual,
		  LPVOID lpBuffer,
		  DWORD dwBytesLimit			
	)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::ReadFile(handle,lpBuffer,dwBytesLimit,&BytesActual,&overlapped);
}

BOOL AsyncReadWrite::write(
		DWORD& BytesActual,
		LPVOID lpBuffer,
		DWORD dwBytesLimit		
		)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::WriteFile(handle,lpBuffer,dwBytesLimit,&BytesActual,&overlapped);
}


BOOL AsyncReadWrite::read_loop(DWORD& BytesActual,LPVOID lpBuffer, DWORD dwBytes, 
			   DWORD timeout, DWORD wait_slice)
{
	if(!is_valid_handle(handle)) return FALSE;
	if(wait_slice<1 || timeout<1) return FALSE;

	BytesActual=0;

	BOOL success=FALSE,result=TRUE;
	DWORD z=0;
	DWORD period=0;

	while(BytesActual<dwBytes)
	{
		z=0;

		success=read(z,(char*)lpBuffer+BytesActual,dwBytes-BytesActual);

		if(success) 
		{
			BytesActual+=z;
			continue;
		}

		if(!success) 
		{
			BytesActual+=z;
			result=FALSE;
			break;
		}

		for(;;)
		{
			period+=wait_slice;

			if(period>timeout)
			{
				result=FALSE;
				break;
			}

			wait(wait_slice);

			BOOL success=get_result(z,FALSE);

			if(success) break;
		}

		BytesActual+=z;
	}

	return result;
}


BOOL AsyncReadWrite::write_loop(DWORD& BytesActual,LPVOID lpBuffer,DWORD dwBytes, 
			   DWORD timeout, DWORD wait_slice)
{
	if(!is_valid_handle(handle)) return FALSE;

	if(wait_slice<1 || timeout<1) return FALSE;

	BytesActual=0;

	BOOL success=FALSE,result=TRUE;
	DWORD z=0;
	DWORD period=0;

	while(BytesActual<dwBytes)
	{
		z=0;

		success=write(z,(char*)lpBuffer+BytesActual,dwBytes-BytesActual);

		if(success) 
		{
			BytesActual+=z;
			continue;
		}

		if(!success) 
		{
			BytesActual+=z;
			result=FALSE;
			break;
		}

		for(;;)
		{
			period+=wait_slice;

			if(period>timeout)
			{
				result=FALSE;
				break;
			}

			wait(wait_slice);

			BOOL success=get_result(z,FALSE);

			if(success) break;
		}

		BytesActual+=z;
	}

	return result;
}


	// AsyncReadWriteEx

BOOL AsyncReadWriteEx::read(LPVOID lpBuffer, DWORD dwBytesLimit	)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::ReadFileEx(handle,lpBuffer,dwBytesLimit,&overlapped,completion_routine);
}

BOOL AsyncReadWriteEx::write(
		LPVOID lpBuffer,
		DWORD dwBytesLimit
		)
{
	if(!is_valid_handle(handle)) return FALSE;
	return ::WriteFileEx(handle,lpBuffer,dwBytesLimit,&overlapped,completion_routine);
}