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

/*
 * TODO: convert to use io_section_t
 */
 
#ifdef		CHECKED_BUILD
#define		MMAP_MAGIC		0x4D4D4150 		/* MMAP */
static char	*mmap_object="file mapping";
#endif		/* CHECKED_BUILD */

typedef struct file_map_s
{
	libv_object_t base;
#ifdef		__UNIX__
	int		fd;
#elif		defined(__WIN32__)
	HANDLE	fd;
	HANDLE	md;
#endif
	size_t	len;
	void	*base_addr;
}	file_map_t, *file_map_p;

V_LOCAL	u32_t V_CALL
mmap_destroy(vhandle_t handle)
{
	file_map_p fmap=(file_map_p)handle;
	if(--fmap->base.ref_count)
		return fmap->base.ref_count;
#ifdef	__WIN32__
	if(fmap->md && fmap->md!=INVALID_HANDLE_VALUE)
	{
		if(fmap->base_addr)
			UnmapViewOfFile(fmap->base_addr);
		CloseHandle(fmap->md);
	}
	if(fmap->fd && fmap->fd!=INVALID_HANDLE_VALUE)
		CloseHandle(fmap->fd);
#elif	defined(__UNIX__)
	if(fmap->base_addr)
		munmap(fmap->base_addr,fmap->len);
	if(fmap->fd!=-1)
		close(fmap->fd);
#endif	/* __UNIX__ */
	memFreeObject(handle);
	return 0;
}

#ifdef	__UNIX__

mmap_t	V_GLOBAL
mmapOpenRead(char *file_name)
{
	int fd;
	struct stat st;
	void *base;
	file_map_p res;
	fd=open(file_name,0600);
	if(fd==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"mmapOpen('%s'): open() failed (code %d).\n",
            dbs(file_name),errno);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	fstat(fd,&st);
	base=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,fd,0);
	if(!base || (int)base==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"mmapOpen('%s'): mmap() failed: %s.\n",
			file_name,strerror(errno));
#endif	/* CHECKED_BUILD */
		close(fd);
		return NULL;
	}
	ALLOC_OBJECT(res,file_map_t,0,MMAP_MAGIC);
	res->base.destroy=mmap_destroy;
	res->fd=fd;
	res->len=st.st_size;
	res->base_addr=base;
	return (mmap_t)res;
}

#elif	defined(__WIN32__)

mmap_t	V_GLOBAL
mmapOpenRead(char *file_name)
{
	DWORD type,size;
	HANDLE fd, md;
	void *base;
	file_map_p res;
	char os_name[_MAX_PATH];
    if(!file_name)
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"mmapOpenRead(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
    }
    vfsUnix2Native(os_name,file_name);
	fd=CreateFile(os_name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
	if(fd==INVALID_HANDLE_VALUE)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"mmapOpen(%s): CreateFile(%s) failed (code %u).\n",
			file_name,os_name,GetLastError());
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	type=GetFileType(fd);
	if(type!=FILE_TYPE_DISK)
	{
#if 	0
		switch(type)
		{
			case FILE_TYPE_CHAR:
				/* Error: attempt to map character device */
				break;
			case FILE_TYPE_PIPE:
				/* Error: attempt to map pipe */
				break;
			default:
				/* Error: unknown file type */
				break;
		}
#endif	/* 0 */
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"mmapOpen('%s'): can't CreateFileMapping():"
			" isn't regular file.\n",file_name);
#endif	/* CHECKED_BUILD */
		CloseHandle(fd);
		return NULL;
	}
	size=GetFileSize(fd,NULL);
	md=CreateFileMapping(fd,NULL,PAGE_READONLY,0,size,NULL);
	if(md==INVALID_HANDLE_VALUE)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"mmapOpen(%s): CreateFileMapping() failed (code %u).\n",
			file_name,GetLastError());
#endif	/* CHECKED_BUILD */
		CloseHandle(fd);
		return NULL;
	}
	base=MapViewOfFile(md,FILE_MAP_READ,0,0,size);
	if(!base)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"mmapOpen(%s): MapViewOfFile() failed (code %u).\n",
			file_name,GetLastError());
#endif	/* CHECKED_BUILD */
		CloseHandle(md);
		CloseHandle(fd);
		return NULL;
	}
	ALLOC_OBJECT(res,file_map_t,0,MMAP_MAGIC);
	res->base.destroy=mmap_destroy;
	res->fd=fd;
	res->md=md;
	res->len=size;
	res->base_addr=base;
	return (mmap_t)res;
}

#endif	/* __WIN32__ */

void_p	V_GLOBAL
mmapBase(mmap_t handle)
{
	if(!libvIsHandle(handle,"mmapClose",mmap_object,MMAP_MAGIC))
		return NULL;
	return ((file_map_p)handle)->base_addr;
}

u32_t	V_GLOBAL
mmapSize(mmap_t handle)
{
	if(!libvIsHandle(handle,"mmapClose",mmap_object,MMAP_MAGIC))
		return 0;
	return ((file_map_p)handle)->len;
}

void	V_GLOBAL
mmapClose(mmap_t handle)
{
	libvCloseHandle(handle,"mmapClose",mmap_object,MMAP_MAGIC);
}

