#include <stdlib.h>
#include <windows.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <errno.h>
#include "mman.h"

static const char id[]="$Id: tpl.c 107 2007-04-20 17:11:29Z thanson $";

/**
 * @brief Map a file to a memory region
 *
 * This function emulates the POSIX mmap() using CreateFileMapping() and
 * MapViewOfFile()
 *
 * @param addr the suggested start address (if != 0)
 * @param len length of the region
 * @param prot region accesibility, bitwise OR of PROT_READ, PROT_WRITE, PROT_EXEC
 * @param flags mapping type and options (ignored)
 * @param fd object to be mapped into memory
 * @param offset offset into mapped object
 * @return pointer to the memory region, or NULL in case of error
 */
void *mmap(void *addr, unsigned int len, int prot, int flags, int fd, unsigned int offset)
{
	DWORD wprot;
	DWORD waccess;
	HANDLE h;
	void *region;

	/* Translate read/write/exec flags into WIN32 constants */
	switch (prot) {
	case PROT_READ:
		wprot = PAGE_READONLY;
		break;
	case PROT_EXEC:
		wprot = PAGE_EXECUTE_READ;
		break;
	case PROT_READ | PROT_EXEC:
		wprot = PAGE_EXECUTE_READ;
		break;
	case PROT_WRITE:
		wprot = PAGE_READWRITE;
		break;
	case PROT_READ | PROT_WRITE:
		wprot = PAGE_READWRITE;
		break;
	case PROT_READ | PROT_WRITE | PROT_EXEC:
		wprot = PAGE_EXECUTE_READWRITE;
		break;
	case PROT_WRITE | PROT_EXEC:
		wprot = PAGE_EXECUTE_READWRITE;
		break;
	}
	
	/* Obtaing handle to map region */
	h = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, wprot, 0, len, 0);
	if (h == NULL) {
		DWORD error = GetLastError();

		/* Try and translate some error codes */
		switch (error) {
		case ERROR_ACCESS_DENIED:
		case ERROR_INVALID_ACCESS:
			errno = EACCES;
			break;
		case ERROR_OUTOFMEMORY:
		case ERROR_NOT_ENOUGH_MEMORY:
			errno = ENOMEM;
			break;
		default:
			errno = EINVAL;
			break;
		}
		return MAP_FAILED;
	}
			

	/* Translate sharing options into WIN32 constants */
	switch (wprot) {
	case PAGE_READONLY:
		waccess = FILE_MAP_READ;
		break;
	case PAGE_READWRITE:
		waccess = FILE_MAP_WRITE;
		break;
	}

	/* Map file and return pointer */
	region = MapViewOfFile(h, waccess, 0, 0, 0);
	if (region == NULL) {
		DWORD error = GetLastError();

		/* Try and translate some error codes */
		switch (error) {
		case ERROR_ACCESS_DENIED:
		case ERROR_INVALID_ACCESS:
			errno = EACCES;
			break;
		case ERROR_INVALID_HANDLE:
			errno = EBADF;
			break;
		default:
			errno = EINVAL;
			break;
		}
		CloseHandle(h);
		return MAP_FAILED;
	}
	
	/* All fine */
	return region;
}


/**
 * @brief Unmap a memory region
 *
 * This is a wrapper around UnmapViewOfFile in the win32 API
 * 
 * @param addr start address
 * @param len length of the region
 * @return 0 for success, -1 for error
 */
int munmap(void *addr, int len) 
{
	if (UnmapViewOfFile(addr)) {
		return 0;
	}
	else {
		errno = EINVAL;
		return -1;
	}
}


/**
 * Synchronize a mapped region
 *
 * This is a wrapper around FlushViewOfFile
 *
 * @param addr start address
 * @param len number of bytes to flush
 * @param flags sync options -- currently ignored
 * @return 0 for success, -1 for error
 */
int msync(char *addr, int len, int flags) 
{
	if (FlushViewOfFile(addr, len) == 0) {
		DWORD error = GetLastError();
		
		/* Try and translate some error codes */
		switch (error) {
		case ERROR_INVALID_PARAMETER:
			errno = EINVAL;
			break;
		case ERROR_WRITE_FAULT:
			errno = EIO;
			break;
		default:
			errno = EINVAL;
			break;
		}
		return -1;
	}

	/* Success */
	return 0;
}
