#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define MAX_PATH 4096
#define DISKSIZE 32*256
#define DISKBLOCKS 256
#define BLOCKSIZE 32
unsigned char disk[256][32];
unsigned char cwd=1;	/*Current working directory*/


/*Some debugging stuff*/
#ifdef DEBUG
#include <stdarg.h>
void ntouch(const char *text, const char *file, const char *function, unsigned int line){
    if (text)
        fprintf(stderr, "TOUCH (%s), in %s %s() line %u\n", text, file, function, line);
    else fprintf(stderr, "TOUCH %s %s() line %u\n", file, function, line);
}
#define touch() ntouch(NULL, __FILE__, __FUNCTION__, __LINE__)
#define ctouch(n) if(n){}else{ntouch(#n, __FILE__, __FUNCTION__, __LINE__);}
void verbose(const char *format, ...){
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsprintf(buffer, format, args);
    va_end(args);
    fprintf(stderr, "%s\n", buffer);
}
#else
void doVoid(){
}
#define touch() doVoid()
#define ctouch(n) doVoid()
void verbose(const char *format, ...){
}
#endif

int checkwholedisk(); /*Nice little prototype for my checkdisk below*/

/********* setbit *****************
 ** sets bit[num] to val in disk **
 ** 0 <= num < 256               **
 ** val is either 0 or 1         **
 **********************************/
void setbit(int num, int val)
{;
  int charnum = num/8;
  int bitnum = num - charnum*8;
  unsigned char c = disk[0][charnum];
  unsigned char masks[]={1,2,4,8,16,32,64,128};

  if (num < 0 || num > 255) {
    fprintf(stderr, "Error in setbit, bad block number\n");
    return;
  }
  if (val == 0)
    c = c & ~masks[bitnum];
  else if (val == 1)
    c = c | masks[bitnum];
  else {
      fprintf(stderr,"Error in setbitmap, bad value\n");
      return;
  }
  disk[0][charnum] = c;
}

/****** getbit ***************************
 ** returns the value of bit number num **
 ** 0 <= num < 256, return value is     **
 ** either 0 or 1                       **
 *****************************************/
int getbit(int num)
{
  int charnum = num/8;
  int bitnum = num - charnum*8;
  unsigned char c = disk[0][charnum];
  unsigned char masks[]={1,2,4,8,16,32,64,128};
  unsigned char d;

  if (num < 0 || num > 255) {
    fprintf(stderr,"Error in getbitmap, bad block number\n");
    return -1;
  }
  d = c & masks[bitnum];
  return (int)d;
}

/******** initialize **************************
 **  tries to open the file filesystem.txt   **
 **  if successful, it copies the filesystem **
 **  into disk. Otherwise it initializes disk**
 **  It returns 0 if  filesystem.txt did     **
 **  not exist, 1 if it existed and was read **
 **  and -1 if an error occurred             **
 **********************************************/
int initialize()
{
  int fd, r, bytesread, target;
  char buffer[DISKSIZE+1];

  memset(disk,0,DISKSIZE+1);  /* set the entire disk to zeros */
  fd = open("filesystem.txt",O_RDONLY);
  if (fd < 0) {
    setbit(0,1);  /* turn on the first bit because the
                   freelist is in the first block*/
    setbit(1,1); /* turn on the second bit because this
                  will be the inode of the root directory*/
    return 0;
  }
  else {
    target = DISKSIZE;
    bytesread = 0;
    while (bytesread < target) {
    r = read(fd,buffer+bytesread,target-bytesread);
        if (r < 0) {
           perror("error reading filesystem.txt: ");
           close(fd);
           return -1;
        }
        if (r == 0) {
           fprintf(stderr,"Error reading filesystem.txt\n");
           close(fd);
           return -1;
        }
        bytesread += r;
    }
    memcpy(disk,buffer,DISKSIZE);
    close(fd);
    return 1;
  }
}

/***** shutdown **********************
 ** copies the contents of disk to  **
 ** the file filesystem.txt         **
 *************************************/

void shutdown()
{
  int fd, r, byteswritten, target;

  fd = open("filesystem.txt",O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd < 0) {
    perror("Error opening filesystem.txt:");
    exit(0);
  }
  target = DISKSIZE;
  byteswritten = 0;
  while (byteswritten < target) {
      r = write(fd,disk+byteswritten,target-byteswritten);
      if (r < 0) {
 	perror("Error writing to the file: ");
        exit(0);
      }
      byteswritten += r;
  }
  close(fd);
}


