/*
 * Contains a simple implementation of a data source that reads from a
 * given file.
 */

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#include "libisofs.h"

#define BLOCK_SIZE 2048

#define BLOCK_OUT_OF_FILE -1; 
#define READ_ERROR -2; 
#define SEEK_ERROR -3; 

struct file_data_src {
	int fd;
	int nblocks;
};

static int 
ds_read_block(struct data_source *src, int lba, unsigned char *buffer)
{
	struct file_data_src *data;
	
	assert(src && buffer);
	
	data = (struct file_data_src*)src->data;
	
	/* For block devices size is always 0, so this can't be used.
	 * if (lba >= data->nblocks) 
	 *	return BLOCK_OUT_OF_FILE;
	 */
	
	/* goes to requested block */
	if ( lseek(data->fd, (off_t)lba * (off_t)BLOCK_SIZE, SEEK_SET) == (off_t) -1 ) 
		return SEEK_ERROR;
	
	if ( read(data->fd, buffer, BLOCK_SIZE) != BLOCK_SIZE ) 
		return READ_ERROR;
		
	return 0;
}
	
static int ds_get_size(struct data_source *src)
{
	struct file_data_src *data;
	
	assert(src);
	
	data = (struct file_data_src*)src->data;
	return data->nblocks;
}
	
static void ds_free_data(struct data_source *src)
{
	struct file_data_src *data;
	
	assert(src);
	
	data = (struct file_data_src*)src->data;
	
	/* close the file */
	close(data->fd);
	free(data);
}
 
struct data_source *data_source_from_file(const char *path)
{
	int fd;
	struct stat info;
	struct file_data_src *data;
	struct data_source *ret;
	
	assert(path);
	
	fd = open(path, O_RDONLY);
	if (fd == -1)
		return NULL;
		 
	fstat(fd, &info);
	
	data = malloc(sizeof(struct file_data_src));
	data->fd = fd;
	data->nblocks = info.st_size / BLOCK_SIZE;
	
	ret = malloc(sizeof(struct data_source));
	ret->refcount = 1;
	ret->read_block = ds_read_block;
	ret->get_size = ds_get_size;
	ret->free_data = ds_free_data;
	ret->data = data;
	return ret;
} 

void data_source_free(struct data_source *src)
{
	if (--src->refcount == 0) {
		src->free_data(src);
		free(src);
	}
}