/* chunks.h   -- low level allocation driver for xfiles
 *               which handles the allocation of 'disc'
 *               chunks including extension of the virtual
 *               disc as necessary.
 */

#ifndef __chunks_h
#define __chunks_h

#include "kernel.h"

#define xFiles_TYPE              0xB23       /* for now             */
#define xFiles_SIG               0x4C494658  /* 'XFIL' or something */
#define xFiles_DIRSIG            0x79646E41  /* 'Andy' of course    */
#define xFiles_STRUCTUREVERSION  1
#define xFiles_DIRECTORYVERSION  1
#define xFiles_ALLOCATIONUNIT    1024        /* and why not? */
#define xFiles_WINDOWSIZE        16384       /* size of window buffer */
#define xFiles_NEWCHUNKS         100
#define xFiles_GROWDIRBY         20
#define xFiles_MAXNAME           256
#define xFiles_CHUNKCACHE        10          /* actually 5 seems OK, but... */

#ifndef BOOL
#define BOOL int
#ifndef FALSE
#define FALSE (1 == 0)
#endif
#ifndef TRUE
#define TRUE  (!FALSE)
#endif
#endif

/* Fundamental disc allocation record. Used ones contain an offset
 * into the image and size. Unused ones are chained together through
 * their offset field (i.e. if the offset field is 1 the next free
 * chunk is number 1). Since chunk zero can never be free a zero in
 * the offset field marks the end of the free chain.
 */
typedef struct
{
   unsigned offset;
   unsigned size;
   unsigned usage;
   unsigned allocSize;

} xFiles_chunk;

typedef struct
{
   unsigned     cnkNum;
   xFiles_chunk chunk;
   
} xFiles_chunkCacheItem;

typedef struct
{
   unsigned sig;
   unsigned hdrSize;                /* size of this structure                      */
   unsigned structureVersion;
   unsigned directoryVersion;

   xFiles_chunk   chunkTable;       /* this record is duplicated in the chunkTable */
   unsigned       rootChunk;        /* root directory                              */

   unsigned       allocationUnit;   /* allocate space in multiples of this. can be */
                                    /* changed after file creation because it only */
                                    /* influences allocation: the existing image   */
                                    /* is valid regardless of allocationUnit size  */
   unsigned       freeChunk;        /* Head of the free chunk list                 */

   unsigned       waste;            /* Amount of wasted space in the image         */                                 

} xFiles_header;

typedef struct xFiles_listItem
{
   struct xFiles_listItem *prev;
   struct xFiles_listItem *next;

} xFiles_listItem;

typedef struct
{
   xFiles_listItem *head, *tail;
   
} xFiles_list;

/* Flags for an image */

enum
{
   xFiles_fNeedTruncate = 0x0001
};

/* Memory stuff */

typedef struct
{
   xFiles_listItem li;    /* Must be first item */

   int fileHandle;        /* OS filehandle associated which this image is open on */
   BOOL readOnly;         /* True if the file is readonly                         */
   BOOL compacting;       /* True during compaction                               */
   int lastCompact;       /* Time of last compaction                              */

   xFiles_list openList;  /* List of open files on this image                     */

   xFiles_header fileHeader;  /* convenient */

   /* Random replacement chunk cache */

   xFiles_chunkCacheItem chunkCache[xFiles_CHUNKCACHE];
   int cacheUsed;

   unsigned fileSize;     /* cached file size */

   unsigned flags;

} xFiles_info;

typedef struct
{
   unsigned sig;       /* 'Andy' of course! */
   unsigned parent;    /* 0 for root */
   unsigned size, used;
   
} xFiles_dirHeader;

/* There's a dirHash for each directory entry. It's not really a hash, just the
 * first four characters of the name (duplicated in the real entry) and an
 * offset to the actual entry.
 */

typedef struct
{
   char     nameStart[4];
   unsigned entryPos;
   unsigned node;        /* chunk number where this entry may be found */
   
} xFiles_dirHash;

