/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/


#if defined(_MSC_VER) && (_MSC_VER >= 1400)
#pragma warning(disable:4740)
#endif

#define _WIN32_WINNT 0x0400
#include "macros.h"

#define	BUFSIZE	16384

static DWORD CALLBACK DefCopyProgressRoutine(
	LARGE_INTEGER	TotalFileSize,	// total file size, in bytes
	LARGE_INTEGER	TotalBytesTransferred,
									// total number of bytes transferred
	LARGE_INTEGER	StreamSize,		// total number of bytes for this stream
	LARGE_INTEGER	StreamBytesTransferred,
									// total number of bytes transferred for
									// this stream
	DWORD		dwStreamNumber,		// the current stream
	DWORD		dwCallbackReason,	// reason for callback
	HANDLE	hSourceFile,			// handle to the source file
	HANDLE	hDestinationFile,		// handle to the destination file
	LPVOID	lpData					// passed by CopyFileEx
)
{
	return PROGRESS_CONTINUE;
}


IMPLEMENT_THUNK( kernel32, WINDOWS, BOOL, WINAPI, CopyFileExA, ( LPCSTR lpExistingFileNameA, LPCSTR lpNewFileNameA, LPPROGRESS_ROUTINE  lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags ) )
{
	BOOL	fSuccess = FALSE; // Assume failure

	HANDLE	hSourceFile = CreateFileA(
		lpExistingFileNameA,
		GENERIC_READ,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0,
		NULL
		);

	if ( IsValidHandle(hSourceFile) )
	{
		LARGE_INTEGER	FileSize, BytesTransferred;
		HANDLE	hTargetFile = NULL;

		SetLastError( ERROR_SUCCESS );
		FileSize.LowPart = GetFileSize( hSourceFile, (LPDWORD)&FileSize.HighPart );
		BytesTransferred.QuadPart = 0;

		if ( (DWORD)-1 != FileSize.LowPart || ERROR_SUCCESS == GetLastError() )
			hTargetFile = CreateFileA(
				lpNewFileNameA,
				GENERIC_WRITE,
				0,
				NULL,
				(DWORD) ((dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS),
				0,
				NULL
				);

		if ( IsValidHandle(hTargetFile) )
		{
			DWORD dwProgressResult = PROGRESS_CONTINUE;

			fSuccess = SetEndOfFile( hTargetFile );

			if ( fSuccess )
			{
				if ( !lpProgressRoutine )
					lpProgressRoutine = DefCopyProgressRoutine;

				dwProgressResult = lpProgressRoutine(
					FileSize,
					BytesTransferred,
					FileSize,
					BytesTransferred,
					1,
					CALLBACK_STREAM_SWITCH,
					hSourceFile,
					hTargetFile,
					lpData
					);

				// Suppress further notifications

				if ( PROGRESS_QUIET == dwProgressResult )
				{
					lpProgressRoutine = DefCopyProgressRoutine;
					dwProgressResult = PROGRESS_CONTINUE;
				}
			}

			while ( fSuccess && PROGRESS_CONTINUE == dwProgressResult )
			{
				BYTE	buffer[BUFSIZE];
				DWORD	dwBytesRead, dwBytesWritten = 0;

				fSuccess = ReadFile( hSourceFile, buffer, BUFSIZE, &dwBytesRead, NULL );

				if ( !dwBytesRead ) break;

				if ( fSuccess )
					fSuccess = WriteFile( hTargetFile, buffer, dwBytesRead, &dwBytesWritten, NULL );

				if ( fSuccess )
				{
					BytesTransferred.QuadPart += (LONGLONG)dwBytesWritten;

					if ( pbCancel && *pbCancel )
						dwProgressResult = PROGRESS_CANCEL;
					else
						dwProgressResult = lpProgressRoutine(
							FileSize,
							BytesTransferred,
							FileSize,
							BytesTransferred,
							1,
							CALLBACK_CHUNK_FINISHED,
							hSourceFile,
							hTargetFile,
							lpData
							);

				}

			}

			CloseHandle( hTargetFile );

			if ( PROGRESS_CANCEL == dwProgressResult )
				DeleteFileA( lpNewFileNameA );
		}


		CloseHandle( hSourceFile );
	}

	return fSuccess;
}