/*  This file is part of Whitix.
 *
 *  Whitix 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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Whitix 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 Whitix; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/* FIXME: Split file into separate headers. */

#ifndef VFS_H
#define VFS_H

/* VFS includes. */
#include <fs/exports.h>

#include <llist.h>
#include <sdevice.h>
#include <typedefs.h>
#include <types.h>
#include <time.h>
#include <wait.h>

/* vNode flags */

#define VFS_ATTR_FILE 		1
#define VFS_ATTR_DIR 		2
#define VFS_ATTR_READ 		4
#define VFS_ATTR_WRITE 		8
#define VFS_ATTR_BLOCK 		16
#define VFS_ATTR_CHAR 		32
#define VFS_ATTR_SOCKET		64

/* Flags for SysOpen and NameToVNode */

#define FILE_READ			1
#define FILE_WRITE			2
#define FILE_CREATE_OPEN	4
#define FILE_FORCE_OPEN		8
#define FILE_FORCE_CREATE	16
#define FILE_DIRECTORY		32 /* Only open the file if it is a directory */

/* General defines */
#define MAX_FILES 10 /* Max files per task */
#define PATH_MAX 2048

struct VNode;
struct File;
struct FileSystem;
struct VfsSuperBlock;
struct PollItem;
struct PollQueue;

int VfsInit();
int VfsMountRoot(struct StorageDevice* storageDev);
int VNodeInitCache();

/* Mount functions (super.c) */
int VfsMount(char* mountPoint,char* deviceName,char* fsName,void* mData);
struct VfsSuperBlock* VfsAllocSuper(struct StorageDevice* sDev,int flags);
void VfsFreeSuper(struct VfsSuperBlock* superBlock);

/* VNode functions (vnode.c) */
int NameToVNode(struct VNode** retVal,char* path,int flags);
struct VNode* DirNameVNode(char* path,char** retPath,int* retLen);

/* VNode cache functions (vcache.c) */
struct VNode* VNodeGet(struct VfsSuperBlock* superBlock,vid_t id);
struct VNode* VNodeGetEmpty();
void VNodeRelease(struct VNode* vNode);
void VNodeLock(struct VNode* vNode);
void VNodeUnlock(struct VNode* vNode);
void VNodeWrite(struct VNode* vNode);
int VNodeRead(struct VNode* vNode);
void VNodeWaitOn(struct VNode* vNode);

int VfsChangeDir(struct Process* process,char* dirName);

/* vNode time functions */
void VfsFileAccessed(struct VNode* vNode);
void VfsFileModified(struct VNode* vNode,int create);

/* File functions (file.cpp) */
int FileGenericRead(struct File* file,BYTE* buffer,size_t size);
int VfsGetFreeFd(struct Process* process);
struct File* FileGet(int fd);
int DoOpenFile(struct File* file,char* path,int flags,int perms);
int DoReadFile(struct File* file,BYTE* buffer,size_t size);
int DoWriteFile(struct File* file,BYTE* buffer,size_t size);
int DoCloseFile(struct File* file);
int DoSeekFile(struct File* file,int distance,int whence);

/* For demand loading */
int FileGenericReadPage(addr_t pageAddr,struct VNode* vNode,int offset);

char* GetName(char* path);

/* Move? */

typedef struct
{
	WORD major;
	WORD minor;
} DevId;

/* Used for the SysGetDirEntries system call */

struct DirEntry
{
	DWORD vNodeNum; /* Need this? */
	DWORD offset;
	WORD length;
	char name[1];
};

/* VNODE 

Represents an on-disk directory entry in memory 

*/

#define VNODE_DIRTY 1

struct VNode
{
	struct VNode* mountNode;
	DWORD flags,refs,size,mode;
	vid_t id;
	struct VfsSuperBlock* superBlock;
	struct VNodeOps* vNodeOps;
	struct FileOps* fileOps;
	struct ListHead next;
	DevId devId;
	WaitQueue waitQueue;
	int lock;
	void* extraInfo;
	struct Time aTime,cTime,mTime;

	/* Virtual memory */
	struct ListHead sharedList;
};

#define SetVNodeDirty(vNode) (((vNode)->flags) |= VNODE_DIRTY)

/* FILE

File object. Used when reading/writing to a file (VNode doesn't need those
things really)

*/

#define FILE_NONBLOCK	0x1

struct File
{
	struct VNode* vNode;
	int position; /* FIXME: Add position type soon. */
	int flags;
	struct FileOps* fileOps;
};

