// This is a part of the Active Template Library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLFILE_H__
#define __ATLFILE_H__

#include <atlbase.h>

//#pragma pack(push, 8 /* _ATL_PACKING */)
namespace ATL
{

class CAtlFile // : public CHandle
{
public:
	CAtlFile() throw():
		m_h( NULL )
	{
	}
	CAtlFile( CAtlFile& h ) throw() :
		m_h( NULL )
	{
		Attach( h.Detach() );
	}
	explicit CAtlFile( FILE *h ) throw() :
		m_h( h )
	{
	}

	~CAtlFile() throw()
	{
		if( m_h != NULL )
		{
			Close();
		}
	}

	CAtlFile& operator=( CAtlFile& h ) throw()
	{
		if( this != &h )
		{
			if( m_h != NULL )
			{
				Close();
			}
			Attach( h.Detach() );
		}

		return( *this );
	}

	void Attach( FILE *h ) throw()
	{
		ATLASSUME( m_h == NULL );
		m_h = h;  // Take ownership
	}

	FILE * Detach() throw()
	{
		FILE *h;

		h = m_h;  // Release ownership
		m_h = NULL;

		return( h );
	}

	void Close() throw()
	{
		if( m_h != NULL )
		{
//			::CloseHandle( m_h );
			fclose(m_h);
			m_h = NULL;
		}
	}

	HRESULT Create(
		__in LPCTSTR szFilename,
		__in DWORD dwDesiredAccess,
		__in DWORD dwShareMode,
		__in DWORD dwCreationDisposition,
		__in DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL,
		__in_opt const void * /*LPSECURITY_ATTRIBUTES*/ lpsa = NULL,
		__in_opt HANDLE hTemplateFile = NULL) throw()
	{
                ((void)dwShareMode);
                ((void)dwFlagsAndAttributes);
                ((void)lpsa);
                ((void)hTemplateFile);
		ATLASSUME(m_h == NULL);
		const TCHAR *sAccess = 0;
		bool bNeedRead = false;
		bool bNeedWrite = false;

		if( (GENERIC_READ|GENERIC_EXECUTE) & dwDesiredAccess ) {
		    bNeedRead = true;
	        }
		if( GENERIC_WRITE & dwDesiredAccess ) {
		    bNeedWrite = true;
		}
		if( !bNeedRead && !bNeedWrite) {
		    return E_INVALIDARG;
		}
		
		switch (dwCreationDisposition) {
		case CREATE_ALWAYS: {
		    bNeedRead ? (sAccess = _TEXT("w+")) : (sAccess = _TEXT("w"));
		    break;
	    	}
		case CREATE_NEW: {
		    FILE *tmpf = _tfopen(szFilename,"r");
		    if(tmpf) {
			fclose(tmpf);
			return NTE_EXISTS;
	    	    }
		    bNeedRead ? (sAccess = _TEXT("w+")) : (sAccess = _TEXT("w"));
		    break;
	    	}
		case OPEN_ALWAYS: {
		    FILE *tmpf = _tfopen(szFilename,"r");
		    if(tmpf) {
			fclose(tmpf);
			bNeedWrite ? (sAccess = _TEXT("r+")) : (sAccess = _TEXT("r"));
	    	    } else {
			bNeedRead ? (sAccess = _TEXT("w+")) : (sAccess = _TEXT("w"));
	    	    }
		    break;
		}
		case OPEN_EXISTING: {
		    bNeedWrite ? (sAccess = _TEXT("r+")) : (sAccess = _TEXT("r"));
		    break;
	        }
		case TRUNCATE_EXISTING: {
		    bNeedRead ? (sAccess = _TEXT("w+")) : (sAccess = _TEXT("w"));
		    break;
		}
		default:
		    return E_INVALIDARG;
		}
		
		FILE *hFile = _tfopen(szFilename,sAccess);
		if(!hFile)
		    return HRESULT_FROM_WIN32( ERROR_OPEN_FAILED );

		Attach(hFile);
		return S_OK;
	}

