// 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_file.h"
#include "bfs.h"
#include "bfs_data.h"
#include "bfs_dir.h"
#include "bfs_convert.h"
#include <string.h>

bool readFile(sBFS* handle, char* path, FILEDATA* fdata) {
    int nextblock;
    int bytecount = 0;
    int i, length;
   	DIRTREE tree;
    BLOCK_FILE data;

	if(!parseDir(handle, path, &tree)) {
		return false;
	}
		
	if(!traverseDir(handle, &tree)) {
        return false;
    } else if(tree.element[tree.elemcount - 1].type[0] != BLOCKTYPE_FILE) {
        sprintf(handle->lastError, "Path %s is not a file", path);
        return false;
    }
    
    nextblock = tree.element[tree.elemcount - 1].startblock;
    
    while(nextblock) {
        if(!readDataBlock_File(handle, nextblock, &data)) {
            return false;
        }
        length = convertFrom2Char(data.dataLength);
        for(i = 0; i < length; i++) {
            ((char*)fdata->data)[bytecount] = data.data[i];
            bytecount++;
        }
        nextblock = convertFrom4Char(data.nextBlock);
    }
    fdata->length = bytecount;
        
    return true;
}

bool writeFile(sBFS* handle, char* path, FILEDATA* fdata) {

    int startblock = 0;
    int nextblock = 0;
    int tmpblock = 0;
	DIRTREE tree;
    BLOCK_FILE data;
    BLOCK_FREE freedata;
    int bytecount;
	if(!parseDir(handle, path, &tree)) {
		return false;
	}
	int i, j;
		
	if(!traverseDir(handle, &tree)) {
            // New File
			if(tree.elemcount == 1) {
                // new file in root dir
                if(!addDirNode(handle, BLOCKTYPE_FILE, path, &startblock)) {
                    return false;
                }
            } else if(tree.elemcount > 1 && 
                tree.element[tree.elemcount - 2].type[0] == BLOCKTYPE_DIR) {
                if(!addDirNode(handle, BLOCKTYPE_FILE, path, &startblock)) {
                    return false;
                }
            } else {
                // No correct path
                strcat(handle->lastError, " - Could not get parent path");
                return false;
            }
	} else {
        // file exists - truncate
        startblock = tree.element[tree.elemcount - 1].startblock;
        nextblock = startblock;
        memset(freedata.data, 0, 512);
        while(nextblock) {
            if(!readDataBlock_File(handle, nextblock, &data)) {
                return false;
            }
            tmpblock = convertFrom4Char(data.nextBlock);
            if(nextblock == startblock) {
                // Clear up first block
                convertTo4Char(0, data.nextBlock);
                convertTo4Char(0, data.prevBlock);
                convertTo2Char(0, data.dataLength);
                memset(data.data, 0, FILEBLOCK_DATASIZE);
                if(!writeDataBlock_File(handle, nextblock, &data)) {
                    return false;
                }
            } else {
                // Free up block
                if(!writeDataBlock_Free(handle, nextblock, &freedata)) {
                    return false;
                }
                handle->blocktype[nextblock] = BLOCKTYPE_FREE;
            }
            nextblock = tmpblock;
        }
    }
            
    if(!startblock) {
        printf("Internal error in writeFile\n");
        exit(2);
    }
    
    nextblock = startblock;
    // Clear up block
    convertTo4Char(0, data.nextBlock);
    convertTo4Char(0, data.prevBlock);
    convertTo2Char(0, data.dataLength);
    memset(data.data, 0, FILEBLOCK_DATASIZE);
    bytecount = 0;
    for(i = 0; i < fdata->length; i++) {
        data.data[bytecount] = ((char *)fdata->data)[i];
        bytecount++;
        tmpblock = 0;
        if(bytecount == FILEBLOCK_DATASIZE && i < (fdata->length - 1)) {
            // Switch block
       		for(j = 0; j < MAXDATABLOCKS; j++) {
    			if(handle->blocktype[j] == BLOCKTYPE_FREE) {
    				handle->blocktype[j] = BLOCKTYPE_FILE;
    				tmpblock = j;
    				break;
    			}
    		}
        	if(!tmpblock) {
    			sprintf(handle->lastError, "FS Full: No more free blocks for file %s", path);
    			return false;
    		}
    		convertTo4Char(tmpblock, data.nextBlock);
    		convertTo2Char(bytecount, data.dataLength);
            if(!writeDataBlock_File(handle, nextblock, &data)) {
                return false;
            }
            nextblock = tmpblock;
            convertTo4Char(0, data.nextBlock);
            convertTo4Char(0, data.prevBlock);
            convertTo2Char(0, data.dataLength);
            memset(data.data, 0, FILEBLOCK_DATASIZE);
            bytecount = 0;
        }
    }
	convertTo4Char(tmpblock, data.nextBlock);
	convertTo2Char(bytecount, data.dataLength);
    if(!writeDataBlock_File(handle, nextblock, &data)) {
        return false;
    }
                            
    return true;            
}

bool deleteFile(sBFS* handle, char* path) {
	handle = handle;// BLA
	path = path; // BLA
    printf("ERROR: NOT IMPLEMENTED\n");
    exit(2);
}

bool statFile(sBFS* handle, char* path, int* numBlocks, int* bytes) {
    int nextblock;

	DIRTREE tree;

	if(!parseDir(handle, path, &tree)) {
		return false;
	}
		
	if(!traverseDir(handle, &tree)) {
        return false;
    } else if(tree.element[tree.elemcount - 1].type[0] != BLOCKTYPE_FILE) {
        sprintf(handle->lastError, "Path %s is not a file", path);
        return false;
    }

    nextblock = tree.element[tree.elemcount - 1].startblock;
    
    return statFile_fromBlock(handle, nextblock, numBlocks, bytes);
    
}

bool statFile_fromBlock(sBFS* handle, int startblock, int* numBlocks, int* bytes) {
    BLOCK_FILE data;
    int nextblock = startblock;
    
    *numBlocks = 0;
    *bytes = 0;
    
    while(nextblock) {
        if(!readDataBlock_File(handle, nextblock, &data)) {
            return false;
        }
        *numBlocks +=1;
        *bytes += convertFrom2Char(data.dataLength);
        nextblock = convertFrom4Char(data.nextBlock);
    }
        
    return true;
}