struct FileOps
{
	int (*open)(struct File* file);
	int (*close)(struct File* file);
	int (*read)(struct File* file,BYTE* buffer,DWORD size);
	int (*write)(struct File* file,BYTE* buffer,DWORD size);
	int (*seek)(struct File* file,int pos,int whence);
	int (*readDir)(struct File* file,void* dirEntries);
	int (*ioctl)(struct File* file,unsigned long code,char* data);
	int (*poll)(struct File* file, struct PollItem* item, struct PollQueue* pollQueue);
	int (*mMap)(struct VNode* vNode, DWORD address, DWORD offset);
};

int FillDir(void* dirEntry,char* name,int nameLen,DWORD vNodeNum); 

struct VNodeOps
{
	int (*create)(struct VNode** retVal,struct VNode* dir,char* name,int nameLength);
	int (*remove)(struct VNode* dir,char* name,int nameLen);
	int (*lookup)(struct VNode** retVal,struct VNode* dir,char* name,int nameLength);
	int (*mkDir)(struct VNode** retVal,struct VNode* dir,char* name,int nameLength);
	int (*rmDir)(struct VNode* dir,char* name,int nameLength);
	int (*permission)(struct VNode* vNode,int access);
	int (*blockMap)(struct VNode* vNode,int offset);
	int (*truncate)(struct VNode* vNode,int size);
};

struct SuperBlockOps
{
	/* General vNode functions */
	int (*allocVNode)(struct VNode* vNode);
	int (*freeVNode)(struct VNode* vNode);
	int (*readVNode)(struct VNode* vNode);
	int (*writeVNode)(struct VNode* vNode);

	/* General superblock functions */
	int (*writeSuper)(struct VfsSuperBlock* superBlock);
};

#define SB_DIRTY	0x1
#define SB_RDONLY	0x2

struct VfsSuperBlock
{
	struct SuperBlockOps* sbOps; /* A field so it is easier to set up a VfsSuperBlock */
	void* privData;
	DWORD coveredId; /* VNode id of the covered vnode by the mount */
	struct VfsSuperBlock* parent;
	struct StorageDevice* sDevice; /* Can be null */
	struct VNode* mount;
	int flags;
	struct ListHead vNodeList, sbList;
};

/* General defines for a superblock */

#define BYTES_PER_SECTOR(s)		((s)->sDevice->softBlockSize)

struct Stat
{
	DWORD size,vNum,mode;
	unsigned long aTime,cTime,mTime;
}PACKED;

/* The StorageDevice parameter can be ignored if, for example, the filesystem is a network one */
typedef struct VfsSuperBlock* (*VfsReadSuperFunc)(struct StorageDevice* dev,int flags,char* data);

struct FileSystem
{
	char* name;
	VfsReadSuperFunc readSuper;
	struct ListHead list;
};

/* For filesystem drivers */
int VfsRegisterFileSystem(struct FileSystem* fs);
int VfsDeregisterFileSystem(struct FileSystem* fs);

/* Block cache functions and structures */

/* Buffer flags */

#define BUFFER_DIRTY	0x1
#define BUFFER_LOCKED	0x2

struct Buffer
{
	struct StorageDevice* device;
	struct ListHead list;
	DWORD blockNum;
	int flags,refs;
	BYTE* data;
	WaitQueue waitQueue;
};

/* buffer.c */

int BlockInit();
struct Buffer* BlockRead(struct StorageDevice* device,DWORD blockNum);
int BlockWrite(struct StorageDevice* device,struct Buffer* buffer);
int BlockSyncAll();
int BlockSyncDevice(struct StorageDevice* device);
int BlockFree(struct Buffer* buffer);
int BlockSetSize(struct StorageDevice* device,int blockSize);
void WaitForBuffer(struct Buffer* buffer);
void BufferUnlock(struct Buffer* buffer);
void BufferLock(struct Buffer* buffer);
struct Buffer* BlockBufferAlloc(struct StorageDevice* device,DWORD blockNum);
int BlockSendRequest(struct StorageDevice* device,struct Buffer* buff,int type);
int BlockSendRequestRaw(struct StorageDevice* device,struct Request* request);
struct Buffer* BlockFindBuffer(struct StorageDevice* sDevice,DWORD blockNum);

/* poll.c */
#define POLL_IN		1
#define POLL_OUT	2

struct PollQueue
{
	int i;
	int numFds;
	WaitQueue** waitQueues;
	struct WaitQueueEntry* waitEntries;
};

struct PollItem
{
	int fd;
	short events;
	short revents;
};

int PollAddWait(struct PollQueue* pollQueue, WaitQueue* waitQueue);

#endif
