// Bastard File System
// (C) 2007 Rene Schickbauer
//
// The Bastard File System (BFS) is a small virtual FS for Applications
// that would normally use Tarballs and/or Zipfiles but have to dynamically
// change its contents
//
// WARNING: This is still ALPHA QUALITY CODE! Use with care!
//
//
#include "bfs.h"
#include "bfs_convert.h"
#include "bfs_magic.h"
#include "bfs_header.h"
#include "bfs_data.h"
#include "bfs_dir.h"
#include "bfs_file.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


sBFS* bfsGetNewHandle() {
    sBFS* handle = (sBFS*) malloc(sizeof(sBFS));
    if(!handle) {
        printf("ERROR: malloc failed in getNewHandle()\n");
        exit(1);
    }
    memset(handle, 0, sizeof(sBFS));
    return handle;
}

bool bfsCloseHandle(sBFS* handle) {
    if(!handle) {
        printf("ERROR: no Handle in CloseHandle()\n");
        exit(1);
    }
    if(handle->fh) {
        bfsClose(handle);
    }
    free(handle);
    return true;
}

bool bfsOpen(sBFS* handle, char* filename) {
    if(!handle) {
        printf("ERROR: no Handle in Open()\n");
        exit(1);
    }
    
    if(handle->fh) {
        sprintf(handle->lastError, "File %s already open", filename);
        return false;        
    }
    
    FILE* fh = fopen(handle->fname, "r+b");
    if(!fh) {
        sprintf(handle->lastError, "Can't open FS-file %s for IO", filename);
        return false;
    }
    handle->fh = fh;
    
    BLOCK_MAGIC magic;
    if(!readMagicBlock(handle, &magic)) {
        return false;
    }
    if(strcmp(magic.IDSTRING, BFSVERSION) != 0) {
        fclose(handle->fh);
        handle->fh = 0;
        sprintf(handle->lastError, "FS-file %s has wrong magic '%s'", filename, magic.IDSTRING);
        return false;        
    }
    sprintf(magic.IDSTRING, "%s", BFSVERSION);
    handle->numDataBlocks = convertFrom4Char(magic.numDataBlocks);
    handle->numHeadBlocks = convertFrom4Char(magic.numHeadBlocks);
    
    // Read file blocks
    BLOCK_HEADER header;
    int i, j;
    for(i = 0; i < handle->numHeadBlocks; i++) {
        if(!readHeaderBlock(handle, i, &header)) {
            return false;
        }
        for(j = 0; j < 512; j++) {
            handle->blocktype[j + (i * 512)] = header.blockType[j];
        }
    }
      
    return true;
    
}

