#include <../source/TAL/objbase.h>
#include <../source/vfs/iobase.h>
#include <../source/PAL/dataman.h>

#ifdef	CHECKED_BUILD

/*
 *	Note: IOPORT_MAGIC #define'd in iobase.h
 */

V_LOCAL char	*io_port_object = "i/o port";

V_LOCAL	char *stub_warning_fmt="file->vtbl->%s(): method undefined.\n";
#define COMPLAIN_STUB(caller) \
		conlPrintf(LOG_ERROR,stub_warning_fmt,(caller));

#else	/* !CHECKED_BUILD */

#define COMPLAIN_STUB(caller)

#endif	/* !CHECKED_BUILD */

#define GET_IO_PORT(handle,caller) \
        (CHECK_AND_GET_OBJECT(ioport_base_t,handle,caller, \
            io_port_object,IOPORT_MAGIC))

#include <../source/PAL/argsused.h>
V_LOCAL vsize_t	V_CALL
io_read_stub(vhandle_t handle,void *buf,vsize_t len)
{
	COMPLAIN_STUB("read");
	return 0;	/* nothing to read */
}

#include <../source/PAL/argsused.h>
V_LOCAL vsize_t	V_CALL
io_write_stub(vhandle_t handle,void *buf,vsize_t len)
{
	COMPLAIN_STUB("write");
	return 0;	/* no destination to write to */
}

#include <../source/PAL/argsused.h>
V_LOCAL vsize_t	V_CALL
io_seek_stub(vhandle_t handle,vssize_t offset,int whence)
{
	COMPLAIN_STUB("seek");
	return INVALID_IO_OFFSET;
}

#include <../source/PAL/argsused.h>
V_LOCAL vsize_t	V_CALL
io_tell_stub(vhandle_t handle)
{
	COMPLAIN_STUB("tell");
	return INVALID_IO_OFFSET;
}

#include <../source/PAL/argsused.h>
V_LOCAL int	V_CALL
io_control_stub(vhandle_t handle,unsigned code,void *parameters)
{
	COMPLAIN_STUB("ioctl");
	return -1;	/* error condition: all codes unknown */
}

V_LOCAL u32_t V_CALL
ioport_destroy(vhandle_t handle)
{
	ioport_base_p self = (ioport_base_p)handle;
	UNREF_EXIT(self);
	memFreeObject(self);
	return 0;
}

/*
 * ioAllocPort(): returns object which can nothing except
 * to destroy itself. Normally used for creation of descendant
 * objects; in later case, 'size_of_real_port' must be equal
 * to sizeof(descendant).
 */
vhandle_t V_CALL
ioAllocPort(vsize_t size_of_real_port)
{
#ifdef	CHECKED_BUILD
	static char *caller = "ioAllocPort";
#endif	/* CHECKED_BUILD */
	ioport_base_p port;
    if(size_of_real_port < sizeof(ioport_base_t))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"%s(%u): size less than sizeof(ioport_base_t) (%u).\n",
			caller,size_of_real_port,sizeof(ioport_base_t));
#endif	/* CHECKED_BUILD */
    	return LIBV_INVALID_HANDLE;
	}
	ALLOC_OBJECT(port,ioport_base_t,
		size_of_real_port - sizeof(ioport_base_t),IOPORT_MAGIC);
	port->base.destroy = ioport_destroy;
    port->read = io_read_stub;
	port->write = io_write_stub;
	port->seek = io_seek_stub;
	port->tell = io_tell_stub;
	port->ioctl = io_control_stub;
	return (vhandle_t)port;
}

/*
 * Global functions to operate with io ports
 */
vsize_t	V_GLOBAL
fileRead(vhandle_t handle,void *buf,vsize_t len)
{
#ifdef	CHECKED_BUILD
	static char *caller	= "fileRead";
#endif	/* CHECKED_BUILD */
	ioport_base_p self = GET_IO_PORT(handle,"fileRead");
	if(self == LIBV_INVALID_HANDLE)
		return 0;
	if(!buf || !len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"%s(%X,%X,%d): incorrect destination.\n",
			caller,handle,buf,len);
#endif	/* CHECKED_BUILD */
		return 0;
	}
	return self->read(self,buf,len);
}

vsize_t	V_GLOBAL
fileWrite(vhandle_t handle,void *buf,vsize_t len)
{
#ifdef	CHECKED_BUILD
	static char *caller	= "fileWrite";
#endif	/* CHECKED_BUILD */
	ioport_base_p self = GET_IO_PORT(handle,caller);
	if(self == LIBV_INVALID_HANDLE)
		return 0;
	if(!buf || !len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"%s(%X,%X,%d): incorrect source/len.\n",caller,handle,buf,len);
#endif	/* CHECKED_BUILD */
		return 0;
	}
	return self->write(self,buf,len);
}

vsize_t	V_GLOBAL
fileSeek(vhandle_t handle,vssize_t offset,int whence)
{
	ioport_base_p self = GET_IO_PORT(handle,"fileSeek");
	if(self == LIBV_INVALID_HANDLE)
		return INVALID_IO_OFFSET;
	return self->seek(self,offset,whence);
}

vsize_t	V_GLOBAL
fileTell(vhandle_t handle)
{
	ioport_base_p self = GET_IO_PORT(handle,"fileTell");
	if(self == LIBV_INVALID_HANDLE)
		return INVALID_IO_OFFSET;
	return self->tell(self);
}

vsize_t	V_GLOBAL
fileSize(vhandle_t handle)
{
	vsize_t filepos, end;
	ioport_base_p self = GET_IO_PORT(handle,"fileSize");
	if(self == LIBV_INVALID_HANDLE)
		return 0;
	if(self->ioctl(self,pctl_get_size,&filepos) != -1)
		return filepos;
	/*
	 * Dedicated ioctl() not supported; fall back to default algorithm.
	 * FIXME: works only with block devs !
	 */
	filepos = self->tell(self);
   	end = self->seek(self,0,SEEK_END);
    self->seek(self,filepos,SEEK_SET);
   	return end;
}

vboolean	V_GLOBAL
fileEof(vhandle_t handle)
{
	vboolean ret;
	ioport_base_p self = GET_IO_PORT(handle,"fileEof");
	if(self == LIBV_INVALID_HANDLE)
		return true;
	if(!self->ioctl(self,pctl_is_eof,&ret))
		return ret;
	/*
	 * Dedicated ioctl() not supported; fall back to default algorithm.
	 */
	return fileTell(handle) == fileSize(handle);
}

vboolean	V_GLOBAL
fileClose(vhandle_t handle)
{
	if(libvCloseHandle(handle,"fileClose",io_port_object,IOPORT_MAGIC))
		return false;
	else
		return true;
}

vboolean	V_GLOBAL
fileControl(vhandle_t handle,unsigned code,void *parameters)
{
	int rc;
	ioport_base_p self = GET_IO_PORT(handle,"fileControl");
	if(self == LIBV_INVALID_HANDLE)
		return false;
	rc = self->ioctl(self,code,parameters);
    if(!rc)
    	return true;
#ifdef	CHECKED_BUILD
	else
    {
    	conlPrintf(LOG_ERROR,"file->ioctl(%d,%X) failed (rc %d).\n",
        	code,parameters,rc);
    }
#endif	/* CHECKED_BUILD */
	return false;
}