	HRESULT Read(
		__out_bcount(nBufSize) LPVOID pBuffer,
		DWORD nBufSize) throw()
	{
		ATLASSUME(m_h != NULL);

		DWORD nBytesRead = fread(pBuffer, 1, nBufSize, m_h);
		if (nBytesRead != nBufSize)
			return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );

		return S_OK;
	}

	HRESULT Read(
		__out_bcount(nBufSize) LPVOID pBuffer,
		__in DWORD nBufSize,
		__out DWORD& nBytesRead) throw()
	{
                ((void)nBytesRead);
		ATLASSUME(m_h != NULL);

		DWORD b = fread(pBuffer, 1, nBufSize, m_h);
		if (!b)
			return HRESULT_FROM_WIN32( ERROR_READ_FAULT );

		return S_OK;
	}

	// this function will usually return HRESULT_FROM_WIN32(ERROR_IO_PENDING)
	// indicating succesful queueing of the operation
// 	HRESULT Read(
// 		__out_bcount(nBufSize) LPVOID pBuffer,
// 		__in DWORD nBufSize,
// 		__in_opt LPOVERLAPPED /*pOverlapped*/) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		BOOL b = ::ReadFile(m_h, pBuffer, nBufSize, NULL, pOverlapped);
// 		if (!b)
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

// 	HRESULT Read(
// 		__in_bcount(nBufSize) LPVOID pBuffer,
// 		__in DWORD nBufSize,
// 		__in_opt LPOVERLAPPED pOverlapped,
// 		__in LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		BOOL b = ::ReadFileEx(m_h, pBuffer, nBufSize, pOverlapped, pfnCompletionRoutine);
// 		if (!b)
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

	HRESULT Write(
		__in_bcount(nBufSize) LPCVOID pBuffer,
		__in DWORD nBufSize,
		__out_opt DWORD* pnBytesWritten = NULL) throw()
	{
		ATLASSUME(m_h != NULL);

		DWORD nBytesWritten;
		BOOL emptyBufffer = FALSE;
		if (pnBytesWritten == NULL) {
			pnBytesWritten = &nBytesWritten;
		}
		if (nBufSize == 0) {
			emptyBufffer = TRUE;
		}
		*pnBytesWritten = fwrite( pBuffer, 1, nBufSize, m_h);
		if (!(*pnBytesWritten) and !emptyBufffer)
			return HRESULT_FROM_WIN32( ERROR_WRITE_FAULT );

		return S_OK;
	}

	// this function will usually return HRESULT_FROM_WIN32(ERROR_IO_PENDING)
	// indicating succesful queueing of the operation
// 	HRESULT Write(
// 		__in_bcount(nBufSize) LPCVOID pBuffer,
// 		__in DWORD nBufSize,
// 		__in_opt LPOVERLAPPED pOverlapped) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		BOOL b = ::WriteFile(m_h, pBuffer, nBufSize, NULL, pOverlapped);
// 		if (!b)
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

// 	HRESULT Write(
// 		__in_bcount(nBufSize) LPCVOID pBuffer,
// 		__in DWORD nBufSize,
// 		__in_opt LPOVERLAPPED pOverlapped,
// 		__in LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		BOOL b = ::WriteFileEx(m_h, pBuffer, nBufSize, pOverlapped, pfnCompletionRoutine);
// 		if (!b)
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

	// this function returns HRESULT_FROM_WIN32(ERROR_IO_INCOMPLETE)
	// if bWait is false and the operation is still pending
// 	HRESULT GetOverlappedResult(
// 		__in LPOVERLAPPED pOverlapped,
// 		__out DWORD& dwBytesTransferred,
// 		__in BOOL bWait) throw()
// 	{
// 		BOOL b = ::GetOverlappedResult(m_h, pOverlapped, &dwBytesTransferred, bWait);
// 		if (!b)
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