/************************************
******SOME HELPER FUNCTIONS**********
************************************/
/*Look for the next free block.  Since block 0
will always be taken, a return value of 0 indicates
disk full*/
unsigned char GetNextFreeBlock(){
	int i=0;
	for (i=0; i<DISKBLOCKS; ++i){
		if (getbit(i) == 0)
			return (unsigned char)i;
	}
	return 0;
}


unsigned char CreateBlock(){
	unsigned char bid = GetNextFreeBlock();
	if (bid == 0){
		fprintf(stderr, "Unable to create new block: Out of space\n");
		return 0;
	}
	setbit(bid,1);
	return bid;
}

void FreeBlock(unsigned char b){
	setbit(b,0);
}

/*Count the number of free blocks remaining*/
unsigned int CountFreeBlocks(){
	int i;
	unsigned int out=0;
	for (i=0; i<DISKBLOCKS; ++i){
		if (getbit(i) == 0)
			++out;
	}
	return out;
}

/*Compares two strings. returns 1 if match, otherwise 0*/
int cmp(const char *s1, const char *s2){
	int len1 = strlen(s1);
	int len2 = strlen(s2);
	int i;
	if (len1 != len2) return 0;
	for (i=0; i<len1; ++i){
		if (s1[i] != s2[i]) return 0;
	}
	return 1;
}

/*Gets the base name of a file*/
void basename(char *out, const char *filename){
    int i,start;
    int len = strlen(filename);

    /*Find beginning of string*/
    for (i=len-1; i>=0; --i){
         if (filename[i] == '/'){
            start = i+1;
            break;
        }
        if (i == 0){
            start = 0;
            break;
        }
    }
    /*Make output string*/
    for (i=0; i<len-start; ++i){
        out[i] = filename[start+i];
        out[i+1] = 0;
    }
}


/*Just like linux's version.. easy to open
directory and get file information*/
struct dFileInfo{
	char filename[8];
	unsigned int size;
	unsigned char inode;
	char type;
};
struct dDirInfo{
	unsigned char inode;	/*Pointer to the inode on disk*/
	int ioffset;					/*offset in the inode, starts at 4*/
	int boffset;					/*Offset in block*/
	int numfiles;					/*Total number of files*/
};


/*Opens a directory based on a inode location*/
struct dDirInfo *dOpenDirBlock(unsigned char inode){
	struct dDirInfo *dp = malloc(sizeof(struct dDirInfo));
	dp->inode = inode;
	dp->numfiles = *(unsigned int*)&disk[inode][0];
	if ( (char)disk[inode][4] != 'D' ){
	    verbose("Unable to open inode %u as a directory, found type %c", inode, disk[inode][4]);
		free(dp);
		return NULL;
	}
	dp->ioffset = 5;
	dp->boffset = 0;
	return dp;
}
void dCloseDir(struct dDirInfo *dp){
	free(dp);
}
int dNextFile(struct dDirInfo *dp, struct dFileInfo *out){
	out->filename[0] = 0; out->size = 0; out->inode = 0;

	/*Check if last file*/
	if ((dp->ioffset-5) * 4 + dp->boffset >= dp->numfiles)
		return 1;

	/*Put information into out*/
	out->inode = disk[disk[dp->inode][dp->ioffset]][dp->boffset*8+7];
	strcpy(out->filename, (char*)&disk[  disk[dp->inode][dp->ioffset]  ][dp->boffset*8]);
	out->size = *(unsigned int*)&disk[   out->inode   ][0];
	out->type = disk[  out->inode  ][4];

	/*iterate numbers and blocks*/
	dp->boffset++;
	if (dp->boffset >= 4){
		dp->ioffset++;
		dp->boffset = 0;
	}
	return 0;
}

/*Given an inode of a directory, finds the inode of a filename to look for
Returns 0 on error, otherwise the resulting inode*/
unsigned char FindFileName(unsigned char inode, const char *name){
	struct dDirInfo *dp;
	struct dFileInfo info;

	/*Some base cases*/
	if (cmp(name, ".")) return inode;
	if (name[0] == 0) return inode;

	/*Searh through the directory*/
	dp = dOpenDirBlock(inode);
	if (!dp) return 0;
	while(!dNextFile(dp, &info)){
		if (cmp(name, info.filename)){
			dCloseDir(dp);
			return info.inode;
		}
	}
	dCloseDir(dp);
	return 0;
}

