/*
 * fat32/fat32_file.c - fat32 file related operations
 *
 * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless
 * Copyright (c) 2011,2012 UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-kernel.
 *
 * ALMOS-kernel is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-kernel is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-kernel; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdint.h>
#include <kmem.h>
#include <string.h>
#include <ppm.h>
#include <pmm.h>
#include <page.h>
#include <mapper.h>
#include <vfs.h>
#include <ppn.h>
#include <thread.h>
#include <sys-vfs.h>

#include <fat32.h>
#include <fat32-private.h>

VFS_OPEN_FILE(vfat_open) 
{
	return 0;
}

VFS_LSEEK_FILE(vfat_lseek)
{
	return 0;
}

VFS_CLOSE_FILE(vfat_close) 
{
	return 0;
}

VFS_RELEASE_FILE(vfat_release) 
{
	assert(file->fr_pv == NULL);
	return 0;
}


VFS_READ_DIR(vfat_readdir) 
{
	struct vfat_DirEntry_s dir;
	struct page_s *page;
	struct mapper_s *mapper;
	vfat_cluster_t node_cluster;
	uint8_t *buff;
	uint32_t found;

	mapper    = file->fr_inode->i_mapper;
	found     = 0;

	/* TODO: dont call mapper every time, as page can be reused */
	while(!found) 
	{
		//FIXME: synchro : either use a lock or force the writer to
		//write the first byte of the name as the last thing he do
		//when adding an entry
		//also lock file offset ?
		if ((page = mapper_get_page(mapper, 
					    file->fr_offset >> PMM_PAGE_SHIFT, 
					    MAPPER_SYNC_OP)) == NULL)
			return VFS_IO_ERR;

		buff  = ppm_page2addr(page);
		buff += file->fr_offset % PMM_PAGE_SIZE;

		memcpy(&dir, buff, sizeof(dir));

		if(dir.DIR_Name[0] == 0x00) {
			vfat_dmsg(3,"vfat_readdir: entries termination found (0x00)\n");
			goto VFS_READ_DIR_EODIR;
		}

		if(dir.DIR_Name[0] == 0xE5) {
			vfat_dmsg(3,"entry was freeed previously\n");
			vfat_getshortname((char*)dir.DIR_Name, (char*)dirent->u_name);
			vfat_dmsg(3,"it was %s\n",dirent->u_name);
			goto VFS_READ_DIR_NEXT;
		}

		if(dir.DIR_Attr == 0x0F) {
			vfat_dmsg(3,"this entry is a long one\n");
			vfat_getshortname((char*)dir.DIR_Name, (char*)dirent->u_name);
			vfat_dmsg(3,"trying to read its name %s\n",dirent->u_name);
			goto VFS_READ_DIR_NEXT;
		}

		if(dir.DIR_Name[0] == '.')
			goto VFS_READ_DIR_NEXT;

		found = 1;
		vfat_getshortname((char *)dir.DIR_Name, (char*)dirent->u_name);

		//dirent->d_size = dir.DIR_FileSize;
		dirent->u_attr = 0;

		if(dir.DIR_Attr & VFAT_ATTR_DIRECTORY)
			dirent->u_attr = VFS_DIR;

		if(dir.DIR_Attr & VFAT_ATTR_SYSTEM)
			dirent->u_attr |= VFS_SYS;

		if(dir.DIR_Attr & VFAT_ATTR_ARCHIVE)
			dirent->u_attr |= VFS_ARCHIVE;

		if(dir.DIR_Attr & VFAT_ATTR_READ_ONLY)
			dirent->u_attr |= VFS_RD_ONLY;

		node_cluster  = dir.DIR_FstClusHI << 16;
		node_cluster |= (0x0000FFFF & dir.DIR_FstClusLO);

		if((!node_cluster) && (dirent->u_attr & VFS_SYS)
		   && (dirent->u_attr & VFS_RD_ONLY) && (dirent->u_attr & VFS_DIR)) {
			dirent->u_attr |= VFS_DEV;
			dirent->u_attr &= ~(VFS_SYS | VFS_RD_ONLY | VFS_DIR);
		}
		else
			if((!node_cluster) && (dirent->u_attr & VFS_SYS) && (dirent->u_attr & VFS_RD_ONLY)) {
				dirent->u_attr |= VFS_FIFO;
				dirent->u_attr &= ~(VFS_SYS | VFS_RD_ONLY);
			}

	VFS_READ_DIR_NEXT:
		file->fr_offset += sizeof(struct vfat_DirEntry_s);
	}

VFS_READ_DIR_EODIR:
	return (found) ? 0 : VFS_EODIR;
}

const struct vfs_file_op_s vfat_f_op =
{
	.open    = vfat_open,
	.read    = vfs_default_read,
	.write   = vfs_default_write,
	.lseek   = NULL,
	.readdir = vfat_readdir,
	.close   = vfat_close,
	.release = vfat_release,
	.mmap    = vfs_default_mmap_file,
	.munmap  = vfs_default_munmap_file
};
