#include "initrd.h"

initrd_header_t*       initrd_header; // The header.
initrd_file_header_t*  file_headers;  // The list of file headers.
fs_node_t*             initrd_root;   // Our root directory node.
fs_node_t*             initrd_dev;    // We also add a directory node for /dev, so we can mount devfs later on.
fs_node_t*             root_nodes;    // List of file nodes.
int                    nroot_nodes;   // Number of file nodes.

struct dirent dirent;

static ULONG initrd_read(fs_node_t* node, ULONG offset, ULONG size, char* buffer)
{
    initrd_file_header_t header = file_headers[node->inode];
    size = header.length;
    if (offset > header.length)      return 0;
    if (offset+size > header.length) size = header.length-offset;
    k_memcpy(buffer, (void*)(header.off + offset), size);
    return size;
}

static struct dirent* initrd_readdir(fs_node_t* node, ULONG index)
{
    if( (node == initrd_root) && (index == 0) )
    {
      k_strcpy(dirent.name, "dev");
      dirent.name[3] = 0; // NUL-terminate the string
      dirent.ino     = 0;
      return &dirent;
    }

    if( index-1 >= nroot_nodes )
        return 0;
    k_strcpy(dirent.name, root_nodes[index-1].name);
    dirent.name[k_strlen(root_nodes[index-1].name)] = 0; // NUL-terminate the string
    dirent.ino = root_nodes[index-1].inode;
    return &dirent;
}

static fs_node_t* initrd_finddir(fs_node_t* node, char* name)
{
    if( (node == initrd_root) && (!k_strcmp(name,"dev")) )
        return initrd_dev;
    int i;
    for( i=0; i<nroot_nodes; ++i)
        if( !k_strcmp(name, root_nodes[i].name) )
            return &root_nodes[i];
    return 0;
}

fs_node_t* install_initrd(ULONG location)
{
    // Initialise the main and file header pointers and populate the root directory.
    initrd_header = (initrd_header_t*) location;
    file_headers  = (initrd_file_header_t*) (location+sizeof(initrd_header_t));

    // Initialise the root directory.
    ///
    #ifdef _DIAGNOSIS_
    settextcolor(2,0);
    printformat("rd_root: ");
    settextcolor(15,0);
    #endif
    ///

    initrd_root = (fs_node_t*) k_malloc( sizeof(fs_node_t),1,0 );
    k_strcpy(initrd_root->name, "initrd");
    initrd_root->mask    = initrd_root->uid = initrd_root->gid = initrd_root->inode = initrd_root->length = 0;
    initrd_root->flags   = FS_DIRECTORY;
    initrd_root->read    = 0;
    initrd_root->write   = 0;
    initrd_root->open    = 0;
    initrd_root->close   = 0;
    initrd_root->readdir = &initrd_readdir;
    initrd_root->finddir = &initrd_finddir;
    initrd_root->ptr     = 0;
    initrd_root->impl    = 0;

    // Initialise the /dev directory (required!)
    ///
    #ifdef _DIAGNOSIS_
    settextcolor(2,0);
    printformat("rd_dev: ");
    settextcolor(15,0);
    #endif
    ///

    initrd_dev = (fs_node_t*)k_malloc(sizeof(fs_node_t),1,0);
    k_strcpy(initrd_dev->name, "dev");
    initrd_dev->mask     = initrd_dev->uid = initrd_dev->gid = initrd_dev->inode = initrd_dev->length = 0;
    initrd_dev->flags    = FS_DIRECTORY;
    initrd_dev->read     = 0;
    initrd_dev->write    = 0;
    initrd_dev->open     = 0;
    initrd_dev->close    = 0;
    initrd_dev->readdir  = &initrd_readdir;
    initrd_dev->finddir  = &initrd_finddir;
    initrd_dev->ptr      = 0;
    initrd_dev->impl     = 0;

    ///
    #ifdef _DIAGNOSIS_
    settextcolor(2,0);
    printformat("root_nodes: ");
    settextcolor(15,0);
    #endif
    ///

    root_nodes = (fs_node_t*) k_malloc( sizeof(fs_node_t)*initrd_header->nfiles,1,0);
    nroot_nodes = initrd_header->nfiles;

    // For every file...
    ULONG i;
    for( i=0; i<initrd_header->nfiles; ++i)
    {
        // Edit the file's header - currently it holds the file offset relative to the start of the ramdisk.
        file_headers[i].off += (location); /// We want it relative to the start of memory. ///

        // Create a new file node.
        k_strncpy(root_nodes[i].name, file_headers[i].name, 64); /// critical !!!
        root_nodes[i].name[64] = 0;

        root_nodes[i].mask    = root_nodes[i].uid = root_nodes[i].gid = 0;
        root_nodes[i].length  = file_headers[i].length;
        root_nodes[i].inode   = i;
        root_nodes[i].flags   = FS_FILE;
        root_nodes[i].read    = (read_type_t) &initrd_read;
        root_nodes[i].write   = 0;
        root_nodes[i].readdir = 0;
        root_nodes[i].finddir = 0;
        root_nodes[i].open    = 0;
        root_nodes[i].close   = 0;
        root_nodes[i].impl    = 0;
    }
    return initrd_root;
}