/*Get a file inode based on the filename
returns 0 on error*/
unsigned char GetInode(const char *filename){
	unsigned char curr_inode;
	unsigned int i,index=0;
	unsigned int len = strlen(filename);
	char temp[MAX_PATH];
	temp[0] = 0;
	curr_inode = (filename[0] == '/' ? 1 : cwd);

	/*Parse file and keep looking for next directory/file*/
	for (i=filename[0] == '/' ? 1 : 0; i<len+1; ++i){
		if (filename[i] == '/' || filename[i] == 0){
			index = 0;
			curr_inode = FindFileName(curr_inode, temp);
			if (curr_inode == 0) break;
			if (filename[i] == 0) break;
		}else{
			/*Forms up the next filename*/
			temp[index] = filename[i];
			temp[index+1] = 0;
			++index;
		}
	}
	return curr_inode;
}

/*Gives an input file or directory and gets an inode based
upon that information.  Will check for both absolute directory
or relative*/
unsigned char GetDirInode(const char *dir){
	char temp[MAX_PATH];
	int len = strlen(dir);
	int i;
	strcpy(temp, dir);

	/*Chop off the ending filename to get the directory
	of where whatever we're looking for resides*/
	for (i=len-1; i>=0; --i){
	    if (i == 0)
            return cwd;
		if (temp[i] == '/'){
			temp[i] = 0;
			break;
		}
	}

	/*Return the inode*/
	return GetInode(temp);
}



/*Gets a dDirInfo ptr based upon the directory inode*/
struct dDirInfo *dOpenDir(const char *dir){
	return dOpenDirBlock(GetInode(dir));
}





/*Returns 1 on error, 0 on success*/
int AddInodeToDir(const char *filename, unsigned char inode){
	unsigned char dir = GetDirInode(filename);
	unsigned int numfiles = *(unsigned int*)&disk[dir][0];
	unsigned char block = numfiles / 4;	/*Offset of the block within inode*/
	unsigned char offset = numfiles % 4;
	unsigned char blockaddr = 0;
	char bn[32];

  if (dir == 0){
	    fprintf(stderr, "Unable to find directory to add %s to\n", filename);
	    return 1;
	}

	if ( disk[dir][4] != 'D'){
	    fprintf(stderr, "Unable to add %s to parent, parent not a directory\n", filename);
	    return 1;
	}

	if (numfiles >= 4 * 27){
		fprintf(stderr, "Max files in this directory!\n");
		return 1;
	}

	(*(unsigned int*)&disk[dir][0])++;

	if (offset == 0){
		/*Need to create new block*/
		blockaddr = disk[dir][5+block] = CreateBlock();
	}else{
		/*Just add it on*/
		blockaddr = disk[dir][5+block];
	}

	/*Get basename of file*/
	basename(bn, filename);

	memcpy(&disk[blockaddr][offset*8], bn, 7);
	disk[blockaddr][offset*8+6] = 0;	/*Make sure null terminated*/
	disk[blockaddr][offset*8+7] = inode;

	return 0;
}

/*Removes an inode from the directory.
returns 0 on success, 1 on error*/
int RemoveInodeFromDir(const char *filename, unsigned char inode){
	unsigned char dir = GetDirInode(filename);
	unsigned int numfiles = *(unsigned int*)&disk[dir][0];
	unsigned char block;
	unsigned char offset;
	int i;
	unsigned char *prev = NULL; /*Keeps pointer to previous one to change*/

    if (dir == 0){
        fprintf(stderr, "Unable to remove %s inode, doesn't exist\n", filename);
        return 1;
    }

	/*Go through and replace the inode*/
	block = offset = 0;
	for (i=0; i<numfiles; ++i){
		if (prev){
			memcpy(prev, &disk[  disk[dir][block+5]   ][offset*8], 8);
			prev = &disk[  disk[dir][block+5]   ][offset*8];
		}else if(disk[  disk[dir][block+5]  ][offset*8+7] == inode){
			prev = &disk[  disk[dir][block+5]   ][offset*8];
		}

		++offset;
		if (offset >= 4){
			offset = 0;
			++block;
		}
	}

	if (prev){
		(*(unsigned int*)&disk[dir][0])--;
		if ( (*(unsigned int*)&disk[dir][0]) % 4 == 0){
			block = (*(unsigned int*)&disk[dir][0])/4;
			FreeBlock( disk[dir][block + 5] );
			disk[dir][block+5] = 0;
		}
		return 0;
	}
	return 1;

}






