/*
 *   Creation Date: <2003/12/07 19:36:00 samuel>
 *   Time-stamp: <2004/01/07 19:28:43 samuel>
 *
 *	<diskio.c>
 *
 *	I/O wrappers
 *
 *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   version 2
 *
 */

#include "config.h"
#include "libopenbios/bindings.h"
#include "libc/diskio.h"

//#define CONFIG_DEBUG_DISKIO
#ifdef CONFIG_DEBUG_DISKIO
#define DPRINTF(fmt, args...)                   \
    do { printk(fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...)
#endif

typedef struct {
	ihandle_t ih;
	int	do_close;
	xt_t	read_xt;
	xt_t	seek_xt;

	xt_t	reopen_xt;
	xt_t	tell_xt;
	xt_t	get_path_xt;
	xt_t	get_fstype_xt;
	xt_t	open_nwrom_xt;
	xt_t	volume_name_xt;
} priv_fd_t;

#define MAX_FD 32
static priv_fd_t *file_descriptors[MAX_FD];

static int
lookup_xt( ihandle_t ih, const char *method, xt_t *xt )
{
	if( *xt )
		return 0;
	*xt = find_ih_method( method, ih );
	return (*xt) ? 0:1;
}

int
open_ih( ihandle_t ih )
{
	xt_t read_xt=0, seek_xt=0;
	priv_fd_t *fdp;
	int fd;

	if( !ih || lookup_xt(ih, "read", &read_xt) )
		return -1;
	if( lookup_xt(ih, "seek", &seek_xt) )
		return -1;

	for (fd=0; fd<MAX_FD; fd++)
		if(file_descriptors[fd]==NULL)
			break;
	if(fd==MAX_FD)
		return -1;

	fdp = malloc( sizeof(*fdp) );
	/* Better clear the fd, as it
	 * contains valuable information
	 */
	memset(fdp, 0, sizeof(*fdp));
	fdp->ih = ih;
	fdp->read_xt = read_xt;
	fdp->seek_xt = seek_xt;
	fdp->do_close = 0;

	file_descriptors[fd]=fdp;
        DPRINTF("%s(0x%lx) = %d\n", __func__, (unsigned long)ih, fd);
	return fd;
}

int
open_io( const char *spec )
{
	int fd;
	ihandle_t ih = open_dev( spec );
	priv_fd_t *fdp;

        DPRINTF("%s(%s)\n", __func__, spec);
	if( !ih )
		return -1;

	if( (fd=open_ih(ih)) == -1 ) {
		close_dev( ih );
		return -1;
	}

	fdp = file_descriptors[fd];
	fdp->do_close = 1;

	return fd;
}

int
reopen( int fd, const char *filename )
{
	priv_fd_t *fdp = file_descriptors[fd];
	int ret;

	if( lookup_xt(fdp->ih, "reopen", &fdp->reopen_xt) )
		return -1;

	push_str( filename );
	call_package( fdp->reopen_xt, fdp->ih );
        ret = (POP() == (ucell)-1)? 0 : -1;

        DPRINTF("%s(%d, %s) = %d\n", __func__, fd, filename, ret);
	return ret;
}

int
reopen_nwrom( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];

        DPRINTF("%s(%d)\n", __func__, fd);
	if( lookup_xt(fdp->ih, "open-nwrom", &fdp->open_nwrom_xt) )
		return -1;
	call_package( fdp->open_nwrom_xt, fdp->ih );
        return (POP() == (ucell)-1)? 0 : -1;
}

ihandle_t
get_ih_from_fd( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];
	return fdp->ih;
}

const char *
get_file_path( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];
	if( lookup_xt(fdp->ih, "get-path", &fdp->get_path_xt) )
		return NULL;
	call_package( fdp->get_path_xt, fdp->ih );
	return (char*)cell2pointer(POP());
}

const char *
get_volume_name( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];
	if( lookup_xt(fdp->ih, "volume-name", &fdp->volume_name_xt) )
		return NULL;
	call_package( fdp->volume_name_xt, fdp->ih );
	return (char*)cell2pointer(POP());
}

const char *
get_fstype( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];
	if( lookup_xt(fdp->ih, "get-fstype", &fdp->get_fstype_xt) )
		return NULL;
	call_package( fdp->get_fstype_xt, fdp->ih );
	return (char*)cell2pointer(POP());
}

int
read_io( int fd, void *buf, size_t cnt )
{
	priv_fd_t *fdp;
	ucell ret;

        DPRINTF("%s(%d, %p, %u)\n", __func__, fd, buf, cnt);
	if (fd != -1) {
		fdp = file_descriptors[fd];

		PUSH( pointer2cell(buf) );
		PUSH( cnt );
		call_package( fdp->read_xt, fdp->ih );
		ret = POP();

		if( !ret && cnt )
			ret = -1;
	} else {
		ret = -1;
	}

	return ret;
}

int
seek_io( int fd, long long offs )
{
	priv_fd_t *fdp;

        DPRINTF("%s(%d, %lld)\n", __func__, fd, offs);
	if (fd != -1) {
		fdp = file_descriptors[fd];
		
		DPUSH( offs );
		call_package( fdp->seek_xt, fdp->ih );
		return ((((cell)POP()) >= 0)? 0 : -1);
	} else {
		return -1;
	}
}

long long
tell( int fd )
{
	priv_fd_t *fdp = file_descriptors[fd];
	long long offs;

	if( lookup_xt(fdp->ih, "tell", &fdp->tell_xt) )
		return -1;
	call_package( fdp->tell_xt, fdp->ih );
	offs = DPOP();
        DPRINTF("%s(%d) = %lld\n", __func__, fd, offs);
	return offs;
}

int
close_io( int fd )
{
	priv_fd_t *fdp;

        DPRINTF("%s(%d)\n", __func__, fd);
	if (fd != -1) {
		fdp = file_descriptors[fd];

		if( fdp->do_close )
			close_dev( fdp->ih );
		free( fdp );

		file_descriptors[fd]=NULL;
	}

	return 0;
}