#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>
#include <../source/PAL/fsops.h>
#include <../source/PAL/oshead.h>
#include <../source/vfs/fbase.h>

#define	ARCH_INVALID_FILE		INVALID_HANDLE_VALUE
typedef HANDLE					arch_file_t;

struct arch_filemap_s
{
	void_p	base_addr;
	HANDLE	mapping_handle;
};

arch_file_t	V_CALL
archFileOpen(char *right_slashed_name,filemode_t filemode)
{
	HANDLE fd;
	char os_name[_MAX_PATH] = "";
	DWORD desired_access, share_mode, creation_distribution;
	switch(filemode)
	{
		case filemode_read:
			desired_access = GENERIC_READ;
			share_mode = FILE_SHARE_READ;
			creation_distribution = OPEN_EXISTING;
			break;
		case filemode_write:
			desired_access = GENERIC_READ | GENERIC_WRITE;
			share_mode = 0;
			creation_distribution = CREATE_ALWAYS;
			break;
		case filemode_append:
			desired_access = GENERIC_READ | GENERIC_WRITE;
			share_mode = 0;
			creation_distribution = OPEN_EXISTING;
			break;
		default:
			return ARCH_INVALID_FILE;
	}
    vfsUnix2Native(os_name,right_slashed_name);
	fd = CreateFile(os_name,desired_access,share_mode,NULL,
		creation_distribution,0,NULL);
	if(fd == INVALID_HANDLE_VALUE)
	{
#ifdef	ARCH_VERBOSE
		conlPrintf(LOG_ERROR,"archOpenFile('%s'): CreateFile() failed (%u).\n",
			os_name,GetLastError());
#endif	/* ARCH_VERBOSE */
		return ARCH_INVALID_FILE;
	}
	if(filemode == filemode_append)
		SetFilePointer(fd,0,NULL,FILE_END);
	return fd;
}

u32_t	V_CALL
archFileSize(arch_file_t file)
{
	DWORD size_lo, size_hi;
	size_lo = GetFileSize(file,&size_hi);
	if(size_hi)
		return 0xFFFFFFFF;
	return (u32_t)size_lo;
}

u32_t	V_CALL
archFileTell(arch_file_t file)
{
	return SetFilePointer(file,0,NULL,FILE_CURRENT);
}

u32_t	V_CALL
archFileSeek(arch_file_t file,u32_t offset,u32_t whence)
{
	DWORD move_method;
	switch(whence)
	{
		case SEEK_SET:
			move_method = FILE_BEGIN;
			break;
		case SEEK_CUR:
			move_method = FILE_CURRENT;
			break;
		case SEEK_END:
			move_method = FILE_END;
			break;
		default:
			return 0xFFFFFFFF;
	}
	return SetFilePointer(file,offset,NULL,move_method);
}

u32_t	V_CALL
archFileRead(arch_file_t file,void *buffer,u32_t len)
{
	DWORD bytes_read;
	if(ReadFile(file,buffer,len,&bytes_read,NULL))
		return bytes_read;
	else
		return 0;
}

u32_t	V_CALL
archFileWrite(arch_file_t file,void *buffer,u32_t len)
{
	DWORD bytes_written;
	if(WriteFile(file,buffer,len,&bytes_written,NULL))
		return bytes_written;
	else
		return 0;
}

void	V_CALL
archFileClose(arch_file_t file)
{
	CloseHandle(file);
}

/*
 * Mapping length can be 0; in this case size-start bytes will be mapped.
 */
void_p	V_CALL
archMapRead(arch_file_t file,u32_t start,u32_t len,struct arch_filemap_s *map)
{
	DWORD size = GetFileSize(file,NULL);
	if(!size)
	{
#ifdef	ARCH_VERBOSE
		conlPuts(LOG_ERROR,
"archMapRead(): empty file, couldn't mmap().\n");
#endif	/* ARCH_VERBOSE */
		return NULL;
	}
	if(!len)
		len = size - start;
	if(start+len > size)
	{
#ifdef	ARCH_VERBOSE
		conlPuts(LOG_ERROR,
"archMapRead(): file too small (%u),"
			" couldn't mmap() %u bytes at 0x%08X.\n",size,len,start);
#endif	/* ARCH_VERBOSE */
		return NULL;
	}
	map->mapping_handle = CreateFileMapping(file,NULL,PAGE_READONLY,0,size,NULL);
	if(map->mapping_handle == INVALID_HANDLE_VALUE)
	{
#ifdef	ARCH_VERBOSE
		conlPrintf(LOG_ERROR,
			"archMapRead(): CreateFileMapping() failed (code %u).\n",
			GetLastError());
#endif	/* ARCH_VERBOSE */
		map->base_addr = NULL;
		return NULL;
	}
	map->base_addr = MapViewOfFile(map->mapping_handle,FILE_MAP_READ,0,start,len);
	if(!map->base_addr)
	{
#ifdef	ARCH_VERBOSE
		conlPrintf(LOG_ERROR,
			"archMapRead(): MapViewOfFile() failed (code %u).\n",
			GetLastError());
#endif	/* ARCH_VERBOSE */
		CloseHandle(map->mapping_handle);
		return NULL;
	}
	return map->base_addr;
}

/*
 * Mapping length must be non-0.
 */
void_p	V_CALL
archMapWrite(arch_file_t file,u32_t start,u32_t len,struct arch_filemap_s *map)
{
	DWORD size = GetFileSize(file,NULL);
	if(!len)
		return NULL;
	if(size < (start + len))
		size = start + len;
	map->mapping_handle = CreateFileMapping(file,NULL,PAGE_READWRITE,0,size,NULL);
	if(map->mapping_handle == INVALID_HANDLE_VALUE)
	{
#ifdef	ARCH_VERBOSE
		conlPrintf(LOG_ERROR,
			"archMapWrite(): CreateFileMapping() failed (code %u).\n",
			GetLastError());
#endif	/* ARCH_VERBOSE */
		map->base_addr = NULL;
		return NULL;
	}
	map->base_addr = MapViewOfFile(map->mapping_handle,FILE_MAP_WRITE,0,start,len);
	if(!map->base_addr)
	{
#ifdef	ARCH_VERBOSE
		conlPrintf(LOG_ERROR,
			"archMapWrite(): MapViewOfFile() failed (code %u).\n",
			GetLastError());
#endif	/* ARCH_VERBOSE */
		CloseHandle(map->mapping_handle);
		return NULL;
	}
	return map->base_addr;
}

void	V_CALL
archUnmap(struct arch_filemap_s *map)
{
	if(map->mapping_handle != INVALID_HANDLE_VALUE)
	{
		if(map->base_addr)
		{
			UnmapViewOfFile(map->base_addr);
			map->base_addr = NULL;
		}
		CloseHandle(map->mapping_handle);
	}
	return;
}

int main()
{
	int i;
	u8_t *base;
	arch_file_t f;
	struct arch_filemap_s map;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	f = archFileOpen("test",filemode_write);
	if(f == ARCH_INVALID_FILE)
		return -1;
	base = (u8_t*)archMapWrite(f,0,10,&map);
	if(!base)
	{
		archFileClose(f);
		return -1;
	}
	for(i=0;i<10;i++)
		base[i] = 'a'+i;
	archUnmap(&map);
	archFileClose(f);
	return 0;
}