/************************************
****ACCESS FUNCTIONS*****************
************************************/




/*Read a file and print it to stdout*/
void d_read(const char *filename){
	unsigned char inode = GetInode(filename);
	unsigned int size;
	int i,j;
	unsigned char block;

	verbose("Reading %s", filename);

	if (inode == 0){
		fprintf(stderr, "READ: File not found, %s\n", filename);
		return;
	}
	if (disk[inode][4] != 'R'){
		fprintf(stderr, "READ: Can not read a directory, %s\n", filename);
		return;
	}

	/*Else read, the file*/
	size = *(unsigned int*)&disk[inode][0];
	for (i=5; i<32; ++i){
		if (size <= 0) break;
		block = disk[inode][i];

		for(j=0; j<32; ++j){
			printf("%c", (char)disk[block][j]);
			--size;
			if (size <= 0) break;
		}
	}
	printf("\n");
	/*DONE*/
}

/*List the files in the directory*/
void d_list(){
	/*List the files*/
	struct dFileInfo info;
	struct dDirInfo *dir = dOpenDir(".");

	verbose("Listing files in cwd %u", cwd);

	if (!dir){
		fprintf(stderr, "list: Unable to find directory\n");
		return;
	}
	while(!dNextFile(dir, &info)){
		if (info.type == 'D')
			printf("%s D\n", info.filename);
		else if(info.type == 'R')
			printf("%s %u\n", info.filename, info.size);
		else
			printf("%s %u UNKNOWN %c\n", info.filename, info.size, info.type);
	}
	dCloseDir(dir);
}


/*Writes data to disk*/
void d_write(const char *filename, const unsigned char *data, unsigned int size){
	unsigned char inode;
	int i,j;
	unsigned int oldsize;
	unsigned int offset=0;

	verbose("Writing %u bytes to %s", size, filename);

	/*Get an inode, or create a new one if it doesn't exist*/
	inode = GetInode(filename);
	if (inode == 0){
		inode = CreateBlock();
		if (inode == 0) return;
		if (AddInodeToDir(filename, inode)){ /*Check if error adding to inode*/
		    FreeBlock(inode);
		    return;
		}
	}else{
	    if (disk[inode][4] != 'R'){
	        fprintf(stderr, "File %s is a directory, unable to write\n", filename);
	        return;
	    }
		/*Free each taken data block*/
		oldsize = *(unsigned int*)&disk[inode][0];
		disk[inode][4] = 'R';
		for (i=0,j=5; i<oldsize; i += 32,++j){
			FreeBlock(disk[inode][j]);
		}
	}


	/*Now that we have a file inode, copy size and create appropriate blockhis*/
	*(unsigned int*)&disk[inode][0] = size;
	disk[inode][4] = 'R';
	int remaining = (int)size;
	for (i=5,offset=0; i<32 && remaining>0; ++i,offset+=32,remaining-=32){
		disk[inode][i] = CreateBlock();
		memcpy(&disk[  disk[inode][i]  ][0], &data[offset], remaining > 32 ? 32 : remaining);
	}


}


/*Appends data to disk*/
void d_append(const char *filename, const unsigned char *data, unsigned int size){
	unsigned char inode;
	int i;
	unsigned int start;
	unsigned char block,offset;

	verbose("Appending %u bytes to %s", size, filename);


	/*Get an inode, create a new one if doesn't exist*/
	inode = GetInode(filename);
	if (inode == 0){
		inode = CreateBlock();
		if (inode == 0) return;
		if (AddInodeToDir(filename, inode)){
		    FreeBlock(inode);
		    return;
		}
		start=0;
	}else{
		if (disk[inode][4] != 'R'){
			fprintf(stderr, "Append: File is a directory\n");
			return;
		}
		start = *(unsigned int*)&disk[inode][0];
	}
	block = start / 32;
	offset = start % 32;

	if (offset == 0 && block >= 27){
		fprintf(stderr, "Append: Out of room\n");
		return;
	}

	/*Now time to add to the file*/
	for (i=0; i<size; ++i){
		if (offset == 0){
			disk[inode][block+5] = CreateBlock();
		}
		disk[   disk[inode][block+5]  ][offset] = data[i];
		++offset;
		if (offset >= 32){
			block++;
			if (block >= 27){
				fprintf(stderr, "Append: Out of room\n");
				break;
			}
			offset = 0;
		}
	}

	*(unsigned int*)&disk[inode][0] += size;
	disk[inode][4] = 'R';
	if (*(unsigned int*)&disk[inode][0] > 27 * 32)
		*(unsigned int*)&disk[inode][0] = 27 * 32;

	/*Let's hope that works...
	yay.. it does*/
}