typedef enum
{
   xFiles_meRead   = 0x001,
   xFiles_meWrite  = 0x002,
   xFiles_locked   = 0x008,
   xFiles_youRead  = 0x010,
   xFiles_youWrite = 0x020,

   /* Now our private ones */

   xFiles_isDir    = 0x100

} xFiles_attr;

/* Here's the actual directory entry. The name, which can be any length, follows
 * the entry and is NULL terminated. The next entry is at the next word boundary
 */

typedef struct
{
   unsigned    load;
   unsigned    exec;
   unsigned    size;        /* can be found from node but this is quicker */
   xFiles_attr attr;
   
   unsigned    nameLen;

   /* Name follows */

} xFiles_dirEntry;

/* Open file information structure */

typedef struct xFiles_fileInfo
{
   xFiles_listItem li;    /* Must be first item */

   int          fileSwitchHandle;
   xFiles_info *pOwner;
   unsigned     cnkNum;
   unsigned     parentDir;   /* directory chunk */
   int          fileSize;

} xFiles_fileInfo;

extern void *xFiles_windowBuffer;    /* shared by all and sundry */
extern int   xFiles_windowBufferSize;
extern xFiles_list xFiles_openFiles;
extern void *wsp;

unsigned xFiles_chunkAddress(xFiles_info *pInfo, unsigned cnkNum);
unsigned xFiles_roundUp(xFiles_info *pInfo, unsigned pos);
unsigned xFiles_roundDown(xFiles_info *pInfo, unsigned pos);
_kernel_oserror *xFiles_InitCache(unsigned size);
_kernel_oserror *xFiles_FlushFileInfo(xFiles_info *pInfo);
_kernel_oserror *xFiles_buildNewFilesystem(xFiles_info *pInfo);
_kernel_oserror *xFiles_claimBuffer(int notBiggerThan);
_kernel_oserror *xFiles_releaseBuffer(void);
_kernel_oserror *xFiles_getChunkInfo(xFiles_info *pInfo, unsigned cnkNum, xFiles_chunk *pChunk);
_kernel_oserror *xFiles_getLength(xFiles_info *pInfo, unsigned *pLength);
_kernel_oserror *xFiles_makeFreeChunks(xFiles_info *pInfo);
_kernel_oserror *xFiles_moveToEnd(xFiles_info *pInfo, unsigned cnkNum);
_kernel_oserror *xFiles_newChunk(xFiles_info *pInfo, unsigned *pCnkNum);
_kernel_oserror *xFiles_OpenImage(xFiles_info *pInfo);
_kernel_oserror *xFiles_CloseImage(xFiles_info *pInfo);
_kernel_oserror *xFiles_read(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size);
_kernel_oserror *xFiles_setChunkInfo(xFiles_info *pInfo, unsigned cnkNum, const xFiles_chunk *pChunk);
_kernel_oserror *xFiles_setLength(xFiles_info *pInfo, unsigned Length);
_kernel_oserror *xFiles_squashFile(xFiles_info *pInfo, BOOL force);
_kernel_oserror *xFiles_updateHeader(xFiles_info *pInfo);
_kernel_oserror *xFiles_write(xFiles_info *pInfo, void *pBuffer, unsigned pos, unsigned size);
_kernel_oserror *xFiles_erase(xFiles_info *pInfo, unsigned pos, unsigned size);

_kernel_oserror *xFiles_freeChunk(xFiles_info *pInfo, unsigned cnkNum);
_kernel_oserror *xFiles_setChunkSize(xFiles_info *pInfo, unsigned cnkNum, unsigned size);
_kernel_oserror *xFiles_moveBlock(xFiles_info *pInfo, unsigned to, unsigned from, int size);
_kernel_oserror *xFiles_midExtend(xFiles_info *pInfo, unsigned cnkNum, int pos, int by);
_kernel_oserror *xFiles_midExtendDirectory(xFiles_info *pInfo, unsigned dirObject, int pos, int by);

_kernel_oserror *xFiles_readChunk(xFiles_info *pInfo, void *pBuffer, unsigned cnkNum, unsigned pos, unsigned size);
_kernel_oserror *xFiles_writeAndGrow(xFiles_info *pInfo, void *pBuffer, unsigned cnkNum, unsigned pos, unsigned size);
_kernel_oserror *xFiles_writeChunk(xFiles_info *pInfo, void *pBuffer, unsigned cnkNum, unsigned pos, unsigned size);