	HRESULT Seek(__in LONGLONG nOffset, __in DWORD dwFrom = FILE_CURRENT) throw()
	{
		ATLASSUME(m_h != NULL);
		ATLASSERT(dwFrom == FILE_BEGIN || dwFrom == FILE_END || dwFrom == FILE_CURRENT);

		int whence = SEEK_SET;
		if(FILE_BEGIN == dwFrom) {
		    whence = SEEK_SET;
	        } else if(FILE_END == dwFrom) {
		    whence = SEEK_END;
	    	} else if(FILE_CURRENT == dwFrom) {
		    whence = SEEK_CUR;
		} else {
		    return E_INVALIDARG;
		}
		
		int res = fseek( m_h,  nOffset, whence);
		if( -1 == res )
		     return HRESULT_FROM_WIN32( ERROR_SEEK_ON_DEVICE );
		
		return S_OK;
	}

	HRESULT GetPosition(__out ULONGLONG& nPos) const throw()
	{
		ATLASSUME(m_h != NULL);

		nPos = ftell(m_h);
		if( -1 == static_cast<long>(nPos) )
		     return HRESULT_FROM_WIN32( ERROR_SEEK_ON_DEVICE );
		
		return S_OK;
	}

	HRESULT Flush() throw()
	{
		ATLASSUME(m_h != NULL);

		if(fflush(m_h))
			return HRESULT_FROM_WIN32( ERROR_WRITE_FAULT );

		return S_OK;
	}

// 	HRESULT LockRange(__in ULONGLONG nPos, __in ULONGLONG nCount) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		LARGE_INTEGER liPos;
// 		liPos.QuadPart = nPos;

// 		LARGE_INTEGER liCount;
// 		liCount.QuadPart = nCount;

// 		if (!::LockFile(m_h, liPos.LowPart, liPos.HighPart, liCount.LowPart, liCount.HighPart))
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

// 	HRESULT UnlockRange(__in ULONGLONG nPos, __in ULONGLONG nCount) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		LARGE_INTEGER liPos;
// 		liPos.QuadPart = nPos;

// 		LARGE_INTEGER liCount;
// 		liCount.QuadPart = nCount;

// 		if (!::UnlockFile(m_h, liPos.LowPart, liPos.HighPart, liCount.LowPart, liCount.HighPart))
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

// 	HRESULT SetSize(__in ULONGLONG nNewLen) throw()
// 	{
// 		ATLASSUME(m_h != NULL);

// 		HRESULT hr = Seek(nNewLen, FILE_BEGIN);
// 		if (FAILED(hr))
// 			return hr;

// 		if (!::SetEndOfFile(m_h))
// 			return AtlHresultFromLastError();

// 		return S_OK;
// 	}

 	HRESULT GetSize(__out ULONGLONG& nLen) const throw()
 	{
 		ATLASSUME(m_h != NULL);

		int cur = ftell(m_h);
		if(fseek(m_h,0,SEEK_END)) {
			return HRESULT_FROM_WIN32(ERROR_SEEK_ON_DEVICE);
		}
		const size_t size = ftell(m_h);
		if(fseek(m_h,cur,SEEK_SET)) {
			return HRESULT_FROM_WIN32(ERROR_SEEK_ON_DEVICE);
		}
		nLen = size;
		return S_OK;

// ORIG: 		ULARGE_INTEGER liFileSize;
// ORIG:  		liFileSize.LowPart = ::GetFileSize(m_h, &liFileSize.HighPart);
// ORIG:  		if (liFileSize.LowPart == INVALID_FILE_SIZE)
// ORIG:  		{
// ORIG:  			HRESULT hr;
// ORIG: 
// ORIG:  			hr = AtlHresultFromLastError();
// ORIG:  			if (FAILED(hr))
// ORIG:  				return hr;
// ORIG:  		}
// ORIG: 
// ORIG:   		nLen = liFileSize.QuadPart;
// ORIG: 
// ORIG: 		return S_OK;
 	}

    FILE *m_h;
};

}; //namespace ATL

//#pragma pack(pop)
#endif //__ATLFILE_H__