/*Copy the file from one location to another*/
void d_copy(const char *from, const char *to){
	unsigned char old_inode, inode;
	unsigned int size;
	unsigned char block,offset;
	int i;

	verbose("Copying %s to %s", from, to);

	/*Get file inode*/
	old_inode = GetInode(from);
	if (old_inode == 0){
		fprintf(stderr, "Unable to copy file %s to %s, file does not exist\n", from, to);
		return; /*Doesn't exist*/
	}

	/*Check to make sure old_inode is a file*/
	if ( disk[old_inode][4] != 'R' ){
	    fprintf(stderr, "Unable to copy file %s to %s, unable to copy directory\n", from, to);
        return;
	}

	/*Check if file already exists*/
	inode = GetInode(to);
	if (inode != 0){
		fprintf(stderr, "File %s already exists\n", to);
		return;
	}

	/*Get size and check if there's enough space to copy*/
	size = *(unsigned int*)&disk[old_inode][0];
	if (CountFreeBlocks() <= size/BLOCKSIZE){
		fprintf(stderr, "Not enough room to copy file\n");
		return;
	}

	/*Start copying over the data*/
	inode = CreateBlock();
	if (AddInodeToDir(to, inode)){
	    FreeBlock(inode);
	    return;
	}

    /*Copy over data*/
	*(unsigned int*)&disk[inode][0] = size;
	disk[inode][4] = 'R';
	block = offset = 0;
	for (i=0; i<size; ++i){
		if (offset == 0){
			disk[inode][block + 5] = CreateBlock();
		}
		disk[  disk[inode][block + 5] ][offset] = disk[  disk[old_inode][block+5]  ][offset];

		++offset;
		if (offset >= BLOCKSIZE){
			offset = 0;
			block++;
		}
	}




	/*Done :)*/
}

/*Simply rename the file*/
void d_rename(const char *from, const char *to){
	unsigned char inode;


    verbose("Renaming %s to %s", from, to);

	/*Get inode*/
	inode = GetInode(from);
	if (inode == 0){
		fprintf(stderr, "Unable to rename file, %s doesn't exist\n", from);
		return;
	}

	/*Check if to already exists*/
	if (GetInode(to) != 0){
		fprintf(stderr, "%s already exists\n", to);
		return;
	}

    /*Remove inode from one directory and add to another*/
    if (AddInodeToDir(to, inode)) return;
    RemoveInodeFromDir(from, inode);
}

/*Deletes a file freeing all its blocks*/
void d_delete(const char *filename){
	unsigned char inode;
	int block;
	unsigned int size;

	verbose("Deleting %s", filename);

	/*Get Inode*/
	inode = GetInode(filename);
	if (inode == 0){
		fprintf(stderr, "Unable to delete %s, does not exist\n", filename);
		return;		/*Doesn't exist.. no reason to do anything*/
	}

	/*TODO, check if it's a directory*/
	if (disk[inode][4] == 'R'){

        /*Go through inode and delete all blocks*/
        size = *(unsigned int*)&disk[inode][0];
        for (block=0; block<27; block++){
            if (block < size/32 || (block == size/32 && size%32 > 0)){
                FreeBlock( disk[inode][5+block] );
            }else break;
        }

	}else if(disk[inode][4] == 'D'){
	    if ( *(unsigned int*)&disk[inode][0] > 1){
	        fprintf(stderr, "Unable to delete directory %s, still contains subdirectories\n", filename);
	        return;
	    }
	    FreeBlock(disk[inode][5]);
	}

	/*Remove from directory*/
    if (RemoveInodeFromDir(filename, inode))
        fprintf(stderr, "Error: Unable to remove inode %u from directory. Something went wrong\n", inode);

	/*Free Inode*/
	FreeBlock(inode);
}

