/*
 *	fbase.c: implementation of base file object.
 */

#include <../source/TAL/objbase.h>
#include <../source/vfs/iobase.h>
#include <../source/vfs/fbase.h>
#include <../source/vfs/mbase.h>
#include <../source/PAL/mmap.h>
#include <../source/PAL/fsops.h>

typedef struct	vfs_file_s
{
	ioport_base_t	vtbl;
	u32_t			fsize;
	FILE			*handle;
}	vfs_file_t, *vfs_file_p;

typedef struct	vfs_mapped_file_s
{
	io_section_t	port;
	mmap_t			map;
}	vfs_mapped_file_t, *vfs_mapped_file_p;

#define	FILEPORT(obj)		((vfs_file_t*)(obj))

V_LOCAL u32_t V_CALL lfs_read(void *object,void *buf,u32_t len)
{
	return fread(buf,1,len,FILEPORT(object)->handle);
}

V_LOCAL u32_t V_CALL lfs_write(void *object,void *buf,u32_t len)
{
	return fwrite(buf,1,len,FILEPORT(object)->handle);
}

V_LOCAL u32_t V_CALL lfs_tell(void *object)
{
	long int retval=ftell(FILEPORT(object)->handle);
	return (retval!=-1) ? (u32_t)retval : INVALID_IO_OFFSET;
}

V_LOCAL u32_t V_CALL lfs_seek(void *object,s32_t offset,int whence)
{
	if(fseek(FILEPORT(object)->handle,offset,whence))
		return INVALID_IO_OFFSET;
	return lfs_tell(object);
}

V_LOCAL u32_t V_CALL lfs_destroy(void *object)
{
	if(--FILEPORT(object)->vtbl.base.ref_count)
    	return FILEPORT(object)->vtbl.base.ref_count;
	if(FILEPORT(object)->handle!=stdout)
		fclose(FILEPORT(object)->handle);
	memFreeObject(object);
	return 0;
}

V_LOCAL u32_t V_CALL mapped_destroy(void *object)
{
	if(--FILEPORT(object)->vtbl.base.ref_count)
    	return FILEPORT(object)->vtbl.base.ref_count;
	if(((vfs_mapped_file_t*)object)->map)
		mmapClose(((vfs_mapped_file_t*)object)->map);
	memFreeObject(object);
	return 0;
}

V_LOCAL	int V_CALL lfs_ioctl(void *object,int code,void *parameters)
{
	switch(code)
    {
    	case pctl_get_type:
        	if(!parameters)
            	return -1;
			if(FILEPORT(object)->vtbl.seek==lfs_seek)
               	*((u32_t*)parameters)=PCTL_BLOCK_DEVICE;
			else
               	*((u32_t*)parameters)=0;
            return 0;
	}
	return -1;
}

#undef	FILEPORT

/*
 *	Factory function for memory-mapped files
 */
file_t V_LOCAL lfsMapFile(char *unix_name)
{
	mmap_t map;
	vfs_mapped_file_p port;
	map=mmapOpenRead(unix_name);
	if(!map)
		return NULL;
	port=(vfs_mapped_file_p)ioAllocSection(
		sizeof(vfs_mapped_file_t),mmapBase(map),mmapSize(map));
	port->port.vtbl.base.destroy=mapped_destroy;
	port->map=map;
	return port;
}

/*
 *	Creates IO port for disk file
 */
file_t V_CALL
lfsCreateFile(char *name,filemode_t mode)
{
	file_t mapped;
	FILE *handle;
	char *fmode;
	vfs_file_t *f;
#ifndef	RIGHT_SLASHED
   	char os_name_buf[_MAX_PATH];
   	char *os_name; /* will be set to os_name_buf later */
#else	/* !RIGHT_SLASHED */
	char *os_name=name;
#endif	/* RIGHT_SLASHED */
	if(!name)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"lfsCreateFile(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	if(mode==filemode_read)
	{
		/* Try to map file */
		mapped=lfsMapFile(name);
		if(mapped)
			return mapped;
		/* mmap() failed; try to use STDC i/o */
		fmode="rb";
	}
	else
	{
		char dir[_MAX_DIR];
		switch(mode)
		{
			case filemode_append:	fmode="ab"; break;
			case filemode_write:	fmode="wb"; break;
			default:				fmode=NULL; break;
		}
		if(!fmode)
		{
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"lfsCreateFile('%s','%d'): unknown mode '%d'.\n",
				name,(int)mode,(int)mode);
#endif	/* CHECKED_BUILD */
			return NULL;
		}
		/* Check for target directory */
		fsSplitPath(name,dir,NULL,NULL);
		if(dir[0])
        {
        	char curdir[_MAX_DIR];
            lfsGetDir(curdir,_MAX_DIR);
			if(!lfsSetDir(dir,true))
				return NULL;
			lfsSetDir(curdir,false);
		}
	}
#ifndef	RIGHT_SLASHED
	/* We are going to call NT; convert unix name to dos name */
	os_name=os_name_buf;
   	vfsUnix2Native(os_name,name);
#endif	/* RIGHT_SLASHED */
	if(strcasecmp(os_name,"CON"))
    {
		handle=fopen(os_name,fmode);
		if(!handle)
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"lfsCreateFile(): fopen('%s','%c') failed.\n",
				os_name,fmode[0]);
#endif		/* CHECKED_BUILD */
			return NULL;
		}
	}
	else
    {
    	if(mode == filemode_read)
	    	handle=stdin;
		else
        	handle=stdout;
	}
	f=(vfs_file_t*)ioAllocPort(sizeof(vfs_file_t));
	f->handle=handle;
	if(mode==filemode_read)
	{
		f->vtbl.read=lfs_read;
		fseek(handle,0,SEEK_END);
		f->fsize=(u32_t)ftell(handle);
		rewind(handle);
		if(f->fsize!=(u32_t)-1)
		{
			/* block device */
			f->vtbl.seek=lfs_seek;
			f->vtbl.tell=lfs_tell;
		}
	}
	else
	{
		f->vtbl.write=lfs_write;
		f->vtbl.seek=lfs_seek;
		f->vtbl.tell=lfs_tell;
		f->fsize=0;
	}
	f->vtbl.base.destroy=lfs_destroy;
	f->vtbl.ioctl=lfs_ioctl;
	return (file_t)f;
}

#ifdef	CHECKED_BUILD
/*
 *	Quiet version of lfsCreateFile()
 */
file_t	V_CALL
lfsCreateFileQuiet(char *name,filemode_t mode)
{
	u32_t ll = logUpperLevel(LOG_MUTE);
	file_t file = lfsCreateFile(name,mode);
    logUpperLevel(ll);
    return file;
}

#endif	/* CHECKED_BUILD */