_kernel_oserror *xFiles_getDirHeader(xFiles_info *pInfo, unsigned dirObject, xFiles_dirHeader *pHdr);
_kernel_oserror *xFiles_setDirHeader(xFiles_info *pInfo, unsigned dirObject, const xFiles_dirHeader *pHdr);
_kernel_oserror *xFiles_cDir(xFiles_info *pInfo, unsigned parent, unsigned *pCnkNum);
_kernel_oserror *xFiles_createDirEntry(xFiles_info *pInfo, unsigned dirObject,
                                       xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt,
                                       const char *pName);
_kernel_oserror *xFiles_makeSpaceInHashTable(xFiles_info *pInfo, unsigned dirObject);
_kernel_oserror *xFiles_relocateHashes(xFiles_info *pInfo, unsigned dirObject, int pos, int by);
_kernel_oserror *xFiles_dirLookup(xFiles_info *pInfo, unsigned dirObject, const char *pName,
                                  unsigned *pHashOffset, unsigned *pEntryOffset,
                                  xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt);
_kernel_oserror *xFiles_dirLookupByNode(xFiles_info *pInfo, unsigned dirObject, unsigned node,
                                        unsigned *pHashOffset, unsigned *pEntryOffset,
                                        xFiles_dirHash *pDirHash);
_kernel_oserror *xFiles_deleteDirEntry(xFiles_info *pInfo, unsigned dirObject, unsigned hashOffset);
_kernel_oserror *xFiles_parsePath(xFiles_info *pInfo, const char *pName, unsigned *pParent,
                                  unsigned *pHashOffset, unsigned *pEntryOffset,
                                  xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt,
                                  BOOL excludeLeaf);

_kernel_oserror *xFiles_Commit(xFiles_info *pInfo);
_kernel_oserror *xFiles_Truncate(xFiles_info *pInfo);
_kernel_oserror *xFiles_Ticker(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_WriteBehind(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_Flush(void);
void xFiles_Tidy(void);

/* List stuff */

void xFiles_InitItem(xFiles_listItem *pItem);
void xFiles_InitList(xFiles_list *pList);
void xFiles_AddAtHead(xFiles_list *pList, xFiles_listItem *pItem);
void xFiles_AddAtTail(xFiles_list *pList, xFiles_listItem *pItem);
void xFiles_Remove(xFiles_list *pList, xFiles_listItem *pItem);

/* Compaction routine */

_kernel_oserror *xFiles_Compact(xFiles_info *pInfo);

/***************************************************************************************/
/*                                                                                     */
/*  Exported FS entry points                                                           */
/*                                                                                     */
/***************************************************************************************/


/* Errors which are defined in error.s */

extern _kernel_oserror xFiles_BadPermission; 
extern _kernel_oserror xFiles_CantFake;
extern _kernel_oserror xFiles_NoMemory;
extern _kernel_oserror xFiles_ImageCorrupt;
extern _kernel_oserror xFiles_Truncated;
extern _kernel_oserror xFiles_Outside;
extern _kernel_oserror xFiles_BrokenDir;
extern _kernel_oserror xFiles_BadDirMark;
extern _kernel_oserror xFiles_OutOfRange;
extern _kernel_oserror xFiles_NotImplemented;
extern _kernel_oserror xFiles_NotFound;
extern _kernel_oserror xFiles_NotADir;
extern _kernel_oserror xFiles_NotAFile;
extern _kernel_oserror xFiles_Locked;
extern _kernel_oserror xFiles_BufferInUse;
extern _kernel_oserror xFiles_DirNotEmpty;
extern _kernel_oserror xFiles_LostTrack;
extern _kernel_oserror xFiles_IsOpen;

_kernel_oserror *xFiles_Open(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_GetBytes(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_PutBytes(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_Args(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_Close(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_File(_kernel_swi_regs *regs);
_kernel_oserror *xFiles_Func(_kernel_swi_regs *regs);

#endif