/*Makes a new directory*/
void d_mkdir(const char *filename){
	unsigned char parent = GetDirInode(filename);	/*Parent inode of the directory*/
	unsigned char inode = GetInode(filename);

	if (parent == 0){
	    fprintf(stderr, "Unable to create new directory, parent doesn't exist!\n");
	    return;
	}
	if (inode != 0){
	    fprintf(stderr, "Directory %s already exists\n", filename);
	    return;
	}
	inode = CreateBlock();
	if (inode == 0) return;

    verbose("Creating directory %s", filename);

	/*Given the new inode, format the inode to be in a directory, and add parent node ptr*/
	*(unsigned int*)&disk[inode][0] = 1;
	disk[inode][4] = 'D';
	disk[inode][5] = CreateBlock();
	strcpy((char*)&disk[  disk[inode][5]  ][0], "..");
	disk[  disk[inode][5]  ][7] = parent;


	/*Add this inode to the directory parent*/
	AddInodeToDir(filename, inode);

	/*DONE!*/
}

/*Change the current working directory*/
void d_chdir(const char *filename){
	unsigned char ncwd = GetInode(filename);
	if (ncwd == 0 || (ncwd > 0 && disk[ncwd][4] != 'D'))
		fprintf(stderr, "chdir: No such directory %s\n", filename);
	else{
		cwd = ncwd;
		verbose("Changed cwd to %s at %u", filename, cwd);
	}
}








/*Loads a file and processes the commands in it,
as defined by the assignment*/
int process(const char *filename){
	char command[128];
	char arg1[128];
	char arg2[128];
	char str[1024];
	int i;
	FILE *file;

	file = fopen(filename, "r");
	if (!file){
		perror("Unable to open file: ");
		return 1;
	}

	/*Parse the file*/
	while(!feof(file)){
		fscanf(file, "%s", command);

		if (cmp(command, "read")){
			fscanf(file, "%s", arg1);
			d_read(arg1);
		}else if(cmp(command, "write")){
			fscanf(file, "%s", arg1);
			fgetc(file);	/*Skip space*/
			for (i=0; i<1024; ++i){
				str[i] = fgetc(file);
				if (feof(file)) break;
				if (str[i] == '\n'){
					str[i] = 0;
					break;
				}
			}
			d_write(arg1, (unsigned char*)str, strlen(str));
		}else if(cmp(command, "append")){
			fscanf(file, "%s", arg1);
			fgetc(file);
			for (i=0; i<1024; ++i){
				str[i] = fgetc(file);
				if (feof(file)) break;
				if (str[i] == '\n'){
					str[i] = 0;
					break;
				}
			}
			d_append(arg1, (unsigned char*)str, strlen(str));
		}else if(cmp(command, "copy")){
			fscanf(file, "%s %s", arg1, arg2);
			d_copy(arg1,arg2);
		}else if(cmp(command, "rename")){
			fscanf(file, "%s %s", arg1, arg2);
			d_rename(arg1,arg2);
		}else if(cmp(command, "delete")){
			fscanf(file, "%s", arg1);
			d_delete(arg1);
		}else if(cmp(command, "list")){
			d_list();
		}else if(cmp(command, "mkdir")){
			fscanf(file, "%s", arg1);
			d_mkdir(arg1);
		}else if(cmp(command, "chdir")){
			fscanf(file, "%s", arg1);
			d_chdir(arg1);
		}else if(cmp(command, "quit")){
			break;
		}else{
			fprintf(stderr, "Invalid command: %s\n", command);
		}
#ifdef DEBUG
        if (checkwholedisk())
            break;
        verbose("----");
#endif
	}

	fclose(file);
	return 0;
}

/*Very simple formatting function*/
void FormatDisk(){
    verbose("Formatting disk");
	memset(disk[0], 0, 32);
	setbit(0,1);
	setbit(1,1);
	setbit(2,1);
	memset(disk[1], 0, 32);

	*(unsigned int*)&disk[1][0] = 1;
	disk[1][4] = 'D';
	disk[1][5] = 2;
	strcpy( (char*)disk[2], "..");
	disk[2][7] = 1;
}

