#include <../source/TAL/objbase.h>
#include <../source/vfs/iobase.h>
#include <../source/vfs/fbase.h>
#include <../source/vfs/mbase.h>
#include <../arch/arch.h>

/*
 * Implementation of memory-mapped, read-only I/O port
 */

V_LOCAL vsize_t V_CALL
section_read(void *object,void *buf,vsize_t len)
{
	io_section_p self = (io_section_p)object;
	vsize_t avail = self->size - self->ptr;
	if(avail < len)
		len = avail;
	memCopy(buf,self->base + self->ptr,len);
	self->ptr += len;
	return len;
}

V_LOCAL vsize_t V_CALL
section_seek(void *object,vssize_t offset,int whence)
{
	io_section_p self = (io_section_p)object;
	switch(whence)
	{
		case SEEK_SET:
			/* this cast also handles incorrect negative offsets */
			if((vsize_t)offset > self->size)
				return INVALID_IO_OFFSET;
			self->ptr = (vsize_t)offset;
			break;
		case SEEK_CUR:
			if(offset < 0)
			{
				if((vsize_t)(-offset) > self->ptr)
					return INVALID_IO_OFFSET;
			}
			else
			{
				if((vsize_t)(offset) > (self->size - self->ptr))
					return INVALID_IO_OFFSET;
			}
			self->ptr = self->ptr + offset;
			break;
		case SEEK_END:
			if((u32_t)(-offset) > self->size)
				return INVALID_IO_OFFSET;
			self->ptr = self->size + offset;
			break;
		default:
			return INVALID_IO_OFFSET;
	}
	return self->ptr;
}

V_LOCAL vsize_t V_CALL
section_tell(void *object)
{
	io_section_p self = (io_section_p)object;
	return self->ptr;
}

V_LOCAL	int V_CALL
section_ioctl(void *object,unsigned code,void *parameters)
{
#ifdef	CHECKED_BUILD
	static char *caller = "section_ioctl";
#endif	/* CHECKED_BUILD */
	io_section_p self = (io_section_p)object;
	switch(code)
	{
		case pctl_get_name:
			if(parameters)
			{
				strCopy((char*)parameters,self->section_name,_MAX_PATH);
				return 0;
			}
			return -1;
		case pctl_get_type:
			if(!parameters)
				return -1;
		   	*((vsize_t*)parameters)=PCTL_BLOCK_DEVICE | PCTL_SUPPORT_MMAP;
			return 0;
		case pctl_map_ispace:
			if(!parameters)
				return -1;
		   	*((void**)parameters) = self->base;
			return 0;
		case pctl_get_size:
			if(!parameters)
				return -1;
		   	*((vsize_t*)parameters) = self->size;
			return 0;
		case pctl_is_eof:
			if(!parameters)
				return -1;
		   	*((vboolean*)parameters) = (self->size == self->ptr) ? true : false;
			return 0;
		case pctl_duplicate_handle:
			/*
			 * The duplication of the section can be tricky, since we don't know
			 * the source of data and we can't just increase the reference counter
			 * because we need separate begin/current/end pointers.
			 * 
			 * If the original data comes from a mmap()'ed file, we can re-open it;
			 * otherwise, we must copy entire data.
			 */
			if(!parameters)
				return -1;
			*((vhandle_t*)parameters) = LIBV_INVALID_HANDLE;
			if(self->section_name[0] && archFileExists(self->section_name))
				*((vhandle_t*)parameters) = fileOpenRead(self->section_name);
			if(*((vhandle_t*)parameters) == LIBV_INVALID_HANDLE)
			{
#ifdef	CHECKED_BUILD
				conlPrintf(LOG_WARNING,"%s(): blind handle duplication, "
					"consider code rewrite.\n",caller);
#endif	/* CHECKED_BUILD */
				*((vhandle_t*)parameters) = ioStoreInNamedSection(self->base,self->size,self->section_name);
			}
			return (*((vhandle_t*)parameters) != LIBV_INVALID_HANDLE) ? 0 : -1;
		default:
			break;
	}
	return -1;
}

V_LOCAL u32_t V_CALL
section_destroy(vhandle_t handle)
{
	io_section_p self = (io_section_p)handle;
	UNREF_EXIT(&self->vtbl);
	memFreeObject(self);
	return 0;
}

/* Factory function */
io_section_p V_CALL
ioAllocNamedSection(vsize_t real_port_size,void *mmap_base,vsize_t mmap_size,
	char *section_name)
{
	io_section_p self;
	if(real_port_size < sizeof(io_section_t))
	{
		/* Descendant can't be less in size than ancestor */
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"ioAllocSection(): "
			"section must be at least %u bytes, got %u.\n",
			sizeof(io_section_t),real_port_size);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	self = (io_section_t*)ioAllocPort(real_port_size);
	if(self == LIBV_INVALID_HANDLE)
		return LIBV_INVALID_HANDLE;
	self->vtbl.base.destroy = section_destroy;
	self->vtbl.read = section_read;
	self->vtbl.seek = section_seek;
	self->vtbl.tell = section_tell;
	self->vtbl.ioctl = section_ioctl;
	self->base=(u8_p)mmap_base;
	self->size=mmap_size;
	self->ptr=0;
	if(section_name && section_name[0])
		strCopy(self->section_name,section_name,_MAX_PATH);
	else
		self->section_name[0] = 0;
	return self;
}

io_section_p V_CALL
ioAllocSection(vsize_t real_port_size,void *mmap_base,vsize_t mmap_size)
{
	return ioAllocNamedSection(real_port_size,mmap_base,mmap_size,NULL);
}

io_section_p V_CALL
ioStoreInNamedSection(void_p buf,vsize_t len,char *section_name)
{
	io_section_p self = ioAllocNamedSection(sizeof(io_section_t) + len,NULL,0,section_name);
	if(self == LIBV_INVALID_HANDLE)
		return LIBV_INVALID_HANDLE;
	self->base = (u8_p)(self + 1);
	self->size = len;
	memCopy(self->base,buf,len);
	return self;
}