bool bfsClose(sBFS* handle) {
    if(!handle) {
        printf("ERROR: no Handle in Close()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }
    
    return true;
}

bool bfsNew(sBFS* handle, char* filename, int blocks) {
    int i, j;
    if(!handle) {
        printf("ERROR: no Handle in New()\n");
        exit(1);
    }
    handle->numDataBlocks = blocks;
    handle->numHeadBlocks = (blocks / 512) + 1; // Every head block stands for 512 data blocks; round up!
    sprintf(handle->fname, "%s", filename);
    
    FILE* fh = fopen(filename, "wb+");
    if(!fh) {
        sprintf(handle->lastError, "Can't open FS-file %s for writing", filename);
        return false;
    }
    handle->fh = fh;
    
    // Generate empty FS
    
    // ** magic **
    BLOCK_MAGIC magic;
    memset(&magic, 0, sizeof(magic));
    sprintf(magic.IDSTRING, "%s", BFSVERSION);
    convertTo4Char(handle->numDataBlocks, magic.numDataBlocks);
    convertTo4Char(handle->numHeadBlocks, magic.numHeadBlocks);
    if(!writeMagicBlock(handle, &magic)) {
        return false;
    }
    
    // REMEMBER: First data Block is the ROOT-Dir block, this must be correctly
    // initialized on creation. Non-Available blocks MUST BE set to type BLOCKTYPE_NONE
    
    // ** Header **
    
    // First set our internal representation of all blocks
    for(i = 0; i < MAXDATABLOCKS; i++) {
        handle->blocktype[i] = BLOCKTYPE_NONE;  // Clean blocks
    }
    for(i = 0; i < MAXDATABLOCKS; i++) {
        handle->blocktype[i] = BLOCKTYPE_FREE;  // Set blocks available
    }
    handle->blocktype[0] = BLOCKTYPE_DIR;  // Root-Dir block
    
    // Write file blocks
    BLOCK_HEADER header;
    for(i = 0; i < handle->numHeadBlocks; i++) {
        for(j = 0; j < 512; j++) {
            header.blockType[j] = handle->blocktype[j + (i * 512)];
        }
        if(!writeHeaderBlock(handle, i, &header)) {
            return false;
        }
    }
    
    // ** Data **
    // First, write all blocks as free; after that create Root-Dir block
    BLOCK_FREE freeblock;
    memset(&freeblock, 0, sizeof(freeblock));
    for(i = 0; i < handle->numDataBlocks; i++) {
        if(!writeDataBlock_Free(handle, i, &freeblock)) {
            return false;
        }
    }
    
    BLOCK_DIR rootdirblock;
    memset(&rootdirblock, 0, sizeof(rootdirblock));
    //   Prepare root dir block
    convertTo4Char(0, rootdirblock.prevBlock); // is First block
    convertTo4Char(0, rootdirblock.nextBlock); // no more blocks
    for(i = 0; i < DIRENTRY_PER_BLOCK; i++) { // Reset all entries
        rootdirblock.entry[i].type[0] = BLOCKTYPE_FREE;
    }
    if(!writeDataBlock_Dir(handle, 0, &rootdirblock)) {
        return false;
    }

       
    
    fclose(fh);
    handle->fh = 0;
    
    return true;
}

bool bfsReadFile(sBFS* handle, char* filename, FILEDATA* data) {
	if(!handle) {
        printf("ERROR: no Handle in ListDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }

    return readFile(handle, filename, data);
}

bool bfsWriteFile(sBFS* handle, char* filename, FILEDATA* data) {
	if(!handle) {
        printf("ERROR: no Handle in ListDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }
    
    if(!writeFile(handle, filename, data) ||
		!writeHeaders(handle)) {
		return false;
	} else {
		return true;
	}	

}

bool bfsGetFileSize(sBFS* handle, char* filename, int *size) {
	if(!handle) {
        printf("ERROR: no Handle in ListDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }

    int numBlocks;
    return statFile(handle, filename, &numBlocks, size);
}

bool bfsDeleteFile(sBFS* handle, char* filename) {
	handle = handle; // BLA
	filename = filename; // BLA
    printf("NOT IMPLEMENTED");
	return false;
}

   // Dir Handling
bool bfsMakeDir(sBFS* handle, char* dirname) {
	if(!handle) {
        printf("ERROR: no Handle in MakeDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }
		
    int newblock;
    if(!addDirNode(handle, BLOCKTYPE_DIR, dirname, &newblock) ||
    	!writeHeaders(handle)) {
		return false;
	} else {
		return true;
	}
}

bool bfsRemoveDir(sBFS* handle, char* dirname) {
	handle = handle; // BLA
	dirname = dirname; // BLA
    printf("NOT IMPLEMENTED");
	return false;
}

bool bfsListDir(sBFS* handle, char* path, DIRLIST* list) {
	if(!handle) {
        printf("ERROR: no Handle in ListDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }

	return listDir(handle, path, list);	
}

bool bfsDumpDirTree(sBFS* handle) {
	if(!handle) {
        printf("ERROR: no Handle in ListDir()\n");
        exit(1);
    }
    
    if(!handle->fh) {
        sprintf(handle->lastError, "File %s not open", handle->fname);
        return false;        
    }
    
    return dumpDirListTree(handle, "/", " ");	
}

   // Maintainance functions
bool bfsDefrag(sBFS* handle) {
	handle = handle; // bla
    printf("NOT IMPLEMENTED");
	return false;
}

bool bfsResize(sBFS* handle, int blocks) {
	handle = handle; // bla
	blocks = blocks; // BLA
    printf("NOT IMPLEMENTED");
	return false;
}

bool bfsShrink(sBFS* handle) {
	handle = handle; // BLA
    printf("NOT IMPLEMENTED");
	return false;
}

    // Error Handling
char* getLastError(sBFS* handle) {
    return handle->lastError;
	return false;
}