/*A nice little function to check the disk tree. Extra credit anyone? :p */
/*Please note, debug has to be enabled from this to print out information*/
/*Returns an error count*/
int checkdisk(unsigned char inode, unsigned char parent, unsigned char fileblock, char *bytemap){
    unsigned int size;
    int errors = 0;
    int block,offset,i,j;
    if (!fileblock) verbose("*Checking block %u...", inode);

    /*Mark as used*/
    bytemap[inode] = 1;

    /*Check to make sure first block is directory*/
    if (inode == 1 && disk[inode][4] != 'D'){
        verbose("***Expected block 1 to be a directory! FAIL");
        return 1;
    }

    /*Check to make sure bit is taken*/
    if (getbit(inode) == 0){
        verbose("***Block %u not marked as taken", inode);
        errors++;
    }

    /*If a file block, then only the above checks need to be taken*/
    if (fileblock) return errors;

    /*Now check integrity based on types*/
    if (disk[inode][4] == 'D'){
        /*Check a directory----------------*/
        size = *(unsigned int*)&disk[inode][0];
        if (size > 27 * 4){
            verbose("***Too many files for directory at inode %u", inode);
            errors++;
        }

        block = offset = 0;
        for (i=0; i<size; ++i){
            bytemap[ disk[inode][block+5] ] = 1;
            if (i > 0){ /*Prevents looking at parent node*/
                /*Check filename*/
                for (j=0; j<7; ++j){
                    if (disk[  disk[inode][block+5]  ][offset*8+j] == 0) break;
                }
                if (j == 7){
                    verbose("***Invalid name format %s at inode %u", &disk[  disk[inode][block+5]  ][offset*8], inode);
                    errors++;
                }else if(j == 0){
                    verbose("***Invalid file name (null) at inode %u", inode);
                    errors++;
                }else
                    verbose("**Checking %s %s...", disk[ disk[  disk[inode][block+5]  ][offset*8+7] ][4] == 'D' ? "directory" : "file", &disk[  disk[inode][block+5]  ][offset*8]);

                /*Check file pointer*/
                errors += checkdisk(disk[  disk[inode][block+5]  ][offset*8+7], inode, 0, bytemap);
            }else{
                if (disk[ disk[inode][block+5] ][7] != parent){
                    verbose("***Invalid parent for inode %u, expected %u but got %u", inode, parent, disk[ disk[inode][block+5] ][7]);
                    errors++;
                }
            }
            /*loop*/
            offset++;
            if (offset%4 == 0){
                offset = 0;
                block++;
            }

        }


    }else if(disk[inode][4] == 'R'){
        /*Check a file---------------------*/
        size = *(unsigned int*)&disk[inode][0];
        if (size > 27 * BLOCKSIZE){
            verbose("***File size greater than possible file size at inode %u", inode);
            errors++;
        }

        block=0;
        while(block*32<size){
            errors += checkdisk(disk[inode][block+5], inode, 1, bytemap);
            ++block;
        }

    }else{
        verbose("***Invalid block type at block %u", inode);
        errors++;
    }
    return errors;
}
/*Just a wrapper to the recursive version*/
int checkwholedisk(){
    char bytemap[DISKBLOCKS];
    int errors=0;
    int i;

    /*Set up and look for errors*/
    memset(bytemap, 0, DISKBLOCKS); bytemap[0] = 1;
    errors += checkdisk(1,1,0,bytemap);

    /*Check for marked blocks that actually aren't used*/
    for (i=0; i<DISKBLOCKS; ++i){
        if (bytemap[i] == 0 && getbit(i)){
            verbose("Block %u is unused, but marked as used", i);
            errors++;
        }
    }
    return errors;
}

int main(int argc, char *argv[])
{
    if (argc != 2){
        fprintf(stderr, "Not enough arguments. Expected filename of file to process.\n");
        return 1;
    }

  int r;
  r =initialize();
  /* You can delete the next three lines and replace them with your code */
  /* If the file did not exist, you will need to initialize the inode
     for the directory in block 1  */
  if (r < 0) fprintf(stderr,"error opening filesystem.txt\n");
  else if (r == 0) fprintf(stderr,"The file filesystem.txt did not exist\n");
  else fprintf(stderr,"The file filesystem.txt was opened and read\n");
  if (r <= 0)
  	FormatDisk();

  /* Begin my code */
  process(argv[1]);

#ifdef DEBUG
    checkwholedisk();
#endif

  shutdown();
  return 0;
}




